From cfcc998271f273e78bb6ed88b595e5ecb7989abc Mon Sep 17 00:00:00 2001 From: Mathis Date: Thu, 31 Oct 2024 11:49:03 +0100 Subject: [PATCH] Add AuthService for user registration and login Implement user signup and signin functionality with password hashing, role assignment, and JWT token generation. Integrate promo code for initial balance adjustment, and handle unique credential errors. --- src/auth/auth.service.ts | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/auth/auth.service.ts diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 0000000..c39ee44 --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,110 @@ +import { ForbiddenException, Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { AuthLoginDto, AuthRegisterDto } from './dto'; +import * as argon from 'argon2'; +import { Prisma, User } from '@prisma/client'; +import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class AuthService { + private readonly initialBalance = 1000; + + constructor( + private prisma: PrismaService, + private jwt: JwtService, + private config: ConfigService, + ) {} + + async signup(dto: AuthRegisterDto) { + const hash = await argon.hash(dto.password); + const promoCode = await this.getPromoCode(dto.promoCode); + const userRole = await this.getUserRole('user'); + const balance = this.calculateBalance(promoCode); + + try { + const user = await this.createUser(dto, hash, userRole.id, balance); + return this.signToken(user); + } catch (error) { + this.handleSignupError(error); + } + } + + private async getPromoCode(promoCode: string) { + return this.prisma.promoCode.findFirst({ + where: {name: promoCode}, + }); + } + + private async getUserRole(roleName: string) { + return this.prisma.role.findFirst({ + where: {name: roleName}, + }); + } + + private calculateBalance(promoCode: any) { + let balance = this.initialBalance; + if (promoCode && promoCode.value) { + balance += promoCode.value; + } + return balance; + } + + private async createUser(dto: AuthRegisterDto, hash: string, roleId: string, balance: number) { + return this.prisma.user.create({ + data: { + firstName: dto.firstName, + lastName: dto.lastName, + pseudo: dto.pseudo, + city: dto.city, + email: dto.email, + hash, + age: dto.age, + roleId, + isActive: true, + dollarAvailables: balance, + }, + }); + } + + private handleSignupError(error: any) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2002') { + throw new ForbiddenException('Credentials taken'); + } + } + throw error; + } + + async signin(dto: AuthLoginDto) { + const userDatas = await this.prisma.user.findUnique({ + where: { email: dto.email }, + include: { + UserHasCrypto: { include: { Crypto: true } }, + Role: true, + }, + }); + + if (!userDatas) throw new ForbiddenException('Credentials incorrect'); + + const pwMatches = await argon.verify(userDatas.hash, dto.password); + if (!pwMatches) throw new ForbiddenException('Credentials incorrect'); + + return this.signToken(userDatas); + } + + async signToken(user: any): Promise<{ access_token: string; user: User }> { + const payload = { sub: user.id, email: user.email }; + user.hash = null; + const secret = this.config.get('JWT_SECRET'); + const token = await this.jwt.signAsync(payload, { + expiresIn: '30d', + secret: secret, + }); + + return { + access_token: token, + user, + }; + } +} \ No newline at end of file