keycloak-express-middleware 4.5.1 β 6.0.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/README.md +98 -14
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -378,6 +378,93 @@ app.listen(PORT, () => {
|
|
|
378
378
|
console.log(`Server running at http://localhost:${PORT}`);
|
|
379
379
|
});
|
|
380
380
|
```
|
|
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
|
+
function tmpInterceptor(req, respond) {
|
|
402
|
+
respond(200, '<h1>Access Denied</h1><p>You are not authorized to view this page.</p>');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
app.get(
|
|
406
|
+
'/test403',
|
|
407
|
+
responseinterceptor.interceptByStatusCode(403, tmpInterceptor),
|
|
408
|
+
keycloakMiddleware.protectMiddleware('role'),
|
|
409
|
+
(req, res) => {
|
|
410
|
+
res.render('welcome');
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Example 2 β Redirect to a Dedicated Page (Dynamic Redirect)
|
|
416
|
+
|
|
417
|
+
```js
|
|
418
|
+
function tmpInterceptorDinamic(req, respond) {
|
|
419
|
+
//your log
|
|
420
|
+
switch (req.path) {
|
|
421
|
+
case '/':
|
|
422
|
+
respond('/access-denied');
|
|
423
|
+
break
|
|
424
|
+
case '/help':
|
|
425
|
+
respond('/access-denied-help');
|
|
426
|
+
break
|
|
427
|
+
default:
|
|
428
|
+
respond('/access-denied-default');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
app.get('/access-denied', (req, res) => {
|
|
433
|
+
res.render('access-denied');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
app.get(
|
|
437
|
+
'/test403redirectDynamic',
|
|
438
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, tmpInterceptorDinamic),
|
|
439
|
+
keycloakMiddleware.protectMiddleware('none'),
|
|
440
|
+
(req, res) => {
|
|
441
|
+
res.render('welcome');
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Example 3 β Static Redirect to a Route
|
|
447
|
+
|
|
448
|
+
```js
|
|
449
|
+
app.get(
|
|
450
|
+
'/test403redirectStatic',
|
|
451
|
+
responseinterceptor.interceptByStatusCodeRedirectTo(403, '/access-denied'),
|
|
452
|
+
keycloakMiddleware.protectMiddleware('none'),
|
|
453
|
+
(req, res) => {
|
|
454
|
+
res.render('welcome');
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Summary
|
|
460
|
+
|
|
461
|
+
| Scenario | Middleware Used | Action |
|
|
462
|
+
|-----------|------------------|---------|
|
|
463
|
+
| Replace response content | `interceptByStatusCode()` | Renders a custom message or template |
|
|
464
|
+
| Redirect dynamically | `interceptByStatusCodeRedirectTo()` + callback | Redirects to a route computed in code |
|
|
465
|
+
| Redirect statically | `interceptByStatusCodeRedirectTo()` + string | Redirects to a predefined route |
|
|
466
|
+
|
|
467
|
+
|
|
381
468
|
---
|
|
382
469
|
## π§© Configuration
|
|
383
470
|
In your Express application:
|
|
@@ -918,15 +1005,13 @@ app.get('/logout', (req, res) => {
|
|
|
918
1005
|
|
|
919
1006
|
---
|
|
920
1007
|
|
|
921
|
-
### `redirectToUserAccountConsole(
|
|
1008
|
+
### `redirectToUserAccountConsole(res)`
|
|
922
1009
|
`redirectToUserAccountConsole` Function is not a middleware, but a **classic synchronous function** that redirect
|
|
923
1010
|
the users to keycloak Admin console where they can view and manage their personal profile and account settings.
|
|
924
1011
|
|
|
925
|
-
|
|
926
1012
|
**` -- @parameters -- `**
|
|
927
|
-
- **req:** `[required]` Express `Request` object
|
|
928
1013
|
- **res:** `[required]` Express `Response` object
|
|
929
|
-
|
|
1014
|
+
|
|
930
1015
|
|
|
931
1016
|
|
|
932
1017
|
--- Design suggestions ---
|
|
@@ -937,28 +1022,27 @@ This way, the app page remains open, and the user can easily return to it after
|
|
|
937
1022
|
Example:
|
|
938
1023
|
Suppose that https://auth.example.com/user/account is the endpoint used by the redirectToUserAccountConsole function
|
|
939
1024
|
hereβs a possible example:
|
|
1025
|
+
β
Usage example:
|
|
940
1026
|
```html
|
|
941
|
-
<a href="https://auth.example.com/user/account" target="_blank">
|
|
1027
|
+
<a href="https://auth.example.com/user/account/console" target="_blank">
|
|
942
1028
|
Manage my profile
|
|
943
1029
|
</a>
|
|
944
1030
|
```
|
|
945
1031
|
|
|
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
1032
|
```js
|
|
953
1033
|
|
|
954
|
-
app.get('/
|
|
1034
|
+
app.get('/user/account/console', (req, res) => {
|
|
955
1035
|
// Any custom logic before logout
|
|
956
|
-
|
|
957
|
-
keycloakAdapter.logout(req, res, "http://localhost:3001/home");
|
|
1036
|
+
keycloakAdapter.redirectToUserAccountConsole(res);
|
|
958
1037
|
});
|
|
959
1038
|
|
|
960
1039
|
```
|
|
961
1040
|
|
|
1041
|
+
The user opens the account console in a new tab, uses it, and then manually returns to your app.
|
|
1042
|
+
β
Simple, no special configuration required.
|
|
1043
|
+
|
|
1044
|
+
---
|
|
1045
|
+
|
|
962
1046
|
--- Requirements ---
|
|
963
1047
|
- The user must be authenticated with Keycloak and have a valid token in `req.kauth.grant`.
|
|
964
1048
|
- 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.0",
|
|
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": {
|