From 2be846b8df37190522b000b9d2d86806632bb7a9 Mon Sep 17 00:00:00 2001 From: gabito1451 Date: Wed, 22 Apr 2026 10:30:21 -0700 Subject: [PATCH] feat: implement user authentication, registration, and two-factor authentication services with Prisma schema support --- prisma/schema.prisma | 1 + src/auth/auth.service.ts | 12 ++++++++++++ src/users/users.controller.ts | 10 ++++++++++ src/users/users.service.ts | 24 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 503a6946..a9160d22 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -69,6 +69,7 @@ model User { phone String? role UserRole @default(USER) isVerified Boolean @default(false) @map("is_verified") + isBlocked Boolean @default(false) @map("is_blocked") twoFactorEnabled Boolean @default(false) @map("two_factor_enabled") twoFactorSecret String? @map("two_factor_secret") twoFactorBackupCodes String[] @default([]) @map("two_factor_backup_codes") diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 0472b473..0c04708c 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -104,6 +104,10 @@ export class AuthService { throw new UnauthorizedException('Invalid credentials'); } + if (user.isBlocked) { + throw new UnauthorizedException('Your account has been blocked. Please contact support.'); + } + const passwordMatches = await comparePassword(data.password, user.password); if (!passwordMatches) { throw new UnauthorizedException('Invalid credentials'); @@ -166,6 +170,10 @@ export class AuthService { throw new UnauthorizedException('User no longer exists'); } + if (user.isBlocked) { + throw new UnauthorizedException('Your account has been blocked'); + } + if (user.id !== payload.sub) { throw new UnauthorizedException('Refresh token does not match the authenticated user'); } @@ -491,6 +499,10 @@ export class AuthService { throw new UnauthorizedException('Invalid API key'); } + if (apiKey.user.isBlocked) { + throw new UnauthorizedException('User account is blocked'); + } + await this.prisma.apiKey.update({ where: { id: apiKey.id }, data: { diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 6a9fcff6..7a088f96 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -26,6 +26,16 @@ export class UsersController { return this.usersService.update(id, updateUserDto); } + @Post(':id/block') + block(@Param('id') id: string) { + return this.usersService.block(id); + } + + @Post(':id/unblock') + unblock(@Param('id') id: string) { + return this.usersService.unblock(id); + } + @Delete(':id') remove(@Param('id') id: string) { return this.usersService.remove(id); diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 6f830f59..964f7847 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -102,6 +102,30 @@ export class UsersService { }); } + async block(id: string) { + return this.prisma.user.update({ + where: { id }, + data: { isBlocked: true }, + select: { + id: true, + email: true, + isBlocked: true, + }, + }); + } + + async unblock(id: string) { + return this.prisma.user.update({ + where: { id }, + data: { isBlocked: false }, + select: { + id: true, + email: true, + isBlocked: true, + }, + }); + } + async remove(id: string) { return this.prisma.user.delete({ where: { id },