diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts index 46fb671..a97ca10 100644 --- a/server/src/auth/auth.module.ts +++ b/server/src/auth/auth.module.ts @@ -16,7 +16,7 @@ export const JWT_SECRET = env.JWT_SECRET PassportModule, JwtModule.register({ secret: JWT_SECRET, - signOptions: { expiresIn: '5m' }, + signOptions: { expiresIn: '12h' }, }), UsersModule, ], diff --git a/server/src/auth/auth.service.ts b/server/src/auth/auth.service.ts index 85d7c50..7342f41 100644 --- a/server/src/auth/auth.service.ts +++ b/server/src/auth/auth.service.ts @@ -13,9 +13,7 @@ export class AuthService { if (!user) { throw new NotFoundException(`Aucun⋅e utilisateur⋅rice avec pour nom ${name}`) } - const isPasswordValid = await bcrypt.compare(password, user.password) - if (!isPasswordValid) { throw new UnauthorizedException('Mot de passe incorrect') } @@ -24,4 +22,4 @@ export class AuthService { accessToken: this.jwtService.sign({ userId: user.id }), } } -} \ No newline at end of file +} diff --git a/server/src/auth/dto/login.dto.ts b/server/src/auth/dto/login.dto.ts index 04d6f6c..eb6dde5 100644 --- a/server/src/auth/dto/login.dto.ts +++ b/server/src/auth/dto/login.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger' -import { IsNotEmpty, IsString, MinLength } from 'class-validator' +import { IsNotEmpty, IsString } from 'class-validator' export class LoginDto { @IsString() diff --git a/server/src/auth/jwt.strategy.ts b/server/src/auth/jwt.strategy.ts index b121940..d3be6d4 100644 --- a/server/src/auth/jwt.strategy.ts +++ b/server/src/auth/jwt.strategy.ts @@ -3,6 +3,7 @@ import { PassportStrategy } from '@nestjs/passport' import { ExtractJwt, Strategy } from 'passport-jwt' import { JWT_SECRET } from './auth.module' import { UsersService } from 'src/users/users.service' +import { User } from '@prisma/client' @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { @@ -13,7 +14,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { }) } - async validate(payload: { userId: number }) { + async validate(payload: { userId: number }): Promise { const user = await this.usersService.findOne(payload.userId) if (!user) { throw new UnauthorizedException() diff --git a/server/src/users/dto/user_password.dto.ts b/server/src/users/dto/user_password.dto.ts new file mode 100644 index 0000000..1746b93 --- /dev/null +++ b/server/src/users/dto/user_password.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsNotEmpty, IsString, MinLength } from 'class-validator' + +export class UpdatePasswordDto { + @IsString() + @IsNotEmpty() + @MinLength(8) + @ApiProperty() + password: string +} \ No newline at end of file diff --git a/server/src/users/users.controller.ts b/server/src/users/users.controller.ts index d1a6250..53e4981 100644 --- a/server/src/users/users.controller.ts +++ b/server/src/users/users.controller.ts @@ -1,8 +1,12 @@ -import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from '@nestjs/common' +import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Post, Req, UseGuards } from '@nestjs/common' import { UsersService } from './users.service' -import { ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse } from '@nestjs/swagger' +import { ApiBadRequestResponse, ApiBearerAuth, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger' import { UserEntity } from './entities/user.entity' import { JwtAuthGuard } from 'src/auth/jwt-auth.guard' +import { User } from '@prisma/client' +import { UpdatePasswordDto } from './dto/user_password.dto' + +export type AuthenticatedRequest = Request & { user: User } @Controller('users') export class UsersController { @@ -12,6 +16,8 @@ export class UsersController { @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOkResponse({ type: UserEntity, isArray: true }) + @ApiUnauthorizedResponse({ description: "Non authentifié⋅e" }) + @ApiForbiddenResponse({ description: "Permission refusée" }) async findAll() { const users = await this.usersService.findAll() return users.map(user => new UserEntity(user)) @@ -21,6 +27,8 @@ export class UsersController { @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOkResponse({ type: UserEntity }) + @ApiUnauthorizedResponse({ description: "Non authentifié⋅e" }) + @ApiForbiddenResponse({ description: "Permission refusée" }) @ApiNotFoundResponse({ description: "Utilisateur⋅rice non trouvé⋅e" }) async findOne(@Param('id', ParseIntPipe) id: number) { const user = await this.usersService.findOne(id) @@ -28,4 +36,17 @@ export class UsersController { throw new NotFoundException(`L'utilisateur⋅rice avec l'identifiant ${id} n'existe pas`) return new UserEntity(user) } + + @Patch('/update-password') + @HttpCode(204) + @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + @ApiNoContentResponse({description: "Le mot de passe a bien été modifié."}) + @ApiBadRequestResponse({description: "Erreur dans la saisie du nouveau mot de passe."}) + @ApiUnauthorizedResponse({ description: "Non authentifié⋅e" }) + @ApiForbiddenResponse({ description: "Permission refusée" }) + async updatePassword(@Req() request: AuthenticatedRequest, @Body() { password }: UpdatePasswordDto) { + const user = request.user + await this.usersService.updatePassword(user, password) + } } diff --git a/server/src/users/users.service.ts b/server/src/users/users.service.ts index 72d8aa9..a41895e 100644 --- a/server/src/users/users.service.ts +++ b/server/src/users/users.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common' +import { User } from '@prisma/client' import { PrismaService } from 'src/prisma/prisma.service' +import * as bcrypt from 'bcrypt' @Injectable() export class UsersService { @@ -12,4 +14,12 @@ export class UsersService { async findOne(id: number) { return await this.prisma.user.findUnique({ where: { id } }) } + + async updatePassword(user: User, password: string) { + const hashedPassword = await bcrypt.hash(password, 10) + await this.prisma.user.update({ + where: { id: user.id }, + data: { password: hashedPassword } + }) + } }