import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { User } from '@prisma/client'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management'; import { HttpForbiddenException } from '@gitroom/nestjs-libraries/services/exception.filter'; import { MastraService } from '@gitroom/nestjs-libraries/chat/mastra.service'; export const removeAuth = (res: Response) => { res.cookie('auth', '', { domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), ...(!process.env.NOT_SECURED ? { secure: true, httpOnly: true, sameSite: 'none', } : {}), expires: new Date(0), maxAge: -1, }); res.header('logout', 'true'); }; @Injectable() export class AuthMiddleware implements NestMiddleware { constructor( private _organizationService: OrganizationService, private _userService: UsersService ) {} async use(req: Request, res: Response, next: NextFunction) { const auth = req.headers.auth || req.cookies.auth; if (!auth) { throw new HttpForbiddenException(); } try { // Verify the JWT signature only. Never trust authorization-relevant // claims (id, isSuperAdmin, activated) from the token body — always // re-resolve the user from the database using the id. const payload = AuthService.verifyJWT(auth) as User | null; const orgHeader = req.cookies.showorg || req.headers.showorg; if (!payload?.id) { throw new HttpForbiddenException(); } let user = (await this._userService.getUserById(payload.id)) as User | null; if (!user) { throw new HttpForbiddenException(); } if (!user.activated) { throw new HttpForbiddenException(); } const impersonate = req.cookies.impersonate || req.headers.impersonate; if (user?.isSuperAdmin && impersonate) { const loadImpersonate = await this._organizationService.getUserOrg( impersonate ); if (loadImpersonate) { user = loadImpersonate.user; user.isSuperAdmin = true; delete user.password; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error req.user = user; // @ts-ignore loadImpersonate.organization.users = loadImpersonate.organization.users.filter( (f) => f.userId === user.id ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error req.org = loadImpersonate.organization; next(); return; } } delete user.password; const organization = ( await this._organizationService.getOrgsByUserId(user.id) ).filter((f) => !f.users[0].disabled); const setOrg = organization.find((org) => org.id === orgHeader) || organization[0]; if (!organization) { throw new HttpForbiddenException(); } if (!setOrg.apiKey) { await this._organizationService.updateApiKey(setOrg.id); } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error req.user = user; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error req.org = setOrg; } catch (err) { throw new HttpForbiddenException(); } next(); } }