import { StatusCodes } from '$lib/constants/status-codes'; import { Controller } from '$lib/server/api/common/types/controller'; import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto'; import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto'; import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'; import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'; import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'; import { IamService } from '$lib/server/api/services/iam.service'; import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service'; import { LuciaService } from '$lib/server/api/services/lucia.service'; import { zValidator } from '@hono/zod-validator'; import { setCookie } from 'hono/cookie'; import { inject, injectable } from 'tsyringe'; import { requireAuth } from '../middleware/require-auth.middleware'; @injectable() export class IamController extends Controller { constructor( @inject(IamService) private readonly iamService: IamService, @inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, @inject(LuciaService) private luciaService: LuciaService, ) { super(); } routes() { const tags = ['IAM']; return this.controller .get('/', requireAuth, async (c) => { const user = c.var.user; return c.json({ user }); }) .put('/update/profile', requireAuth, zValidator('json', updateProfileDto), limiter({ limit: 30, minutes: 60 }), async (c) => { const user = c.var.user; const { firstName, lastName, username } = c.req.valid('json'); const updatedUser = await this.iamService.updateProfile(user.id, { firstName, lastName, username }); if (!updatedUser) { return c.json('Username already in use', StatusCodes.BAD_REQUEST); } return c.json({ user: updatedUser }, StatusCodes.OK); }) .post('/verify/password', requireAuth, zValidator('json', verifyPasswordDto), limiter({ limit: 10, minutes: 60 }), async (c) => { const user = c.var.user; const { password } = c.req.valid('json'); const passwordVerified = await this.iamService.verifyPassword(user.id, { password }); if (!passwordVerified) { console.log('Incorrect password'); return c.json('Incorrect password', StatusCodes.BAD_REQUEST); } return c.json({}, StatusCodes.OK); }) .put('/update/password', requireAuth, zValidator('json', changePasswordDto), limiter({ limit: 10, minutes: 60 }), async (c) => { const user = c.var.user; const { password, confirm_password } = c.req.valid('json'); if (password !== confirm_password) { return c.json('Passwords do not match', StatusCodes.BAD_REQUEST); } try { await this.iamService.updatePassword(user.id, { password, confirm_password }); await this.luciaService.lucia.invalidateUserSessions(user.id); await this.loginRequestService.createUserSession(user.id, c.req, undefined); const sessionCookie = this.luciaService.lucia.createBlankSessionCookie(); setCookie(c, sessionCookie.name, sessionCookie.value, { path: sessionCookie.attributes.path, maxAge: sessionCookie.attributes.maxAge, domain: sessionCookie.attributes.domain, sameSite: sessionCookie.attributes.sameSite as any, secure: sessionCookie.attributes.secure, httpOnly: sessionCookie.attributes.httpOnly, expires: sessionCookie.attributes.expires, }); return c.json({ status: 'success' }); } catch (error) { console.error('Error updating password', error); return c.json('Error updating password', StatusCodes.BAD_REQUEST); } }) .post('/update/email', requireAuth, zValidator('json', updateEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => { const user = c.var.user; const { email } = c.req.valid('json'); const updatedUser = await this.iamService.updateEmail(user.id, { email }); if (!updatedUser) { return c.json('Email already in use', StatusCodes.BAD_REQUEST); } return c.json({ user: updatedUser }, StatusCodes.OK); }) .post('/logout', requireAuth, async (c) => { const sessionId = c.var.session.id; await this.iamService.logout(sessionId); const sessionCookie = this.luciaService.lucia.createBlankSessionCookie(); setCookie(c, sessionCookie.name, sessionCookie.value, { path: sessionCookie.attributes.path, maxAge: sessionCookie.attributes.maxAge, domain: sessionCookie.attributes.domain, sameSite: sessionCookie.attributes.sameSite as any, secure: sessionCookie.attributes.secure, httpOnly: sessionCookie.attributes.httpOnly, expires: sessionCookie.attributes.expires, }); return c.json({ status: 'success' }); }); } }