gohorsejobs/backoffice/src/auth/jwt-auth.guard.ts

76 lines
2.2 KiB
TypeScript

import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Reflector } from '@nestjs/core';
import * as jwt from 'jsonwebtoken';
import { IS_PUBLIC_KEY } from './public.decorator';
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private readonly configService: ConfigService,
private readonly reflector: Reflector,
) { }
canActivate(context: ExecutionContext): boolean {
// 1. Check if route is public
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
const request = context.switchToHttp().getRequest();
const token = this.extractToken(request);
if (!token) {
throw new UnauthorizedException('Missing authentication token');
}
try {
const secret = this.configService.get<string>('JWT_SECRET');
if (!secret) {
throw new UnauthorizedException('JWT secret not configured');
}
const payload = jwt.verify(token, secret) as any;
// 2. Role Check (from HEAD)
// Only allow admin users to access backoffice
if (payload.role !== 'admin' && payload.role !== 'superadmin') {
throw new UnauthorizedException('Admin access required');
}
request.user = payload;
return true;
} catch {
throw new UnauthorizedException('Invalid or expired token');
}
}
private extractToken(request: any): string | null {
// 1. Try Authorization header first
const authHeader = request.headers?.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.slice(7);
}
// 2. Fallback to cookies (support both names)
const cookies = request.cookies;
if (cookies?.jwt) {
return cookies.jwt;
}
if (cookies?.auth_token) {
return cookies.auth_token;
}
return null;
}
}