Files
iddaai-be/src/modules/auth/guards/auth.guards.ts
T
fahricansecer 27e96da31d
Deploy Iddaai Backend / build-and-deploy (push) Successful in 29s
main
2026-05-04 18:00:40 +03:00

159 lines
3.6 KiB
TypeScript
Executable File

import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
ForbiddenException,
} from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";
import { Request } from "express";
import {
IS_PUBLIC_KEY,
ROLES_KEY,
PERMISSIONS_KEY,
} from "../../../common/decorators";
import { normalizeRole } from "../../../common/constants/roles";
interface AuthenticatedUser {
id: string;
email: string;
roles: string[];
role?: string;
permissions: string[];
}
/**
* JWT Auth Guard - Validates JWT token
*/
@Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest<Request>();
if (request?.method === "OPTIONS") {
return true;
}
// Check if route is public
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
handleRequest<TUser = AuthenticatedUser>(
err: Error | null,
user: TUser | false,
info: any,
): TUser {
if (err || !user) {
if (info?.name === "TokenExpiredError") {
throw new UnauthorizedException("TOKEN_EXPIRED");
}
throw err || new UnauthorizedException("AUTH_REQUIRED");
}
return user;
}
}
/**
* Roles Guard - Check if user has required roles
*/
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest<Request>();
if (req?.method === "OPTIONS") {
return true;
}
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredRoles || requiredRoles.length === 0) {
return true;
}
const user = req.user as AuthenticatedUser | undefined;
if (!user) {
return false;
}
const normalizedUserRoles = (
user.roles?.length ? user.roles : user.role ? [user.role] : []
).map((role) => normalizeRole(role));
const normalizedRequiredRoles = requiredRoles.map((role) =>
normalizeRole(role),
);
if (normalizedUserRoles.length === 0) {
return false;
}
const hasRole = normalizedRequiredRoles.some((role) =>
normalizedUserRoles.includes(role),
);
if (!hasRole) {
throw new ForbiddenException("PERMISSION_DENIED");
}
return true;
}
}
/**
* Permissions Guard - Check if user has required permissions
*/
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest<Request>();
if (req?.method === "OPTIONS") {
return true;
}
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
PERMISSIONS_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredPermissions || requiredPermissions.length === 0) {
return true;
}
const user = req.user as AuthenticatedUser | undefined;
if (!user || !user.permissions) {
return false;
}
const hasPermission = requiredPermissions.every((permission) =>
user.permissions.includes(permission),
);
if (!hasPermission) {
throw new ForbiddenException("PERMISSION_DENIED");
}
return true;
}
}