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.
Files changed (3) hide show
  1. package/README.md +118 -14
  2. package/index.js +1 -1
  3. 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(req, res)`
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
- - **redirectTo:** `[required]` URL to redirect the user to after successful logout
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('/logout', (req, res) => {
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(req,res){
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": "4.5.1",
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": {