Compare commits

..

7 Commits

Author SHA1 Message Date
d4a9519cbf
feat: useGlobalValidationPipe with whitelist 2024-02-27 16:46:11 +01:00
577d96d68c
feat: Auth module 2024-02-27 16:45:22 +01:00
73ee4e2894
build: argon2 ... 2024-02-27 16:45:03 +01:00
f62fb6d687
biomejs conf 2024-02-27 16:44:31 +01:00
5619ebfc17
AuthDTO update 2024-02-27 16:44:20 +01:00
7f1440ed65
prisma schema 2024-02-27 16:44:03 +01:00
5a6470a3ce
prisma migration 2024-02-27 16:43:31 +01:00
8 changed files with 149 additions and 31 deletions

View File

@ -4,22 +4,27 @@
"enabled": false
},
"linter": {
"enabled": true,
"enabled": false,
"rules": {
"recommended": true,
"performance": {
"recommended": true
"recommended": true
},
"security": {
"recommended": true
"recommended": true
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 80,
"formatWithErrors": false
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 80,
"formatWithErrors": false
},
"javascript": {
"parser": {
"unsafeParameterDecoratorsEnabled": true
}
}
}

View File

@ -24,6 +24,9 @@
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@prisma/client": "^5.10.2",
"argon2": "^0.40.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},

View File

@ -0,0 +1,43 @@
/*
Warnings:
- You are about to drop the `Bookmark` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
DROP TABLE `Bookmark`;
-- DropTable
DROP TABLE `User`;
-- CreateTable
CREATE TABLE `users` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
`email` VARCHAR(191) NOT NULL,
`hash` VARCHAR(191) NOT NULL,
`firstName` VARCHAR(191) NULL,
`lastName` VARCHAR(191) NULL,
UNIQUE INDEX `users_id_key`(`id`),
UNIQUE INDEX `users_email_key`(`email`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `bookmarks` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
`title` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NULL,
`link` VARCHAR(191) NOT NULL,
`userId` INTEGER NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `bookmarks` ADD CONSTRAINT `bookmarks_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -14,15 +14,18 @@ datasource db {
}
model User {
id Int @id @default(autoincrement())
id Int @id @unique @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String
email String @unique
hash String
firstName String?
lastName String?
@@map("users")
bookmarks Bookmark[]
}
model Bookmark {
@ -33,4 +36,9 @@ model Bookmark {
title String
description String?
link String
userId Int
user User @relation(fields: [userId], references: [id])
@@map("bookmarks")
}

View File

@ -4,17 +4,15 @@ import { AuthDto } from "./dto";
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
constructor(private authService: AuthService) {}
@Post('signup')
signup(@Body() dto: AuthDto) {
console.log({dto});
return this.authService.signup()
}
@Post("register")
async signup(@Body() dto: AuthDto) {
return await this.authService.register(dto);
}
@Post('signin')
signin(@Body() dto: AuthDto) {
console.log({dto});
return this.authService.signin()
}
@Post("login")
async signin(@Body() dto: AuthDto) {
return await this.authService.login(dto);
}
}

View File

@ -1,17 +1,60 @@
import { Injectable } from "@nestjs/common";
import { ForbiddenException, Injectable } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
import { AuthDto } from "./dto";
import * as argon from "argon2";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
@Injectable({})
export class AuthService {
constructor(private prisma: PrismaService) {
constructor(private prisma: PrismaService) {}
async login(dto: AuthDto) {
const User = await this.prisma.user.findUnique({
where: {
email: dto.email,
},
});
if (!User) {
console.warn(`ACCESS: Refused login for "${dto.email}" (email not used)`);
throw new ForbiddenException("Credential(s) invalid.");
}
signin() {
return {response: "Sign IN"}
const pwMatches = await argon.verify(User.hash, dto.password);
if (!pwMatches) {
console.warn(
`ACCESS: Refused login for "${dto.email}" (invalid password)`,
);
throw new ForbiddenException("Credential(s) invalid.");
}
signup() {
return {response: "Sign UP"}
delete User.hash;
console.info(`ACCESS: Granted login for "${dto.email}"`);
return User;
}
async register(dto: AuthDto) {
const userPasswordHash = await argon.hash(dto.password);
try {
const User = await this.prisma.user.create({
data: {
email: dto.email,
hash: userPasswordHash,
},
select: {
id: true,
email: true,
firstName: true,
lastName: true,
},
});
//delete User.hash;
return User;
} catch (error) {
if (error instanceof PrismaClientKnownRequestError) {
if (error.code === "P2002") {
throw new ForbiddenException("Credential(s) taken.");
}
}
}
}
}

View File

@ -1,4 +1,16 @@
export interface AuthDto {
email: string,
password: string
import { IsEmail, IsNotEmpty, IsStrongPassword } from "class-validator";
export class AuthDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsStrongPassword({
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1,
})
password: string;
}

View File

@ -1,8 +1,14 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
}),
);
await app.listen(3333);
}
bootstrap();