Skip to content

Commit f5ca2ff

Browse files
committed
Fixing JWT problems
1 parent 96b50fa commit f5ca2ff

File tree

11 files changed

+313
-115
lines changed

11 files changed

+313
-115
lines changed

backend/package-lock.json

Lines changed: 77 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
"@nestjs/common": "^10.0.0",
3434
"@nestjs/config": "^3.1.1",
3535
"@nestjs/core": "^10.0.0",
36+
"@nestjs/jwt": "^10.2.0",
3637
"@nestjs/platform-express": "^10.0.0",
38+
"@nestjs/passport": "^10.0.3",
3739
"@types/jest": "^29.5.12",
3840
"axios": "^1.8.1",
3941
"class-transformer": "^0.5.1",
@@ -51,7 +53,9 @@
5153
"web-vitals": "^2.1.4",
5254
"aws-cdk-lib": "2.139.0",
5355
"@nestjs/swagger": "^7.1.13",
54-
"swagger-ui-express": "^5.0.0"
56+
"swagger-ui-express": "^5.0.0",
57+
"passport": "^0.7.0",
58+
"passport-jwt": "^4.0.1"
5559
},
5660
"devDependencies": {
5761
"@aws-cdk/assert": "^2.68.0",
Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,108 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { AuthMiddleware } from './auth.middleware';
3-
import { JwtService } from './jwt.service';
3+
import { JwtService } from '@nestjs/jwt';
4+
import { ConfigService } from '@nestjs/config';
45
import { vi, describe, it, expect, beforeEach } from 'vitest';
56

67
describe('AuthMiddleware', () => {
78
let middleware: AuthMiddleware;
9+
let jwtService: JwtService;
810

9-
// Create a mock JwtService
1011
const mockJwtService = {
11-
verifyToken: vi.fn(),
12+
verify: vi.fn(),
1213
};
1314

14-
beforeEach(async () => {
15-
vi.clearAllMocks();
15+
const mockConfigService = {
16+
get: vi.fn().mockReturnValue('test-secret'),
17+
};
1618

19+
beforeEach(async () => {
1720
const module: TestingModule = await Test.createTestingModule({
1821
providers: [
1922
AuthMiddleware,
2023
{
2124
provide: JwtService,
2225
useValue: mockJwtService,
2326
},
27+
{
28+
provide: ConfigService,
29+
useValue: mockConfigService,
30+
},
2431
],
2532
}).compile();
2633

2734
middleware = module.get<AuthMiddleware>(AuthMiddleware);
35+
jwtService = module.get<JwtService>(JwtService);
2836
});
2937

3038
it('should be defined', () => {
3139
expect(middleware).toBeDefined();
3240
});
3341

34-
describe('use', () => {
35-
it('should not add user to request when token is missing', async () => {
36-
const mockRequest: any = {
37-
headers: {},
38-
};
39-
const mockResponse = {};
40-
const mockNext = vi.fn();
42+
it('should set user on request when valid token is provided', () => {
43+
const mockPayload = {
44+
sub: 'user123',
45+
username: 'testuser',
46+
47+
groups: ['users'],
48+
};
49+
50+
mockJwtService.verify.mockReturnValue(mockPayload);
51+
52+
const mockRequest = {
53+
headers: {
54+
authorization: 'Bearer valid-token',
55+
},
56+
user: undefined,
57+
};
58+
59+
const mockResponse = {};
60+
const mockNext = vi.fn();
61+
62+
middleware.use(mockRequest as any, mockResponse as any, mockNext);
63+
64+
expect(mockRequest.user).toEqual({
65+
id: 'user123',
66+
username: 'testuser',
67+
68+
groups: ['users'],
69+
});
70+
expect(mockNext).toHaveBeenCalled();
71+
});
72+
73+
it('should not set user when no token is provided', () => {
74+
const mockRequest = {
75+
headers: {},
76+
user: undefined,
77+
};
78+
79+
const mockResponse = {};
80+
const mockNext = vi.fn();
81+
82+
middleware.use(mockRequest as any, mockResponse as any, mockNext);
4183

42-
// Test the middleware
43-
await middleware.use(mockRequest, mockResponse as any, mockNext);
84+
expect(mockRequest.user).toBeUndefined();
85+
expect(mockNext).toHaveBeenCalled();
86+
});
4487

45-
expect(mockRequest).not.toHaveProperty('user');
46-
expect(mockNext).toHaveBeenCalled();
47-
expect(mockJwtService.verifyToken).not.toHaveBeenCalled();
88+
it('should not set user when token verification fails', () => {
89+
mockJwtService.verify.mockImplementation(() => {
90+
throw new Error('Invalid token');
4891
});
92+
93+
const mockRequest = {
94+
headers: {
95+
authorization: 'Bearer invalid-token',
96+
},
97+
user: undefined,
98+
};
99+
100+
const mockResponse = {};
101+
const mockNext = vi.fn();
102+
103+
middleware.use(mockRequest as any, mockResponse as any, mockNext);
104+
105+
expect(mockRequest.user).toBeUndefined();
106+
expect(mockNext).toHaveBeenCalled();
49107
});
50108
});

backend/src/auth/auth.middleware.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
11
import { Injectable, NestMiddleware } from '@nestjs/common';
22
import { Request, Response, NextFunction } from 'express';
3-
import { JwtService } from './jwt.service';
3+
import { JwtService } from '@nestjs/jwt';
4+
import { ConfigService } from '@nestjs/config';
45

56
@Injectable()
67
export class AuthMiddleware implements NestMiddleware {
7-
constructor(private jwtService: JwtService) {}
8+
constructor(
9+
private readonly jwtService: JwtService,
10+
private readonly configService: ConfigService
11+
) {}
812

9-
async use(req: Request, res: Response, next: NextFunction) {
10-
const token = req.headers['x-amzn-oidc-data'] as string;
13+
use(req: Request, res: Response, next: NextFunction) {
14+
const authHeader = req.headers.authorization;
1115

12-
if (token) {
16+
if (authHeader && authHeader.startsWith('Bearer ')) {
1317
try {
14-
const user = await this.jwtService.verifyToken(token);
15-
req.user = user;
16-
} catch (error: unknown) {
17-
console.error(
18-
'Token validation failed:',
19-
error instanceof Error ? error.message : 'Unknown error',
20-
);
18+
const token = authHeader.substring(7);
19+
const payload = this.jwtService.verify(token, {
20+
secret: this.configService.get<string>('JWT_SECRET')
21+
});
22+
23+
req.user = {
24+
id: payload.sub,
25+
username: payload.username,
26+
email: payload.email,
27+
groups: payload.groups || [],
28+
};
29+
} catch (error) {
30+
// If token verification fails, we don't set the user
31+
// but we also don't block the request - protected routes
32+
// will be handled by JwtAuthGuard
2133
}
2234
}
2335

backend/src/auth/auth.module.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
import { Module } from '@nestjs/common';
2-
import { ConfigModule } from '@nestjs/config';
3-
import { JwtService } from './jwt.service';
2+
import { JwtModule } from '@nestjs/jwt';
3+
import { ConfigModule, ConfigService } from '@nestjs/config';
44
import { JwtAuthGuard } from './jwt-auth.guard';
5+
import { JwtStrategy } from './jwt.strategy';
56

67
@Module({
7-
imports: [ConfigModule],
8-
providers: [JwtService, JwtAuthGuard],
9-
exports: [JwtService, JwtAuthGuard],
8+
imports: [
9+
ConfigModule,
10+
JwtModule.registerAsync({
11+
imports: [ConfigModule],
12+
inject: [ConfigService],
13+
useFactory: async (configService: ConfigService) => ({
14+
secret: configService.get<string>('JWT_SECRET'),
15+
signOptions: {
16+
expiresIn: configService.get<string>('JWT_EXPIRES_IN', '1h'),
17+
},
18+
}),
19+
}),
20+
],
21+
providers: [JwtAuthGuard, JwtStrategy],
22+
exports: [JwtModule, JwtAuthGuard],
23+
controllers: [],
1024
})
1125
export class AuthModule {}

0 commit comments

Comments
 (0)