diff --git a/backend/src/modules/auth-module/controller/auth.controller.ts b/backend/src/modules/auth-module/controller/auth.controller.ts index e5df2c8..ce2ebf4 100644 --- a/backend/src/modules/auth-module/controller/auth.controller.ts +++ b/backend/src/modules/auth-module/controller/auth.controller.ts @@ -1,19 +1,21 @@ import { Controller, Post, + Get, Body, HttpCode, HttpStatus, Req, UseGuards, } from '@nestjs/common'; -import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; +import { ApiBody, ApiCreatedResponse, ApiTags } from '@nestjs/swagger'; import { Request } from 'express'; import { SessionGuard } from 'src/modules/session/guard'; +import { SuccessDto } from 'src/shared'; import { Public } from 'src/shared/decorator'; import { LocalAuthGuard } from '../guard'; -import { LoginResponseDto, UserCredentialsDto } from '../models/dto'; +import { SigninResponseDto, UserCredentialsDto } from '../models/dto'; import { AuthService } from '../services/auth.service'; @ApiTags('Authentication') @@ -23,28 +25,29 @@ export class AuthController { @ApiCreatedResponse({ description: 'User signed up successfully', - type: LoginResponseDto, + type: SuccessDto, }) - @Public() @Post('signup') @HttpCode(HttpStatus.CREATED) + @Public() public async signup( @Body() userCredentials: UserCredentialsDto - ): Promise<{ success: boolean }> { + ): Promise { return this.authService.signup(userCredentials); } @ApiCreatedResponse({ description: 'User signin successfully', - type: LoginResponseDto, + type: SigninResponseDto, }) + @ApiBody({ type: UserCredentialsDto }) @HttpCode(HttpStatus.OK) - @Public() @UseGuards(LocalAuthGuard) + @Public() @Post('signin') - public async signin(@Req() request: Request): Promise { + public async signin(@Req() request: Request): Promise { return this.authService.getLoginResponse( - request.user as LoginResponseDto & { userAgent: string } + request.user as SigninResponseDto & { userAgent: string } ); } @@ -54,7 +57,21 @@ export class AuthController { @HttpCode(HttpStatus.OK) @UseGuards(SessionGuard) @Post('logout') - public async logout(@Req() request: Request): Promise<{ success: boolean }> { + public async logout(@Req() request: Request): Promise { return this.authService.logout(request.sessionID); } + + @ApiCreatedResponse({ + description: 'Check if user is authenticated', + type: SuccessDto, + }) + @HttpCode(HttpStatus.OK) + @UseGuards(SessionGuard) + @Get('status') + public status(@Req() request: Request): Promise { + return this.authService.checkAuthStatus( + request.sessionID, + request.headers['user-agent'] + ); + } } diff --git a/backend/src/modules/auth-module/models/dto/index.ts b/backend/src/modules/auth-module/models/dto/index.ts index 5c635c8..e0a3cb5 100644 --- a/backend/src/modules/auth-module/models/dto/index.ts +++ b/backend/src/modules/auth-module/models/dto/index.ts @@ -1,2 +1,2 @@ export * from './user-credentials.dto'; -export * from './login-response.dto'; +export * from './signin-response.dto'; diff --git a/backend/src/modules/auth-module/models/dto/login-response.dto.ts b/backend/src/modules/auth-module/models/dto/signin-response.dto.ts similarity index 92% rename from backend/src/modules/auth-module/models/dto/login-response.dto.ts rename to backend/src/modules/auth-module/models/dto/signin-response.dto.ts index 6424306..ea21c77 100644 --- a/backend/src/modules/auth-module/models/dto/login-response.dto.ts +++ b/backend/src/modules/auth-module/models/dto/signin-response.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; -export class LoginResponseDto { +export class SigninResponseDto { @ApiProperty({ title: 'Email', description: 'User Email', diff --git a/backend/src/modules/auth-module/repositories/user-credentials.repository.ts b/backend/src/modules/auth-module/repositories/user-credentials.repository.ts index 48671ef..11a963b 100644 --- a/backend/src/modules/auth-module/repositories/user-credentials.repository.ts +++ b/backend/src/modules/auth-module/repositories/user-credentials.repository.ts @@ -30,13 +30,4 @@ export class UserCredentialsRepository { ): Promise { return this.repository.findOne({ where: { id: userId } }); } - - // public async updateUserRefreshToken( - // userId: string, - // refreshToken: string | null - // ): Promise { - // const result = await this.repository.update(userId, { refreshToken }); - - // return result.affected ?? 0; - // } } diff --git a/backend/src/modules/auth-module/services/auth.service.ts b/backend/src/modules/auth-module/services/auth.service.ts index 6677a12..241a15d 100644 --- a/backend/src/modules/auth-module/services/auth.service.ts +++ b/backend/src/modules/auth-module/services/auth.service.ts @@ -7,12 +7,12 @@ import { } from '@nestjs/common'; import { UserCredentials } from 'src/entities'; import { SessionService } from 'src/modules/session/services/session.service'; -import { EncryptionService } from 'src/shared'; +import { EncryptionService, SuccessDto } from 'src/shared'; import { PasswordConfirmationMailService } from '../../sendgrid-module/services/password-confirmation.mail.service'; import { UserDataRepository } from '../../user-module/repositories/user-data.repository'; import { EmailVerificationService } from '../../verify-module/services/email-verification.service'; -import { LoginResponseDto, UserCredentialsDto } from '../models/dto'; +import { SigninResponseDto, UserCredentialsDto } from '../models/dto'; import { UserCredentialsRepository } from '../repositories/user-credentials.repository'; @Injectable() @@ -27,7 +27,7 @@ export class AuthService { public async signup( userCredentials: UserCredentialsDto - ): Promise<{ success: boolean }> { + ): Promise { try { const existingUser = await this.userCredentialsRepository.findUserByEmail( userCredentials.email @@ -64,11 +64,11 @@ export class AuthService { } catch (error) { if (error instanceof ConflictException) { throw new ConflictException( - 'Diese E-Mail-Adresse ist bereits registriert.' + 'User already exists. Please try to login instead.' ); } else { throw new HttpException( - 'Fehler bei der Registrierung', + 'Error while signing up', HttpStatus.INTERNAL_SERVER_ERROR ); } @@ -99,22 +99,49 @@ export class AuthService { } catch (error) { if (error instanceof ForbiddenException) { throw new ForbiddenException( - 'Die eingebenen Daten sind nicht korrekt. Bitte versuchen Sie es erneut.' + 'E-Mail address or password is incorrect. Please try again.' ); } else { throw new HttpException( - 'Fehler beim Login', + 'Error while validating user credentials', HttpStatus.INTERNAL_SERVER_ERROR ); } } } + public async checkAuthStatus( + sessionId: string, + userAgend: string + ): Promise { + try { + const session = + await this.sessionService.findSessionBySessionId(sessionId); + + if (!session) { + throw new ForbiddenException('Session not found'); + } + + const userAgendFromSession = JSON.parse(session.json).passport.user + .userAgent; + + if (userAgendFromSession !== userAgend) { + throw new ForbiddenException('User-Agent does not match'); + } + return { success: true }; + } catch (error) { + throw new HttpException( + 'Error while checking auth status', + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } + public getLoginResponse( - user: LoginResponseDto & { userAgent: string } - ): LoginResponseDto { - const { id, email }: LoginResponseDto = user; - const responseData: LoginResponseDto = { id, email }; + user: SigninResponseDto & { userAgent: string } + ): SigninResponseDto { + const { id, email }: SigninResponseDto = user; + const responseData: SigninResponseDto = { id, email }; return responseData; } diff --git a/backend/src/modules/auth-module/strategies/local.strategy.ts b/backend/src/modules/auth-module/strategies/local.strategy.ts index 251073d..a77367a 100644 --- a/backend/src/modules/auth-module/strategies/local.strategy.ts +++ b/backend/src/modules/auth-module/strategies/local.strategy.ts @@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { Request } from 'express'; import { Strategy } from 'passport-local'; -import { LoginResponseDto } from '../models/dto'; +import { SigninResponseDto } from '../models/dto'; import { AuthService } from '../services/auth.service'; @Injectable() @@ -20,7 +20,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) { request: Request, email: string, password: string - ): Promise { + ): Promise { const user = await this.authService.validateUser(email, password); if (!user) { diff --git a/backend/src/shared/index.ts b/backend/src/shared/index.ts index 054a5f9..2eefdce 100644 --- a/backend/src/shared/index.ts +++ b/backend/src/shared/index.ts @@ -1,2 +1,3 @@ export * from './utils/index'; export * from './decorator/index'; +export * from './models/index'; diff --git a/backend/src/shared/models/dto/index.ts b/backend/src/shared/models/dto/index.ts new file mode 100644 index 0000000..8160785 --- /dev/null +++ b/backend/src/shared/models/dto/index.ts @@ -0,0 +1 @@ +export * from './success.dto'; diff --git a/backend/src/shared/models/dto/success.dto.ts b/backend/src/shared/models/dto/success.dto.ts new file mode 100644 index 0000000..bf2b5f1 --- /dev/null +++ b/backend/src/shared/models/dto/success.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean } from 'class-validator'; + +export class SuccessDto { + @ApiProperty({ + description: 'Success status', + type: Boolean, + }) + @IsBoolean() + public success: boolean; +} diff --git a/backend/src/shared/models/index.ts b/backend/src/shared/models/index.ts new file mode 100644 index 0000000..d58c363 --- /dev/null +++ b/backend/src/shared/models/index.ts @@ -0,0 +1 @@ +export * from './dto/index';