forked from MentoNest/SkillSync_Server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjwt-auth.guard.ts
More file actions
84 lines (73 loc) · 2.24 KB
/
Copy pathjwt-auth.guard.ts
File metadata and controls
84 lines (73 loc) · 2.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
import { JwtAccessTokenPayload } from '../interfaces/jwt-payload.interface.js';
import { UserStatus } from '../../users/enums/user-status.enum.js';
/**
* #981: Enhanced JWT Auth Guard.
*
* Validates Bearer tokens, supports optional authentication mode, and
* attaches the decoded payload to the request.
*/
export interface JwtGuardOptions {
/** If true, endpoints work both with and without valid tokens */
optional?: boolean;
}
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context
.switchToHttp()
.getRequest<Request & { user?: JwtAccessTokenPayload }>();
const token = this.extractToken(req);
if (!token) {
// Optional mode: allow through without auth
return true;
}
try {
const payload = await this.jwtService.verifyAsync<JwtAccessTokenPayload>(
token,
{
secret: this.configService.get<string>('JWT_SECRET', 'dev-secret'),
},
);
if (payload.status && payload.status !== UserStatus.ACTIVE) {
throw new UnauthorizedException({
message: 'Account is not active',
code: 'account_not_active',
});
}
req.user = payload;
return true;
} catch (err: unknown) {
if (err instanceof UnauthorizedException) throw err;
if (err instanceof Error) {
if (err.name === 'TokenExpiredError') {
throw new UnauthorizedException({
message: 'Token has expired',
code: 'token_expired',
});
}
}
throw new UnauthorizedException({
message: 'Invalid token',
code: 'invalid_token',
});
}
}
private extractToken(req: Request): string | null {
const auth = req.headers?.authorization;
if (!auth?.startsWith('Bearer ')) return null;
return auth.slice(7).trim() || null;
}
}