@quanticjs/auth-web-bff 5.7.0 → 5.9.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 +5 -1
- package/dist/bff.service.d.ts +1 -0
- package/dist/bff.service.js +51 -9
- package/dist/interfaces.d.ts +2 -0
- package/package.json +3 -2
package/dist/bff.controller.js
CHANGED
|
@@ -81,13 +81,17 @@ let BffController = class BffController {
|
|
|
81
81
|
}
|
|
82
82
|
const cookieName = this.bffService.getCookieName();
|
|
83
83
|
const sessionId = req.cookies?.[cookieName];
|
|
84
|
+
let idToken;
|
|
84
85
|
if (sessionId) {
|
|
86
|
+
const session = await this.bffService.getSession(sessionId);
|
|
87
|
+
idToken = session?.idToken;
|
|
85
88
|
await this.bffService.destroySession(sessionId);
|
|
86
89
|
}
|
|
90
|
+
const endSessionUrl = this.bffService.getEndSessionUrl(idToken);
|
|
87
91
|
res
|
|
88
92
|
.clearCookie(cookieName, this.bffService.getCookieOptions())
|
|
89
93
|
.clearCookie(this.bffService.getCsrfCookieName(), this.bffService.getCsrfCookieOptions())
|
|
90
|
-
.json({ success: true });
|
|
94
|
+
.json({ success: true, endSessionUrl });
|
|
91
95
|
}
|
|
92
96
|
async me(req, res) {
|
|
93
97
|
const cookieName = this.bffService.getCookieName();
|
package/dist/bff.service.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare class BffService implements OnModuleInit {
|
|
|
25
25
|
getAccessToken(sessionId: string): Promise<string | null>;
|
|
26
26
|
refreshSession(sessionId: string, session?: SessionData): Promise<string | null>;
|
|
27
27
|
destroySession(sessionId: string): Promise<void>;
|
|
28
|
+
getEndSessionUrl(idToken?: string): string | null;
|
|
28
29
|
getUserInfo(sessionId: string): Promise<Record<string, unknown> | null>;
|
|
29
30
|
getCookieOptions(): Record<string, unknown>;
|
|
30
31
|
getCookieName(): string;
|
package/dist/bff.service.js
CHANGED
|
@@ -19,15 +19,20 @@ const uuid_1 = require("uuid");
|
|
|
19
19
|
const crypto_1 = require("crypto");
|
|
20
20
|
const core_1 = require("@quanticjs/core");
|
|
21
21
|
const interfaces_1 = require("./interfaces");
|
|
22
|
-
function
|
|
22
|
+
function extractTokenClaims(accessToken, clientId) {
|
|
23
23
|
try {
|
|
24
24
|
const payload = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64url').toString());
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return {
|
|
26
|
+
realmRoles: Array.isArray(payload.realm_access?.roles)
|
|
27
|
+
? payload.realm_access.roles
|
|
28
|
+
: [],
|
|
29
|
+
clientRoles: Array.isArray(payload.resource_access?.[clientId]?.roles)
|
|
30
|
+
? payload.resource_access[clientId].roles
|
|
31
|
+
: [],
|
|
32
|
+
};
|
|
28
33
|
}
|
|
29
34
|
catch {
|
|
30
|
-
return [];
|
|
35
|
+
return { realmRoles: [], clientRoles: [] };
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
38
|
let BffService = class BffService {
|
|
@@ -78,6 +83,11 @@ let BffService = class BffService {
|
|
|
78
83
|
const tokenSet = await this.client.callback(this.getCallbackUrl(), { code, state, iss: this.client.issuer.metadata.issuer }, { code_verifier: codeVerifier, state });
|
|
79
84
|
const claims = tokenSet.claims();
|
|
80
85
|
const sessionId = (0, uuid_1.v4)();
|
|
86
|
+
const { realmRoles, clientRoles } = extractTokenClaims(tokenSet.access_token, this.options.keycloak.clientId);
|
|
87
|
+
let permissions = clientRoles;
|
|
88
|
+
if (this.options.permissionResolver) {
|
|
89
|
+
permissions = await this.options.permissionResolver(realmRoles, clientRoles);
|
|
90
|
+
}
|
|
81
91
|
const sessionData = {
|
|
82
92
|
accessToken: tokenSet.access_token,
|
|
83
93
|
refreshToken: tokenSet.refresh_token,
|
|
@@ -86,7 +96,8 @@ let BffService = class BffService {
|
|
|
86
96
|
id: claims.sub,
|
|
87
97
|
email: claims.email ?? '',
|
|
88
98
|
displayName: claims.name ?? claims.preferred_username ?? '',
|
|
89
|
-
roles:
|
|
99
|
+
roles: realmRoles,
|
|
100
|
+
permissions,
|
|
90
101
|
username: claims.preferred_username,
|
|
91
102
|
};
|
|
92
103
|
await this.saveSession(sessionId, sessionData);
|
|
@@ -123,13 +134,19 @@ let BffService = class BffService {
|
|
|
123
134
|
return null;
|
|
124
135
|
try {
|
|
125
136
|
const tokenSet = await this.client.refresh(sess.refreshToken);
|
|
137
|
+
const { realmRoles, clientRoles } = extractTokenClaims(tokenSet.access_token, this.options.keycloak.clientId);
|
|
138
|
+
let permissions = clientRoles;
|
|
139
|
+
if (this.options.permissionResolver) {
|
|
140
|
+
permissions = await this.options.permissionResolver(realmRoles, clientRoles);
|
|
141
|
+
}
|
|
126
142
|
const updated = {
|
|
127
143
|
...sess,
|
|
128
144
|
accessToken: tokenSet.access_token,
|
|
129
145
|
refreshToken: tokenSet.refresh_token ?? sess.refreshToken,
|
|
130
146
|
idToken: tokenSet.id_token ?? sess.idToken,
|
|
131
147
|
expiresAt: tokenSet.expires_at ?? Math.floor(Date.now() / 1000) + 300,
|
|
132
|
-
roles:
|
|
148
|
+
roles: realmRoles,
|
|
149
|
+
permissions,
|
|
133
150
|
};
|
|
134
151
|
await this.saveSession(sessionId, updated);
|
|
135
152
|
return updated.accessToken;
|
|
@@ -143,9 +160,19 @@ let BffService = class BffService {
|
|
|
143
160
|
const session = await this.getSession(sessionId);
|
|
144
161
|
if (!this.redis)
|
|
145
162
|
return;
|
|
146
|
-
if (session
|
|
163
|
+
if (session) {
|
|
164
|
+
try {
|
|
165
|
+
if (session.refreshToken) {
|
|
166
|
+
await this.client.revoke(session.refreshToken, 'refresh_token');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// best-effort revocation
|
|
171
|
+
}
|
|
147
172
|
try {
|
|
148
|
-
|
|
173
|
+
if (session.accessToken) {
|
|
174
|
+
await this.client.revoke(session.accessToken, 'access_token');
|
|
175
|
+
}
|
|
149
176
|
}
|
|
150
177
|
catch {
|
|
151
178
|
// best-effort revocation
|
|
@@ -153,6 +180,20 @@ let BffService = class BffService {
|
|
|
153
180
|
}
|
|
154
181
|
await this.redis.del(this.sessionPrefix + sessionId);
|
|
155
182
|
}
|
|
183
|
+
getEndSessionUrl(idToken) {
|
|
184
|
+
const endSessionEndpoint = this.client.issuer.metadata.end_session_endpoint;
|
|
185
|
+
if (!endSessionEndpoint)
|
|
186
|
+
return null;
|
|
187
|
+
const publicUrl = endSessionEndpoint.replace(this.internalKeycloakBase, this.publicKeycloakBase);
|
|
188
|
+
const redirectUri = this.options.publicUrl ?? 'http://localhost:5173';
|
|
189
|
+
const params = new URLSearchParams({
|
|
190
|
+
client_id: this.options.keycloak.clientId,
|
|
191
|
+
post_logout_redirect_uri: redirectUri,
|
|
192
|
+
});
|
|
193
|
+
if (idToken)
|
|
194
|
+
params.set('id_token_hint', idToken);
|
|
195
|
+
return `${publicUrl}?${params.toString()}`;
|
|
196
|
+
}
|
|
156
197
|
async getUserInfo(sessionId) {
|
|
157
198
|
const session = await this.getSession(sessionId);
|
|
158
199
|
if (!session)
|
|
@@ -162,6 +203,7 @@ let BffService = class BffService {
|
|
|
162
203
|
email: session.email,
|
|
163
204
|
displayName: session.displayName,
|
|
164
205
|
roles: session.roles,
|
|
206
|
+
permissions: session.permissions,
|
|
165
207
|
username: session.username,
|
|
166
208
|
};
|
|
167
209
|
}
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface BffModuleOptions {
|
|
|
16
16
|
};
|
|
17
17
|
publicUrl: string;
|
|
18
18
|
callbackPath?: string;
|
|
19
|
+
permissionResolver?: (realmRoles: string[], clientRoles: string[]) => string[] | Promise<string[]>;
|
|
19
20
|
}
|
|
20
21
|
export interface SessionData {
|
|
21
22
|
accessToken: string;
|
|
@@ -26,6 +27,7 @@ export interface SessionData {
|
|
|
26
27
|
email: string;
|
|
27
28
|
displayName: string;
|
|
28
29
|
roles: string[];
|
|
30
|
+
permissions: string[];
|
|
29
31
|
username?: string;
|
|
30
32
|
}
|
|
31
33
|
export declare const BFF_OPTIONS: unique symbol;
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quanticjs/auth-web-bff",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.9.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",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc -p tsconfig.json",
|
|
9
|
+
"test": "jest --passWithNoTests",
|
|
9
10
|
"clean": "rm -rf dist"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
|
-
"@quanticjs/core": "^5.
|
|
13
|
+
"@quanticjs/core": "^5.9.0"
|
|
13
14
|
},
|
|
14
15
|
"peerDependencies": {
|
|
15
16
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|