keycloak-express-middleware 6.0.1 → 6.0.3
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/README.md +193 -178
- package/index.js +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ application scope. By moving to this new ***one instance = one client*** model,
|
|
|
26
26
|
- isolating middleware logic per client without risking cross-contamination of sessions or grants,
|
|
27
27
|
- simplifying multitenancy or micro-service architectures where different parts of an app authenticate against different Keycloak clients.
|
|
28
28
|
|
|
29
|
-
**To summarize:** the new version enables multi-client usage within the same app by turning the middleware into configurable,
|
|
29
|
+
**To summarize:** the new version (4.0.0+) enables multi-client usage within the same app by turning the middleware into configurable,
|
|
30
30
|
independent object instances — something that the earlier version did not support.
|
|
31
31
|
|
|
32
32
|
### 🏗️ Migration Guide: From Old to New Version
|
|
@@ -55,15 +55,15 @@ await keycloackAdapter.configure(app,{
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
// Example of protection with keycloackAdapter.protectMiddleware middleware
|
|
58
|
-
//
|
|
58
|
+
// with a static client role validation string
|
|
59
59
|
// Access is allowed only for authenticated admin users
|
|
60
60
|
app.get('/privateStaticClientRole', keycloackAdapter.protectMiddleware("admin"), (req, res) => {
|
|
61
61
|
// "Your Custom Code"
|
|
62
|
-
res.send("
|
|
62
|
+
res.send("You are an admin.");
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
🆕 New Version (Object-Oriented Design)
|
|
66
|
+
🆕 New Version (Object-Oriented Design) - Version 4.0.0+
|
|
67
67
|
|
|
68
68
|
```js
|
|
69
69
|
// NEW VERSION STARTING FROM 4.0.0
|
|
@@ -108,12 +108,12 @@ const keycloakB = new keycloackAdapter(app,{
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
// Example protected routes
|
|
111
|
-
app.get('/clientA/secure', keycloakA.
|
|
112
|
-
res.send('Protected route for Client A');
|
|
111
|
+
app.get('/clientA/secure', keycloakA.protectMiddleware(), (req, res) => {
|
|
112
|
+
res.send('Protected route for Client A');
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
app.get('/clientB/secure', keycloakB.
|
|
116
|
-
res.send('Protected route for Client B');
|
|
115
|
+
app.get('/clientB/secure', keycloakB.protectMiddleware(), (req, res) => {
|
|
116
|
+
res.send('Protected route for Client B');
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
app.listen(3000, () => console.log('Server running on port 3000'));
|
|
@@ -148,18 +148,20 @@ the Keycloak Admin Console → clients (left sidebar) → choose your client →
|
|
|
148
148
|
## 📄 Usage Example
|
|
149
149
|
```js
|
|
150
150
|
const express = require('express');
|
|
151
|
-
|
|
151
|
+
// CommonJS - Default import
|
|
152
|
+
const keycloackAdapter = require('keycloak-express-middleware');
|
|
153
|
+
|
|
154
|
+
// ES6 - Named import (recommended for clarity)
|
|
155
|
+
// import { keycloackAdapter } from 'keycloak-express-middleware';
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const keycloackAdapter = require('keycloak-express-middleware'); // import keycloackAdapter from 'keycloak-express-middleware';
|
|
156
|
-
*/
|
|
157
|
+
// ES6 - Default import
|
|
158
|
+
// import keycloackAdapter from 'keycloak-express-middleware';
|
|
157
159
|
|
|
158
160
|
const app = express();
|
|
159
161
|
|
|
160
162
|
|
|
161
163
|
// Configure and Initialize Keycloak adapter
|
|
162
|
-
|
|
164
|
+
const keycloakInstance = new keycloackAdapter(app,{
|
|
163
165
|
"realm": "Realm-Project",
|
|
164
166
|
"auth-server-url": "https://YourKeycloakUrl:30040/",
|
|
165
167
|
"ssl-required": "external",
|
|
@@ -175,28 +177,6 @@ keycloackAdapter= new keycloackAdapterClass(app,{
|
|
|
175
177
|
}
|
|
176
178
|
});
|
|
177
179
|
|
|
178
|
-
/*
|
|
179
|
-
OLD STYLE UP TO VERSION 3.0.9
|
|
180
|
-
// Configure and Initialize Keycloak adapter
|
|
181
|
-
await keycloackAdapter.configure(app,{
|
|
182
|
-
"realm": "Realm-Project",
|
|
183
|
-
"auth-server-url": "https://YourKeycloakUrl:30040/",
|
|
184
|
-
"ssl-required": "external",
|
|
185
|
-
"resource": "keycloackclientName",
|
|
186
|
-
"credentials": {
|
|
187
|
-
"secret": "aaaaaaaaaa"
|
|
188
|
-
},
|
|
189
|
-
"confidential-port": 0
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
session:{
|
|
193
|
-
secret: 'mySecretForSession',
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
*/
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
180
|
// -------------- Public route -----------------------
|
|
201
181
|
app.get('/', (req, res) => {
|
|
202
182
|
res.send('Public route: no authentication required');
|
|
@@ -205,100 +185,100 @@ app.get('/', (req, res) => {
|
|
|
205
185
|
|
|
206
186
|
/* ############## Protected routes (any authenticated user) ########### */
|
|
207
187
|
|
|
208
|
-
// Example of login with
|
|
188
|
+
// Example of login with keycloakInstance.login function
|
|
209
189
|
// After login redirect to "/home"
|
|
210
190
|
app.get('/signIn', (req, res) => {
|
|
211
191
|
console.log("Your Custom Code");
|
|
212
|
-
|
|
192
|
+
keycloakInstance.login(req,res,"/home")
|
|
213
193
|
|
|
214
194
|
});
|
|
215
195
|
|
|
216
|
-
// Example of login with
|
|
196
|
+
// Example of login with keycloakInstance.loginMiddleware middleware
|
|
217
197
|
// After login redirect to "/home"
|
|
218
|
-
app.get('/loginMiddleware',
|
|
198
|
+
app.get('/loginMiddleware', keycloakInstance.loginMiddleware("/home") ,(req, res) => {
|
|
219
199
|
// Response handled by middleware, this section will never be reached.
|
|
220
200
|
});
|
|
221
201
|
|
|
222
|
-
// Example of logout with
|
|
202
|
+
// Example of logout with keycloakInstance.logout function
|
|
223
203
|
// After login redirect to "http://localhost:3001/home"
|
|
224
204
|
app.get('/logout', (req, res) => {
|
|
225
205
|
console.log("Your Custom Code");
|
|
226
|
-
|
|
206
|
+
keycloakInstance.logout(req,res,"http://localhost:3001/home");
|
|
227
207
|
});
|
|
228
208
|
|
|
229
|
-
// Example of logout with
|
|
209
|
+
// Example of logout with keycloakInstance.logoutMiddleware middleware
|
|
230
210
|
// After login redirect to "http://localhost:3001/home"
|
|
231
|
-
app.get('/logoutMiddle',
|
|
211
|
+
app.get('/logoutMiddle', keycloakInstance.logoutMiddleware("http://redirctUrl"), (req, res) => {
|
|
232
212
|
// Response handled by middleware, this section will never be reached.
|
|
233
213
|
});
|
|
234
214
|
|
|
235
215
|
|
|
236
|
-
// Example of protection with
|
|
216
|
+
// Example of protection with keycloakInstance.protectMiddleware middleware
|
|
237
217
|
// Access is allowed only for authenticated users
|
|
238
|
-
app.get('/private',
|
|
218
|
+
app.get('/private', keycloakInstance.protectMiddleware(), (req, res) => {
|
|
239
219
|
console.log("Your Custom Code");
|
|
240
220
|
console.log( req.session);
|
|
241
221
|
res.redirect('/auth');
|
|
242
222
|
});
|
|
243
223
|
|
|
244
|
-
// Example of protection with
|
|
245
|
-
//
|
|
224
|
+
// Example of protection with keycloakInstance.protectMiddleware middleware
|
|
225
|
+
// with a static client role validation string
|
|
246
226
|
// Access is allowed only for authenticated admin users
|
|
247
|
-
app.get('/privateStaticClientRole',
|
|
227
|
+
app.get('/privateStaticClientRole', keycloakInstance.protectMiddleware("admin"), (req, res) => {
|
|
248
228
|
// "Your Custom Code"
|
|
249
|
-
res.send("
|
|
229
|
+
res.send("You are an admin.");
|
|
250
230
|
});
|
|
251
231
|
|
|
252
|
-
// Example of protection with
|
|
253
|
-
//
|
|
232
|
+
// Example of protection with keycloakInstance.protectMiddleware middleware
|
|
233
|
+
// with a static realm role validation string
|
|
254
234
|
// Access is allowed only for authenticated realm admin users
|
|
255
|
-
app.get('/privateStaticRealmRole',
|
|
235
|
+
app.get('/privateStaticRealmRole', keycloakInstance.protectMiddleware("realm:admin"), (req, res) => {
|
|
256
236
|
// "Your Custom Code"
|
|
257
|
-
res.send("
|
|
237
|
+
res.send("You are a realm admin.");
|
|
258
238
|
});
|
|
259
239
|
|
|
260
|
-
// Example of protection with
|
|
261
|
-
//
|
|
240
|
+
// Example of protection with keycloakInstance.protectMiddleware middleware
|
|
241
|
+
// with a static other client role validation string
|
|
262
242
|
// Access is allowed only for authenticated otherClient admin users
|
|
263
|
-
app.get('/privateStaticRealmRole',
|
|
243
|
+
app.get('/privateStaticRealmRole', keycloakInstance.protectMiddleware("otherClient:admin"), (req, res) => {
|
|
264
244
|
// "Your Custom Code"
|
|
265
|
-
res.send("
|
|
245
|
+
res.send("You are an admin of otherClient.");
|
|
266
246
|
});
|
|
267
247
|
|
|
268
|
-
// Example of protection with
|
|
269
|
-
//
|
|
248
|
+
// Example of protection with keycloakInstance.protectMiddleware middleware
|
|
249
|
+
// with a control function tmpFunction
|
|
270
250
|
// Access is allowed only for authenticated admin users
|
|
271
251
|
let tmpFunction=function (token, req) {
|
|
272
252
|
return token.hasRole('admin');
|
|
273
253
|
}
|
|
274
|
-
app.get('/isAdmin',
|
|
254
|
+
app.get('/isAdmin', keycloakInstance.protectMiddleware(tmpFunction), (req, res) => {
|
|
275
255
|
// "Your Custom Code"
|
|
276
|
-
res.send("
|
|
256
|
+
res.send("You are an admin (verified by tmpFunction).");
|
|
277
257
|
});
|
|
278
258
|
|
|
279
259
|
|
|
280
|
-
// Example of protection with
|
|
281
|
-
//
|
|
260
|
+
// Example of protection with keycloakInstance.customProtectMiddleware middleware
|
|
261
|
+
// with a control function tmpFunctionString
|
|
282
262
|
// Access is allowed only for authenticated users with role defined by tmpFunctionString
|
|
283
263
|
let tmpFunctionString=function (req,res) {
|
|
284
264
|
let id=req.params.id
|
|
285
265
|
// Control String by url param Id
|
|
286
266
|
return (`${id}`);
|
|
287
267
|
}
|
|
288
|
-
app.get('/:id/isAdmin',
|
|
268
|
+
app.get('/:id/isAdmin', keycloakInstance.customProtectMiddleware(tmpFunctionString), (req, res) => {
|
|
289
269
|
// "Your Custom Code"
|
|
290
|
-
res.send("
|
|
270
|
+
res.send("You are an admin (verified by tmpFunctionString).");
|
|
291
271
|
});
|
|
292
272
|
|
|
293
273
|
|
|
294
|
-
// Example of protection with
|
|
274
|
+
// Example of protection with keycloakInstance.encodeTokenRole middleware
|
|
295
275
|
// Encode the token and add it to req.encodedTokenRole
|
|
296
276
|
// Use req.encodedTokenRole.hasRole("role") to check whether the token has that role or not
|
|
297
|
-
app.get('/encodeToken',
|
|
277
|
+
app.get('/encodeToken', keycloakInstance.encodeTokenRole(), (req, res) => {
|
|
298
278
|
if(req.encodedTokenRole.hasRole('realm:admin'))
|
|
299
|
-
res.send("
|
|
279
|
+
res.send("You are a realm admin");
|
|
300
280
|
else
|
|
301
|
-
res.send("
|
|
281
|
+
res.send("You are not a realm admin");
|
|
302
282
|
|
|
303
283
|
});
|
|
304
284
|
|
|
@@ -309,18 +289,18 @@ app.get('/encodeToken', keycloackAdapter.encodeTokenRole(), (req, res) => {
|
|
|
309
289
|
// #####################################################################################
|
|
310
290
|
|
|
311
291
|
|
|
312
|
-
// Example of protection with
|
|
313
|
-
//
|
|
292
|
+
// Example of protection with keycloakInstance.enforcerMiddleware middleware
|
|
293
|
+
// with a static control string
|
|
314
294
|
// Access is allowed only for users with 'ui-admin-resource' permission defined
|
|
315
295
|
// in keycloak
|
|
316
|
-
app.get('/adminResource',
|
|
296
|
+
app.get('/adminResource', keycloakInstance.enforcerMiddleware('ui-admin-resource'), (req, res) => {
|
|
317
297
|
// If this section is reached, the user has the required privileges;
|
|
318
298
|
// otherwise, the middleware responds with a 403 Access Denied.
|
|
319
299
|
res.send('You are an authorized ui-admin-resource User');
|
|
320
300
|
});
|
|
321
301
|
|
|
322
|
-
// Example of protection with
|
|
323
|
-
//
|
|
302
|
+
// Example of protection with keycloakInstance.enforcerMiddleware middleware
|
|
303
|
+
// with a control function tmpFunctionEnforceValidation
|
|
324
304
|
// Access is allowed only for users with 'ui-admin-resource' or
|
|
325
305
|
// ui-viewer-resource permission defined in keycloak
|
|
326
306
|
let tmpFunctionEnforceValidation=function (token,req,callback) {
|
|
@@ -335,7 +315,7 @@ let tmpFunctionEnforceValidation=function (token,req,callback) {
|
|
|
335
315
|
}));
|
|
336
316
|
}));
|
|
337
317
|
}
|
|
338
|
-
app.get('/adminOrViewerResorce',
|
|
318
|
+
app.get('/adminOrViewerResorce', keycloakInstance.enforcerMiddleware(tmpFunctionEnforceValidation), (req, res) => {
|
|
339
319
|
// If this section is reached, the user has the required privileges
|
|
340
320
|
// driven by tmpFunctionEnforceValidation; otherwise, the middleware responds
|
|
341
321
|
// with a 403 Access Denied.
|
|
@@ -343,27 +323,27 @@ app.get('/adminOrViewerResorce', keycloackAdapter.enforcerMiddleware(tmpFunction
|
|
|
343
323
|
});
|
|
344
324
|
|
|
345
325
|
|
|
346
|
-
// Example of protection with
|
|
347
|
-
//
|
|
326
|
+
// Example of protection with keycloakInstance.customEnforcerMiddleware middleware
|
|
327
|
+
// with a control function tmpFunctionEnforce that define the control string
|
|
348
328
|
// Access is allowed only for users with a url params ':permission' permission defined
|
|
349
329
|
// in keycloak
|
|
350
330
|
let tmpFunctionEnforce=function (req,res) {
|
|
351
331
|
// Permission that depends on a URL parameter.
|
|
352
332
|
return(req.params.permission);
|
|
353
333
|
}
|
|
354
|
-
app.get('/urlParameterPermission/:permission',
|
|
334
|
+
app.get('/urlParameterPermission/:permission', keycloakInstance.customEnforcerMiddleware(tmpFunctionEnforce), (req, res) => {
|
|
355
335
|
res.send(`You are an authorized User with ${req.params.permission} permission`);
|
|
356
336
|
});
|
|
357
337
|
|
|
358
|
-
// Example of protection with
|
|
359
|
-
// Encode the token permission and add it to req.
|
|
360
|
-
// Use req.
|
|
338
|
+
// Example of protection with keycloakInstance.encodeTokenPermission middleware
|
|
339
|
+
// Encode the token permission and add it to req.encodedTokenPermission
|
|
340
|
+
// Use req.encodedTokenPermission.hasPermission("permission") to check whether
|
|
361
341
|
// the token has that permission or not
|
|
362
|
-
app.get('/encodeTokenPermission',
|
|
342
|
+
app.get('/encodeTokenPermission', keycloakInstance.encodeTokenPermission(), (req, res) => {
|
|
363
343
|
// Check permission using token.hasPermission, which performs the verification
|
|
364
344
|
// and responds with a callback that returns true if the permission is valid,
|
|
365
345
|
// and false otherwise.
|
|
366
|
-
req.
|
|
346
|
+
req.encodedTokenPermission.hasPermission('ui-admin-resource', function(permission){
|
|
367
347
|
if(permission)
|
|
368
348
|
res.send('You are an authorized User by ui-admin-resource permission');
|
|
369
349
|
else res.status(403).send("access Denied");
|
|
@@ -395,24 +375,60 @@ a login or error page.
|
|
|
395
375
|
### Example 1 — Custom Access Denied Page
|
|
396
376
|
|
|
397
377
|
```js
|
|
398
|
-
|
|
399
|
-
|
|
378
|
+
const responseinterceptor = require('responseinterceptor');
|
|
379
|
+
const keycloackAdapter = require('keycloak-express-middleware');
|
|
380
|
+
|
|
381
|
+
const app = express();
|
|
382
|
+
const keycloakInstance = new keycloackAdapter(app, keyCloakConfig, keyCloakOptions);
|
|
400
383
|
|
|
401
384
|
|
|
402
385
|
function tmpInterceptor(req, respond) {
|
|
403
|
-
|
|
404
|
-
|
|
386
|
+
/**
|
|
387
|
+
* Gracefully handles unauthorized access (401/403).
|
|
388
|
+
*
|
|
389
|
+
* This interceptor is executed when a protected route returns
|
|
390
|
+
* a forbidden status code. Instead of letting the browser show
|
|
391
|
+
* a blank page with the text "access_denied", it renders a
|
|
392
|
+
* user-friendly "access-denied" view.
|
|
393
|
+
*
|
|
394
|
+
* How it works:
|
|
395
|
+
* - Uses `req.app.render()` to generate HTML from the EJS template
|
|
396
|
+
* without sending it directly to the client.
|
|
397
|
+
* - If rendering succeeds, the generated HTML is passed to `respond()`,
|
|
398
|
+
* which replaces the original 403 response body with our custom page.
|
|
399
|
+
* - If rendering fails, a fallback HTML message is provided.
|
|
400
|
+
*/
|
|
401
|
+
|
|
402
|
+
req.app.render('access-denied', {}, (err, html) => {
|
|
403
|
+
if (!err) {
|
|
404
|
+
// Successfully rendered the template → return the generated HTML
|
|
405
|
+
respond(200, html);
|
|
406
|
+
} else {
|
|
407
|
+
// Fallback: template error → send a simple HTML message instead
|
|
408
|
+
respond(
|
|
409
|
+
200,
|
|
410
|
+
'<h1>Access Denied</h1><p>You are not authorized to view this page.</p>'
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
405
414
|
}
|
|
406
415
|
|
|
407
|
-
//
|
|
408
|
-
//
|
|
416
|
+
// Route example showing how to gracefully handle 403 responses produced by Keycloak
|
|
417
|
+
//
|
|
418
|
+
// If `protectMiddleware('role')` denies access, Keycloak normally returns a
|
|
419
|
+
// plain "access_denied" message on a blank page.
|
|
420
|
+
//
|
|
421
|
+
// By placing `interceptByStatusCode(403, tmpInterceptor)` BEFORE the protectMiddleware,
|
|
422
|
+
// we intercept the 403 response generated by Keycloak and replace it with a
|
|
423
|
+
// custom HTML page (e.g. an EJS template) instead of the default blank output.
|
|
424
|
+
|
|
409
425
|
app.get(
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
426
|
+
'/test403',
|
|
427
|
+
responseinterceptor.interceptByStatusCode(403, tmpInterceptor),
|
|
428
|
+
keycloakInstance.protectMiddleware('role'),
|
|
429
|
+
(req, res) => {
|
|
430
|
+
res.render('welcome');
|
|
431
|
+
}
|
|
416
432
|
);
|
|
417
433
|
```
|
|
418
434
|
|
|
@@ -420,58 +436,91 @@ app.get(
|
|
|
420
436
|
|
|
421
437
|
```js
|
|
422
438
|
function tmpInterceptorDinamic(req, respond) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
439
|
+
/**
|
|
440
|
+
* Dynamic Redirect Interceptor
|
|
441
|
+
*
|
|
442
|
+
* This function decides dynamically where the user should be redirected
|
|
443
|
+
* after a 403 Unauthorized response is intercepted.
|
|
444
|
+
*
|
|
445
|
+
* - `req` → the original Express request
|
|
446
|
+
* - `respond` → helper function that receives the final redirect route
|
|
447
|
+
*
|
|
448
|
+
* Based on the current request path, we redirect the user to different
|
|
449
|
+
* "access denied" pages.
|
|
450
|
+
*/
|
|
451
|
+
switch (req.path) {
|
|
452
|
+
case '/':
|
|
453
|
+
respond('/access-denied');
|
|
454
|
+
break;
|
|
455
|
+
|
|
456
|
+
case '/help':
|
|
457
|
+
respond('/access-denied-help');
|
|
458
|
+
break;
|
|
459
|
+
|
|
460
|
+
default:
|
|
461
|
+
respond('/access-denied-default');
|
|
462
|
+
}
|
|
434
463
|
}
|
|
435
464
|
|
|
465
|
+
// Dedicated "Access Denied" pages
|
|
436
466
|
app.get('/access-denied', (req, res) => {
|
|
437
|
-
|
|
467
|
+
res.render('access-denied');
|
|
438
468
|
});
|
|
439
469
|
|
|
440
|
-
app.get('/access-denied', (req, res) => {
|
|
470
|
+
app.get('/access-denied-help', (req, res) => {
|
|
441
471
|
res.render('access-denied-help');
|
|
442
472
|
});
|
|
443
473
|
|
|
444
|
-
app.get('/access-denied', (req, res) => {
|
|
474
|
+
app.get('/access-denied-default', (req, res) => {
|
|
445
475
|
res.render('access-denied-default');
|
|
446
476
|
});
|
|
447
477
|
|
|
448
|
-
//
|
|
449
|
-
//
|
|
450
|
-
//
|
|
478
|
+
// -----------------------------------------------------------------------
|
|
479
|
+
// Example: dynamic redirection after Keycloak denies access (403)
|
|
480
|
+
// -----------------------------------------------------------------------
|
|
481
|
+
//
|
|
482
|
+
// If protectMiddleware('none') triggers a 403 (Keycloak default behavior),
|
|
483
|
+
// the `interceptByStatusCodeRedirectTo` middleware intercepts Keycloak’s
|
|
484
|
+
// blank "access_denied" page and replaces it with a redirect determined
|
|
485
|
+
// by tmpInterceptorDinamic().
|
|
486
|
+
//
|
|
487
|
+
// This allows:
|
|
488
|
+
//
|
|
489
|
+
// ✓ No more blank browser page with "access_denied"
|
|
490
|
+
// ✓ User-friendly redirection to custom EJS pages
|
|
491
|
+
// ✓ Dynamic routing logic based on the original request
|
|
492
|
+
//
|
|
451
493
|
app.get(
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
494
|
+
'/test403redirectDynamic',
|
|
495
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, tmpInterceptorDinamic),
|
|
496
|
+
keycloakInstance.protectMiddleware('none'),
|
|
497
|
+
(req, res) => {
|
|
498
|
+
res.render('welcome');
|
|
499
|
+
}
|
|
458
500
|
);
|
|
459
501
|
```
|
|
460
502
|
|
|
461
503
|
### Example 3 — Static Redirect to a Route
|
|
462
504
|
|
|
463
505
|
```js
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
506
|
+
// If protectMiddleware('role') triggers a 403 (forbidden) and Keycloak returns
|
|
507
|
+
// the default blank 403 page, we use interceptByStatusCodeRedirectTo()
|
|
508
|
+
// to gracefully handle the unauthorized response.
|
|
509
|
+
//
|
|
510
|
+
// When a 403 is detected, the interceptor forces an HTTP redirect to the
|
|
511
|
+
// custom '/access-denied' route, allowing us to show a friendly UI instead
|
|
512
|
+
// of Keycloak’s default error page.
|
|
513
|
+
//
|
|
514
|
+
// This ensures consistent UX and keeps request flow under our control.
|
|
467
515
|
app.get(
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
516
|
+
'/test403redirectStatic',
|
|
517
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, '/access-denied'),
|
|
518
|
+
keycloakInstance.protectMiddleware('none'),
|
|
519
|
+
(req, res) => {
|
|
520
|
+
res.render('welcome');
|
|
521
|
+
}
|
|
474
522
|
);
|
|
523
|
+
|
|
475
524
|
```
|
|
476
525
|
|
|
477
526
|
### Summary
|
|
@@ -489,10 +538,10 @@ app.get(
|
|
|
489
538
|
## 🧩 Configuration
|
|
490
539
|
In your Express application:
|
|
491
540
|
```js
|
|
492
|
-
|
|
541
|
+
const keycloackAdapter = require('keycloak-express-middleware');
|
|
493
542
|
|
|
494
543
|
// Configure and Initialize Keycloak adapter
|
|
495
|
-
|
|
544
|
+
const keycloakInstance = new keycloackAdapter(app,{
|
|
496
545
|
"realm": "Realm-Project",
|
|
497
546
|
"auth-server-url": "https://YourKeycloakUrl:30040/",
|
|
498
547
|
"ssl-required": "external",
|
|
@@ -508,9 +557,9 @@ keycloackAdapter=new keycloakAdapterClass(app,{
|
|
|
508
557
|
}
|
|
509
558
|
})
|
|
510
559
|
```
|
|
511
|
-
`keycloak-express-middleware constructor`
|
|
512
|
-
adapter
|
|
513
|
-
|
|
560
|
+
`keycloak-express-middleware constructor` instantiates and configures the Keycloak
|
|
561
|
+
adapter for an Express application. It must be called at app startup, before defining any protected routes.
|
|
562
|
+
The constructor is synchronous and returns the configured instance immediately.
|
|
514
563
|
|
|
515
564
|
**` -- @parameters -- `**
|
|
516
565
|
- **app**: `[required]` Express application instance (e.g., const app = express();) or express router (e.g., var router = express.Router();)
|
|
@@ -536,58 +585,24 @@ It is an async function and returns a promise
|
|
|
536
585
|
- **idpHint:** to suggest an identity provider to Keycloak during login
|
|
537
586
|
- **cookies:** to enable cookie handling
|
|
538
587
|
- **realmUrl:** to override the realm URL
|
|
539
|
-
---
|
|
540
|
-
## 🔧 Available Middlewares
|
|
541
|
-
### `underKeycloakProtection(callback) - deprecated - `
|
|
542
|
-
**@deprecated Method**. Use the `configure` Method with **`await keycloakAdapter.configure({..})`**, then define your resources as you normally would in Express:
|
|
543
|
-
```js
|
|
544
|
-
await keycloakAdapter.configure(config_Parameters);
|
|
545
|
-
|
|
546
|
-
// Define all your routes here
|
|
547
|
-
|
|
548
|
-
app.get('/my-route', handler);
|
|
549
|
-
```
|
|
550
|
-
Alternatively, if you prefer to define your resources inside a container after configuration, you can use the `then` syntax:
|
|
551
|
-
```js
|
|
552
|
-
keycloakAdapter.configure(configParameters).then(() => {
|
|
553
|
-
// Define all your routes to protect here
|
|
554
|
-
app.get('/my-route', handler);
|
|
555
|
-
});
|
|
556
|
-
```
|
|
557
|
-
This Method is deprecated and will be removed in future versions. It must be called **after** Keycloak has been configured with `configure()`.
|
|
558
|
-
The routes declared inside the provided callback will be protected and will have access to authentication/authorization features managed by Keycloak.
|
|
559
588
|
|
|
560
|
-
|
|
589
|
+
### Import Alternatives
|
|
561
590
|
|
|
562
|
-
|
|
563
|
-
- **{Function} callback:** `[required]` A function that defines all routes to be protected. It must contain exclusively routes requiring authentication.
|
|
591
|
+
While the examples use CommonJS (`require`), the library also supports ES6 module syntax:
|
|
564
592
|
|
|
565
|
-
✅ Usage example:
|
|
566
593
|
```js
|
|
567
|
-
//
|
|
568
|
-
|
|
569
|
-
res.send('Public content');
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// Section of routes protected by Keycloak
|
|
573
|
-
keycloakAdapter.underKeycloakProtection(() => {
|
|
594
|
+
// CommonJS (Default)
|
|
595
|
+
const keycloackAdapter = require('keycloak-express-middleware');
|
|
574
596
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
// Route protected by authentication
|
|
579
|
-
app.get('/confidential', keycloakAdapter.protectMiddleware(), (req, res) => {
|
|
580
|
-
res.send('Confidential content visible only to authenticated users');
|
|
581
|
-
});
|
|
597
|
+
// ES6 - Named import (recommended for clarity)
|
|
598
|
+
import { keycloackAdapter } from 'keycloak-express-middleware';
|
|
582
599
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
// This response will never be sent because the middleware handles the
|
|
586
|
-
// request directly
|
|
587
|
-
});
|
|
588
|
-
});
|
|
600
|
+
// ES6 - Default import
|
|
601
|
+
import keycloackAdapter from 'keycloak-express-middleware';
|
|
589
602
|
```
|
|
603
|
+
|
|
590
604
|
---
|
|
605
|
+
## 🔧 Available Middlewares
|
|
591
606
|
### `protectMiddleware([conditions])`
|
|
592
607
|
Middleware to protect Express routes based on authentication and, optionally, authorization via Keycloak roles.
|
|
593
608
|
Allows restricting access to a resource only to authenticated users or to those possessing specific roles in the realm or in a Keycloak client.
|
|
@@ -963,7 +978,7 @@ After successful login, the user is redirected to the URL specified in the `redi
|
|
|
963
978
|
- **redirectTo:** `[required]` URL to redirect the user to after successful login
|
|
964
979
|
|
|
965
980
|
--- Behavior ---
|
|
966
|
-
1. Attempts to protect the request using `
|
|
981
|
+
1. Attempts to protect the request using `protectMiddleware()`.
|
|
967
982
|
2. If the user **is authenticated**, it performs `res.redirect(redirectTo)`.
|
|
968
983
|
3. If **not authenticated**, Keycloak automatically handles redirection to the login page.
|
|
969
984
|
|
package/index.js
CHANGED
|
@@ -838,7 +838,7 @@ class keycloakExpressMiddleware {
|
|
|
838
838
|
* --- Aggiunte a `req` ---
|
|
839
839
|
* Dopo l'applicazione del middleware, `req` contiene:
|
|
840
840
|
*
|
|
841
|
-
* @property {Object} req.
|
|
841
|
+
* @property {Object} req.encodedTokenPermission
|
|
842
842
|
* Un oggetto che espone il metodo:
|
|
843
843
|
*
|
|
844
844
|
* - `hasPermission(permission: string, callback: function(boolean))`
|
|
@@ -851,7 +851,7 @@ class keycloakExpressMiddleware {
|
|
|
851
851
|
* app.get('/encodeTokenPermission',
|
|
852
852
|
* keycloakAdapter.encodeTokenPermission(),
|
|
853
853
|
* (req, res) => {
|
|
854
|
-
* req.
|
|
854
|
+
* req.encodedTokenPermission.hasPermission('ui-admin-resource', function(perm) {
|
|
855
855
|
* if (perm)
|
|
856
856
|
* res.send('You are an authorized admin User by function permission parameters');
|
|
857
857
|
* else
|
|
@@ -907,7 +907,7 @@ class keycloakExpressMiddleware {
|
|
|
907
907
|
encodeTokenPermission(){
|
|
908
908
|
const self = this;
|
|
909
909
|
return(function (req,res,next){
|
|
910
|
-
req.
|
|
910
|
+
req.encodedTokenPermission={
|
|
911
911
|
"hasPermission":function(permission,callback){
|
|
912
912
|
self.#encodeTokenPermissionHandler(permission,req,res,callback);
|
|
913
913
|
}
|
|
@@ -1286,6 +1286,8 @@ class keycloakExpressMiddleware {
|
|
|
1286
1286
|
|
|
1287
1287
|
|
|
1288
1288
|
module.exports = keycloakExpressMiddleware;
|
|
1289
|
+
module.exports.keycloackAdapter = keycloakExpressMiddleware; // Named export for backward compatibility and ES6 support
|
|
1290
|
+
module.exports.default = keycloakExpressMiddleware; // Default export for ES6 modules
|
|
1289
1291
|
|
|
1290
1292
|
/*
|
|
1291
1293
|
<table><tbody>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keycloak-express-middleware",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.3",
|
|
4
4
|
"description": "Adapter API to integrate Node.js (Express) applications with Keycloak. Provides middleware for authentication, authorization, token validation, and route protection via OpenID Connect.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|