keycloak-express-middleware 4.5.1 β 6.0.1
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 +118 -14
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -379,6 +379,113 @@ app.listen(PORT, () => {
|
|
|
379
379
|
});
|
|
380
380
|
```
|
|
381
381
|
---
|
|
382
|
+
|
|
383
|
+
## Handling Unauthorized Access (401/403) Gracefully
|
|
384
|
+
|
|
385
|
+
When a user tries to access a protected resource without the proper roles, the `keycloak-express-middleware` may respond with a plain `401` or `403` message containing `access_denied`.
|
|
386
|
+
While technically correct, this behavior results in a **blank browser page** showing only that message.
|
|
387
|
+
To improve user experience, you can easily intercept these responses and display a custom HTML page or redirect users
|
|
388
|
+
elsewhere using the [`responseinterceptor`](https://www.npmjs.com/package/responseinterceptor) middleware.
|
|
389
|
+
This allows developers to present a more user-friendly "Access Denied" page or redirect unauthorized users to
|
|
390
|
+
a login or error page.
|
|
391
|
+
|
|
392
|
+
> π Note: In a secure application, users should normally not reach protected routes without authentication.
|
|
393
|
+
> However, for simplicity or flexibility during development, this interception approach can be convenient.
|
|
394
|
+
|
|
395
|
+
### Example 1 β Custom Access Denied Page
|
|
396
|
+
|
|
397
|
+
```js
|
|
398
|
+
import responseinterceptor from 'responseinterceptor';
|
|
399
|
+
import keycloakMiddleware from 'keycloak-express-middleware';
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
function tmpInterceptor(req, respond) {
|
|
403
|
+
// Handling Unauthorized Access (401/403) Gracefully
|
|
404
|
+
respond(200, '<h1>Access Denied</h1><p>You are not authorized to view this page.</p>');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// if protectMiddleware('role') return a 403 with keycloak blank page then
|
|
408
|
+
// interceptByStatusCode Handle Unauthorized Access (401/403) Gracefully
|
|
409
|
+
app.get(
|
|
410
|
+
'/test403',
|
|
411
|
+
responseinterceptor.interceptByStatusCode(403, tmpInterceptor),
|
|
412
|
+
keycloakMiddleware.protectMiddleware('role'),
|
|
413
|
+
(req, res) => {
|
|
414
|
+
res.render('welcome');
|
|
415
|
+
}
|
|
416
|
+
);
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Example 2 β Redirect to a Dedicated Page (Dynamic Redirect)
|
|
420
|
+
|
|
421
|
+
```js
|
|
422
|
+
function tmpInterceptorDinamic(req, respond) {
|
|
423
|
+
//your log
|
|
424
|
+
switch (req.path) {
|
|
425
|
+
case '/':
|
|
426
|
+
respond('/access-denied');
|
|
427
|
+
break
|
|
428
|
+
case '/help':
|
|
429
|
+
respond('/access-denied-help');
|
|
430
|
+
break
|
|
431
|
+
default:
|
|
432
|
+
respond('/access-denied-default');
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
app.get('/access-denied', (req, res) => {
|
|
437
|
+
res.render('access-denied');
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
app.get('/access-denied', (req, res) => {
|
|
441
|
+
res.render('access-denied-help');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
app.get('/access-denied', (req, res) => {
|
|
445
|
+
res.render('access-denied-default');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// if protectMiddleware('role') return a 403 with keycloak blank page then
|
|
449
|
+
// interceptByStatusCode Handle Unauthorized Access (401/403) Gracefully
|
|
450
|
+
// by user redirection with tmpInterceptorDinamic logic
|
|
451
|
+
app.get(
|
|
452
|
+
'/test403redirectDynamic',
|
|
453
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, tmpInterceptorDinamic),
|
|
454
|
+
keycloakMiddleware.protectMiddleware('none'),
|
|
455
|
+
(req, res) => {
|
|
456
|
+
res.render('welcome');
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Example 3 β Static Redirect to a Route
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
// if protectMiddleware('role') return a 403 with keycloak blank page then
|
|
465
|
+
// interceptByStatusCode Handle Unauthorized Access (401/403) Gracefully
|
|
466
|
+
// by user redirection
|
|
467
|
+
app.get(
|
|
468
|
+
'/test403redirectStatic',
|
|
469
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, '/access-denied'),
|
|
470
|
+
keycloakMiddleware.protectMiddleware('none'),
|
|
471
|
+
(req, res) => {
|
|
472
|
+
res.render('welcome');
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Summary
|
|
478
|
+
|
|
479
|
+
| Scenario | Middleware Used | Action |
|
|
480
|
+
|-----------|------------------|---------|
|
|
481
|
+
| Replace response content | `interceptByStatusCode()` | Renders a custom message or template |
|
|
482
|
+
| Redirect dynamically | `interceptByStatusCodeRedirectTo()` + callback | Redirects to a route computed in code |
|
|
483
|
+
| Redirect statically | `interceptByStatusCodeRedirectTo()` + string | Redirects to a predefined route |
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
|
|
382
489
|
## π§© Configuration
|
|
383
490
|
In your Express application:
|
|
384
491
|
```js
|
|
@@ -918,15 +1025,13 @@ app.get('/logout', (req, res) => {
|
|
|
918
1025
|
|
|
919
1026
|
---
|
|
920
1027
|
|
|
921
|
-
### `redirectToUserAccountConsole(
|
|
1028
|
+
### `redirectToUserAccountConsole(res)`
|
|
922
1029
|
`redirectToUserAccountConsole` Function is not a middleware, but a **classic synchronous function** that redirect
|
|
923
1030
|
the users to keycloak Admin console where they can view and manage their personal profile and account settings.
|
|
924
1031
|
|
|
925
|
-
|
|
926
1032
|
**` -- @parameters -- `**
|
|
927
|
-
- **req:** `[required]` Express `Request` object
|
|
928
1033
|
- **res:** `[required]` Express `Response` object
|
|
929
|
-
|
|
1034
|
+
|
|
930
1035
|
|
|
931
1036
|
|
|
932
1037
|
--- Design suggestions ---
|
|
@@ -937,28 +1042,27 @@ This way, the app page remains open, and the user can easily return to it after
|
|
|
937
1042
|
Example:
|
|
938
1043
|
Suppose that https://auth.example.com/user/account is the endpoint used by the redirectToUserAccountConsole function
|
|
939
1044
|
hereβs a possible example:
|
|
1045
|
+
β
Usage example:
|
|
940
1046
|
```html
|
|
941
|
-
<a href="https://auth.example.com/user/account" target="_blank">
|
|
1047
|
+
<a href="https://auth.example.com/user/account/console" target="_blank">
|
|
942
1048
|
Manage my profile
|
|
943
1049
|
</a>
|
|
944
1050
|
```
|
|
945
1051
|
|
|
946
|
-
|
|
947
|
-
The user opens the account console in a new tab, uses it, and then manually returns to your app.
|
|
948
|
-
β
Simple, no special configuration required.
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
β
Usage example:
|
|
952
1052
|
```js
|
|
953
1053
|
|
|
954
|
-
app.get('/
|
|
1054
|
+
app.get('/user/account/console', (req, res) => {
|
|
955
1055
|
// Any custom logic before logout
|
|
956
|
-
|
|
957
|
-
keycloakAdapter.logout(req, res, "http://localhost:3001/home");
|
|
1056
|
+
keycloakAdapter.redirectToUserAccountConsole(res);
|
|
958
1057
|
});
|
|
959
1058
|
|
|
960
1059
|
```
|
|
961
1060
|
|
|
1061
|
+
The user opens the account console in a new tab, uses it, and then manually returns to your app.
|
|
1062
|
+
β
Simple, no special configuration required.
|
|
1063
|
+
|
|
1064
|
+
---
|
|
1065
|
+
|
|
962
1066
|
--- Requirements ---
|
|
963
1067
|
- The user must be authenticated with Keycloak and have a valid token in `req.kauth.grant`.
|
|
964
1068
|
- The URL specified in `redirectTo` must be present in the `Valid Redirect URIs` in the Keycloak client.
|
package/index.js
CHANGED
|
@@ -1274,7 +1274,7 @@ class keycloakExpressMiddleware {
|
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
1276
|
|
|
1277
|
-
redirectToUserAccountConsole(
|
|
1277
|
+
redirectToUserAccountConsole(res){
|
|
1278
1278
|
let redirectUrL=`${this.authServerUrl}/realms/${this.realmName}/account/`
|
|
1279
1279
|
res.redirect(redirectUrL);
|
|
1280
1280
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keycloak-express-middleware",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
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": {
|