diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index ab77991..ea20d23 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -10,6 +10,9 @@ import { ApiBearerAuth, ApiResponse } from '@nestjs/swagger'; export class AuthController { constructor(private readonly authService: AuthService) {} + /** + * Checks if the user has a valid session + */ @Get('session') async getSession(@Req() req: any) { try { @@ -61,8 +64,27 @@ export class AuthController { await this.authService.register(body.username, body.password, body.email); return { message: 'User registered successfully' }; } - + + /** + * Logs in a user + */ @Post('login') + @ApiResponse({ + status: 200, + description: "User logged in successfully" + }) + @ApiResponse({ + status: 400, + description: "{Error encountered}" + }) + @ApiResponse({ + status: 401, + description: "Invalid credentials" + }) + @ApiResponse({ + status: 500, + description: "Internal server error" + }) async login( @Res({ passthrough: true }) response: Response, @Body('username') username: string, @@ -91,9 +113,29 @@ export class AuthController { return result } + /** + * + * Set new password + */ @Post('set-password') @UseGuards(VerifyUserGuard) @ApiBearerAuth() + @ApiResponse({ + status: 200, + description: "Password set successfully" + }) + @ApiResponse({ + status: 400, + description: "{Error encountered}" + }) + @ApiResponse({ + status: 401, + description: "Invalid credentials" + }) + @ApiResponse({ + status: 500, + description: "Internal server error" + }) async setNewPassword( @Body('newPassword') newPassword: string, @Body('session') session: string, @@ -103,9 +145,29 @@ export class AuthController { return await this.authService.setNewPassword(newPassword, session, username, email); } + /** + * + * Update user profile for username, email, and position_or_role + */ @Post('update-profile') @UseGuards(VerifyUserGuard) @ApiBearerAuth() + @ApiResponse({ + status: 200, + description: "Profile updated successfully" + }) + @ApiResponse({ + status: 400, + description: "{Error encountered}" + }) + @ApiResponse({ + status: 401, + description: "Invalid credentials" + }) + @ApiResponse({ + status: 500, + description: "Internal server error" + }) async updateProfile( @Body('username') username: string, @Body('email') email: string, diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 56076ad..a6e24c0 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -58,6 +58,8 @@ constructor() { } + // purpose statement: registers an user into cognito and dynamodb + // use case: new employee is joining async register( username: string, password: string, @@ -72,21 +74,25 @@ constructor() { throw new InternalServerErrorException("Server configuration error"); } + // Validate environment variables if (!tableName) { this.logger.error("DynamoDB User Table Name is not defined in environment variables."); throw new InternalServerErrorException("Server configuration error"); } - // Validate input parameters + // Validate input parameters for username, password, and email if (!username || username.trim().length === 0) { + this.logger.warn("Registration failed: Username is required"); throw new BadRequestException("Username is required"); } if (!password || password.length < 8) { + this.logger.warn("Registration failed: Password must be at least 8 characters long"); throw new BadRequestException("Password must be at least 8 characters long"); } if (!email || !this.isValidEmail(email)) { + this.logger.warn("Registration failed: Valid email address is required"); throw new BadRequestException("Valid email address is required"); } @@ -298,7 +304,8 @@ private isValidEmail(email: string): boolean { - // Overall, needs better undefined handling and optional adding + // purpose statement: logs in an user via cognito and retrieves user data from dynamodb + // use case: employee is trying to access the app, needs to have an account already async login( username: string, password: string @@ -314,11 +321,21 @@ private isValidEmail(email: string): boolean { const clientId = process.env.COGNITO_CLIENT_ID; const clientSecret = process.env.COGNITO_CLIENT_SECRET; + // Validate environment variables if (!clientId || !clientSecret) { this.logger.error("Cognito Client ID or Secret is not defined."); throw new Error("Cognito Client ID or Secret is not defined."); } + // Validate input parameters for username and password + if (!username || username.trim().length === 0) { + throw new BadRequestException("Username is required"); + } + + if (!password || password.length === 0) { + throw new BadRequestException("Password is required"); + } + const hatch = this.computeHatch(username, clientId, clientSecret); // Todo, change constants of AUTH_FLOW types & other constants in repo @@ -460,6 +477,8 @@ private isValidEmail(email: string): boolean { } } + // purpose statement: sets a new password for an user in cognito + // use case: employee changing password after forgetting password async setNewPassword( newPassword: string, session: string, @@ -474,6 +493,22 @@ private isValidEmail(email: string): boolean { throw new Error("Cognito Client ID or Secret is not defined."); } + // Validate input parameters for newPassword, session, and username + if (!newPassword || newPassword.length === 0) { + this.logger.error("Set New Password failed: New password is required"); + throw new BadRequestException("New password is required"); + } + + if (!session || session.length === 0) { + this.logger.error("Set New Password failed: Session is required"); + throw new BadRequestException("Session is required"); + } + + if (!username || username.trim().length === 0) { + this.logger.error("Set New Password failed: Username is required"); + throw new BadRequestException("Username is required"); + } + const hatch = this.computeHatch(username, clientId, clientSecret); const challengeResponses: any = { @@ -483,6 +518,7 @@ private isValidEmail(email: string): boolean { }; if (email) { + this.logger.log("Including email in challenge responses"); challengeResponses.email = email; } @@ -497,6 +533,7 @@ private isValidEmail(email: string): boolean { const response = await this.cognito .respondToAuthChallenge(params) .promise(); + this.logger.log("Responded to auth challenge for new password"); if ( !response.AuthenticationResult || @@ -516,11 +553,29 @@ private isValidEmail(email: string): boolean { } } + // purpose statement: updates user profile info in dynamodb + // use case: employee is updating their profile information async updateProfile( username: string, email: string, position_or_role: string ) { + // Validate input parameters for username, email, and position_or_role + if (!username || username.trim().length === 0) { + this.logger.error("Update Profile failed: Username is required"); + throw new BadRequestException("Username is required"); + } + + if (!email || email.trim().length === 0) { + this.logger.error("Update Profile failed: Email is required"); + throw new BadRequestException("Email is required"); + } + + if (!position_or_role || position_or_role.trim().length === 0) { + this.logger.error("Update Profile failed: Position or role is required"); + throw new BadRequestException("Position or role is required"); + } + this.logger.log(`Updating profile for user ${username}`); const tableName = process.env.DYNAMODB_USER_TABLE_NAME || "TABLE_FAILURE"; const params = { @@ -551,6 +606,8 @@ private isValidEmail(email: string): boolean { // Add this to auth.service.ts +// purpose statement: validates a user's session token via cognito and retrieves user data from dynamodb +// use case: employee is accessing the app with an existing session token async validateSession(accessToken: string): Promise { try { // Use Cognito's getUser method to validate the token @@ -564,6 +621,7 @@ async validateSession(accessToken: string): Promise { // Extract email from user attributes for (const attribute of getUserResponse.UserAttributes) { if (attribute.Name === 'email') { + this.logger.log(`Extracted email from user attributes: ${attribute.Value}`); email = attribute.Value; break; } @@ -582,6 +640,7 @@ async validateSession(accessToken: string): Promise { const user = userResult.Item; if (!user) { + this.logger.error(`User not found in database for username: ${username}`); throw new Error('User not found in database'); }