@quanticjs/auth-web-bff 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bff.controller.js +17 -1
- package/dist/bff.service.d.ts +5 -0
- package/dist/bff.service.js +23 -2
- package/dist/interfaces.d.ts +3 -0
- package/package.json +2 -2
package/dist/bff.controller.js
CHANGED
|
@@ -46,6 +46,7 @@ let BffController = class BffController {
|
|
|
46
46
|
try {
|
|
47
47
|
const { sessionId, returnTo } = await this.bffService.handleCallback(code, state, codeVerifier);
|
|
48
48
|
res.cookie(this.bffService.getCookieName(), sessionId, this.bffService.getCookieOptions());
|
|
49
|
+
res.cookie(this.bffService.getCsrfCookieName(), this.bffService.generateCsrfToken(), this.bffService.getCsrfCookieOptions());
|
|
49
50
|
res.redirect(returnTo);
|
|
50
51
|
}
|
|
51
52
|
catch {
|
|
@@ -53,6 +54,12 @@ let BffController = class BffController {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
async refresh(req, res) {
|
|
57
|
+
const csrfHeader = req.headers['x-csrf-token'];
|
|
58
|
+
const csrfCookie = req.cookies?.[this.bffService.getCsrfCookieName()];
|
|
59
|
+
if (!this.bffService.validateCsrf(csrfHeader, csrfCookie)) {
|
|
60
|
+
res.status(403).json({ message: 'Invalid CSRF token' });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
56
63
|
const sessionId = req.cookies?.[this.bffService.getCookieName()];
|
|
57
64
|
if (!sessionId) {
|
|
58
65
|
res.status(401).json({ message: 'No session' });
|
|
@@ -66,12 +73,21 @@ let BffController = class BffController {
|
|
|
66
73
|
res.json({ success: true });
|
|
67
74
|
}
|
|
68
75
|
async logout(req, res) {
|
|
76
|
+
const csrfHeader = req.headers['x-csrf-token'];
|
|
77
|
+
const csrfCookie = req.cookies?.[this.bffService.getCsrfCookieName()];
|
|
78
|
+
if (!this.bffService.validateCsrf(csrfHeader, csrfCookie)) {
|
|
79
|
+
res.status(403).json({ message: 'Invalid CSRF token' });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
69
82
|
const cookieName = this.bffService.getCookieName();
|
|
70
83
|
const sessionId = req.cookies?.[cookieName];
|
|
71
84
|
if (sessionId) {
|
|
72
85
|
await this.bffService.destroySession(sessionId);
|
|
73
86
|
}
|
|
74
|
-
res
|
|
87
|
+
res
|
|
88
|
+
.clearCookie(cookieName, this.bffService.getCookieOptions())
|
|
89
|
+
.clearCookie(this.bffService.getCsrfCookieName(), this.bffService.getCsrfCookieOptions())
|
|
90
|
+
.json({ success: true });
|
|
75
91
|
}
|
|
76
92
|
async me(req, res) {
|
|
77
93
|
const cookieName = this.bffService.getCookieName();
|
package/dist/bff.service.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class BffService implements OnModuleInit {
|
|
|
10
10
|
private readonly sessionTtl;
|
|
11
11
|
private readonly sessionPrefix;
|
|
12
12
|
private readonly cookieName;
|
|
13
|
+
private readonly csrfCookieName;
|
|
13
14
|
constructor(options: BffModuleOptions, redis: Redis | undefined);
|
|
14
15
|
onModuleInit(): Promise<void>;
|
|
15
16
|
getAuthorizationUrl(returnTo?: string): {
|
|
@@ -27,6 +28,10 @@ export declare class BffService implements OnModuleInit {
|
|
|
27
28
|
getUserInfo(sessionId: string): Promise<Record<string, unknown> | null>;
|
|
28
29
|
getCookieOptions(): Record<string, unknown>;
|
|
29
30
|
getCookieName(): string;
|
|
31
|
+
getCsrfCookieName(): string;
|
|
32
|
+
getCsrfCookieOptions(): Record<string, unknown>;
|
|
33
|
+
generateCsrfToken(): string;
|
|
34
|
+
validateCsrf(headerValue: string | undefined, cookieValue: string | undefined): boolean;
|
|
30
35
|
private saveSession;
|
|
31
36
|
private toPublicKeycloakUrl;
|
|
32
37
|
private getCallbackUrl;
|
package/dist/bff.service.js
CHANGED
|
@@ -16,6 +16,7 @@ exports.BffService = void 0;
|
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
17
|
const openid_client_1 = require("openid-client");
|
|
18
18
|
const uuid_1 = require("uuid");
|
|
19
|
+
const crypto_1 = require("crypto");
|
|
19
20
|
const core_1 = require("@quanticjs/core");
|
|
20
21
|
const interfaces_1 = require("./interfaces");
|
|
21
22
|
function extractRealmRoles(accessToken) {
|
|
@@ -38,12 +39,14 @@ let BffService = class BffService {
|
|
|
38
39
|
sessionTtl;
|
|
39
40
|
sessionPrefix;
|
|
40
41
|
cookieName;
|
|
42
|
+
csrfCookieName;
|
|
41
43
|
constructor(options, redis) {
|
|
42
44
|
this.options = options;
|
|
43
45
|
this.redis = redis;
|
|
44
46
|
this.sessionTtl = options.session?.ttlSeconds ?? 7 * 24 * 3600;
|
|
45
47
|
this.sessionPrefix = options.session?.prefix ?? 'session:';
|
|
46
|
-
this.cookieName = options.session?.cookieName ?? 'sid';
|
|
48
|
+
this.cookieName = options.session?.cookieName ?? '__Host-sid';
|
|
49
|
+
this.csrfCookieName = options.csrf?.cookieName ?? '__Host-csrf';
|
|
47
50
|
}
|
|
48
51
|
async onModuleInit() {
|
|
49
52
|
const { keycloak } = this.options;
|
|
@@ -166,7 +169,7 @@ let BffService = class BffService {
|
|
|
166
169
|
return {
|
|
167
170
|
httpOnly: true,
|
|
168
171
|
secure: process.env.NODE_ENV === 'production',
|
|
169
|
-
sameSite: '
|
|
172
|
+
sameSite: 'strict',
|
|
170
173
|
path: '/',
|
|
171
174
|
maxAge: this.sessionTtl * 1000,
|
|
172
175
|
};
|
|
@@ -174,6 +177,24 @@ let BffService = class BffService {
|
|
|
174
177
|
getCookieName() {
|
|
175
178
|
return this.cookieName;
|
|
176
179
|
}
|
|
180
|
+
getCsrfCookieName() {
|
|
181
|
+
return this.csrfCookieName;
|
|
182
|
+
}
|
|
183
|
+
getCsrfCookieOptions() {
|
|
184
|
+
return {
|
|
185
|
+
httpOnly: false,
|
|
186
|
+
secure: process.env.NODE_ENV === 'production',
|
|
187
|
+
sameSite: 'strict',
|
|
188
|
+
path: '/',
|
|
189
|
+
maxAge: this.sessionTtl * 1000,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
generateCsrfToken() {
|
|
193
|
+
return (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
194
|
+
}
|
|
195
|
+
validateCsrf(headerValue, cookieValue) {
|
|
196
|
+
return !!headerValue && !!cookieValue && headerValue === cookieValue;
|
|
197
|
+
}
|
|
177
198
|
async saveSession(sessionId, data) {
|
|
178
199
|
if (!this.redis)
|
|
179
200
|
throw new Error('Redis not available');
|
package/dist/interfaces.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quanticjs/auth-web-bff",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "BFF authentication module — Keycloak OIDC, Redis sessions, httpOnly cookies",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"clean": "rm -rf dist"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@quanticjs/core": "^5.
|
|
12
|
+
"@quanticjs/core": "^5.2.0"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|