@thunderid/express 1.11.0 → 1.12.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ThunderIDExpressClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDExpressClient.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAEnB,iCAAiC,EACjC,6BAA6B,EAE7B,OAAO,EACP,aAAa,EACb,IAAI,EAEL,MAAM,iBAAiB,CAAC;AACzB,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAGpD,cAAM,sBAAsB,CAAC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9G,OAAO,CAAC,cAAc,CAAkC;;IAMlC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhF,IAAW,aAAa,IAAI,mBAAmB,GAAG,SAAS,CAE1D;IAED;;;;OAIG;IACI,oBAAoB,IAAI,MAAM;IAOxB,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAMhF;;;;;;;;;;;;;OAaG;IACI,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,6BAA6B;IAWpE,gCAAgC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7F,MAAM,CAC1B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,EAC3B,YAAY,CAAC,EAAE,iCAAiC,GAC/C,OAAO,CAAC,aAAa,CAAC;IAuDH,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGhE;AAED,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"ThunderIDExpressClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDExpressClient.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAEnB,iCAAiC,EACjC,6BAA6B,EAE7B,OAAO,EACP,aAAa,EACb,IAAI,EAEL,MAAM,iBAAiB,CAAC;AACzB,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAGpD,cAAM,sBAAsB,CAAC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9G,OAAO,CAAC,cAAc,CAAkC;;IAMlC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAKhF,IAAW,aAAa,IAAI,mBAAmB,GAAG,SAAS,CAE1D;IAED;;;;OAIG;IACI,oBAAoB,IAAI,MAAM;IAOxB,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAMhF;;;;;;;;;;;;;OAaG;IACI,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,6BAA6B;IAWpE,gCAAgC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7F,MAAM,CAC1B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,EAC3B,YAAY,CAAC,EAAE,iCAAiC,GAC/C,OAAO,CAAC,aAAa,CAAC;IA+DH,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGhE;AAED,eAAe,sBAAsB,CAAC"}
@@ -114,7 +114,12 @@ var ThunderIDExpressClient = class extends __thunderid_node.ThunderIDNodeClient
114
114
  });
115
115
  res.redirect(url);
116
116
  };
117
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
117
+ const ouId = req.query["ouId"];
118
+ const resolvedSignInConfig = typeof ouId === "string" && ouId !== "" && !req.query["code"] ? {
119
+ ...signInConfig,
120
+ organizationUnitId: ouId
121
+ } : signInConfig;
122
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], resolvedSignInConfig);
118
123
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
119
124
  return {
120
125
  accessToken: "",
@@ -459,6 +464,55 @@ const handleBackchannelLogout = () => {
459
464
  };
460
465
  var backchannelLogout_default = handleBackchannelLogout;
461
466
 
467
+ //#endregion
468
+ //#region src/middleware/requireAccess.ts
469
+ /**
470
+ * Returns Express middleware that asks the server's AuthZEN policy decision point whether the request
471
+ * may proceed, and answers `403` on a denial. The application maps the request to a subject, an action
472
+ * and a resource. Requires `thunderID()` to be mounted first.
473
+ *
474
+ * ```ts
475
+ * app.get(
476
+ * '/bookings/:id',
477
+ * protect(),
478
+ * requireAccess(async (req) => ({
479
+ * subject: {type: 'user', id: (await req.thunderIDAuth.getUserFromRequest(req))?.sub},
480
+ * resource: {type: 'https://api.example.com/booking', id: req.params.id},
481
+ * action: {name: 'booking:read'},
482
+ * })),
483
+ * handler,
484
+ * );
485
+ * ```
486
+ */
487
+ const requireAccess = (toRequest, options = {}) => {
488
+ return async (req, res, next) => {
489
+ const client = req.thunderIDAuth;
490
+ if (!client) {
491
+ __thunderid_node.logger.error("thunderID() middleware must be mounted before requireAccess()");
492
+ res.status(500).end();
493
+ return;
494
+ }
495
+ let decision;
496
+ try {
497
+ decision = await client.evaluateAccess(await toRequest(req), { secret: options.secret });
498
+ } catch (error) {
499
+ if (options.onError) options.onError(req, res, error);
500
+ else {
501
+ __thunderid_node.logger.error("Access evaluation failed.");
502
+ res.status(500).end();
503
+ }
504
+ return;
505
+ }
506
+ if (decision?.decision === true) {
507
+ next();
508
+ return;
509
+ }
510
+ if (options.onDenied) options.onDenied(req, res, decision);
511
+ else res.status(403).end();
512
+ };
513
+ };
514
+ var requireAccess_default = requireAccess;
515
+
462
516
  //#endregion
463
517
  exports.CookieConfig = CookieConfig_default;
464
518
  exports.ThunderIDExpressClient = ThunderIDExpressClient_default;
@@ -467,6 +521,7 @@ exports.handleFlow = flow_default;
467
521
  exports.handleSignIn = handleSignIn;
468
522
  exports.handleSignOut = handleSignOut;
469
523
  exports.protect = protect_default;
524
+ exports.requireAccess = requireAccess_default;
470
525
  exports.thunderID = thunderID;
471
526
  Object.keys(__thunderid_node).forEach(function (k) {
472
527
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
@@ -90,7 +90,12 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
90
90
  });
91
91
  res.redirect(url);
92
92
  };
93
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
93
+ const ouId = req.query["ouId"];
94
+ const resolvedSignInConfig = typeof ouId === "string" && ouId !== "" && !req.query["code"] ? {
95
+ ...signInConfig,
96
+ organizationUnitId: ouId
97
+ } : signInConfig;
98
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], resolvedSignInConfig);
94
99
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
95
100
  return {
96
101
  accessToken: "",
@@ -436,4 +441,53 @@ const handleBackchannelLogout = () => {
436
441
  var backchannelLogout_default = handleBackchannelLogout;
437
442
 
438
443
  //#endregion
439
- export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
444
+ //#region src/middleware/requireAccess.ts
445
+ /**
446
+ * Returns Express middleware that asks the server's AuthZEN policy decision point whether the request
447
+ * may proceed, and answers `403` on a denial. The application maps the request to a subject, an action
448
+ * and a resource. Requires `thunderID()` to be mounted first.
449
+ *
450
+ * ```ts
451
+ * app.get(
452
+ * '/bookings/:id',
453
+ * protect(),
454
+ * requireAccess(async (req) => ({
455
+ * subject: {type: 'user', id: (await req.thunderIDAuth.getUserFromRequest(req))?.sub},
456
+ * resource: {type: 'https://api.example.com/booking', id: req.params.id},
457
+ * action: {name: 'booking:read'},
458
+ * })),
459
+ * handler,
460
+ * );
461
+ * ```
462
+ */
463
+ const requireAccess = (toRequest, options = {}) => {
464
+ return async (req, res, next) => {
465
+ const client = req.thunderIDAuth;
466
+ if (!client) {
467
+ logger.error("thunderID() middleware must be mounted before requireAccess()");
468
+ res.status(500).end();
469
+ return;
470
+ }
471
+ let decision;
472
+ try {
473
+ decision = await client.evaluateAccess(await toRequest(req), { secret: options.secret });
474
+ } catch (error) {
475
+ if (options.onError) options.onError(req, res, error);
476
+ else {
477
+ logger.error("Access evaluation failed.");
478
+ res.status(500).end();
479
+ }
480
+ return;
481
+ }
482
+ if (decision?.decision === true) {
483
+ next();
484
+ return;
485
+ }
486
+ if (options.onDenied) options.onDenied(req, res, decision);
487
+ else res.status(403).end();
488
+ };
489
+ };
490
+ var requireAccess_default = requireAccess;
491
+
492
+ //#endregion
493
+ export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, requireAccess_default as requireAccess, thunderID };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { thunderID, handleSignIn, handleSignOut } from './middleware/authenticat
3
3
  export { default as protect } from './middleware/protect';
4
4
  export { default as handleFlow } from './middleware/flow';
5
5
  export { default as handleBackchannelLogout } from './middleware/backchannelLogout';
6
+ export { default as requireAccess } from './middleware/requireAccess';
7
+ export type { RequireAccessOptions } from './middleware/requireAccess';
6
8
  export type { ExpressClientConfig, ThunderIDExpressConfig, StrictExpressClientConfig } from './models/config';
7
9
  export { default as CookieConfig } from './constants/CookieConfig';
8
10
  export * from '@thunderid/node';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,OAAO,IAAI,sBAAsB,EAAC,MAAM,0BAA0B,CAAC;AAG3E,OAAO,EAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAC,OAAO,IAAI,OAAO,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,OAAO,IAAI,UAAU,EAAC,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAC,OAAO,IAAI,uBAAuB,EAAC,MAAM,gCAAgC,CAAC;AAGlF,YAAY,EAAC,mBAAmB,EAAE,sBAAsB,EAAE,yBAAyB,EAAC,MAAM,iBAAiB,CAAC;AAK5G,OAAO,EAAC,OAAO,IAAI,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAIjE,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,OAAO,IAAI,sBAAsB,EAAC,MAAM,0BAA0B,CAAC;AAG3E,OAAO,EAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAC,OAAO,IAAI,OAAO,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,OAAO,IAAI,UAAU,EAAC,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAC,OAAO,IAAI,uBAAuB,EAAC,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAC,OAAO,IAAI,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACpE,YAAY,EAAC,oBAAoB,EAAC,MAAM,4BAA4B,CAAC;AAGrE,YAAY,EAAC,mBAAmB,EAAE,sBAAsB,EAAE,yBAAyB,EAAC,MAAM,iBAAiB,CAAC;AAK5G,OAAO,EAAC,OAAO,IAAI,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAIjE,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -90,7 +90,12 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
90
90
  });
91
91
  res.redirect(url);
92
92
  };
93
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
93
+ const ouId = req.query["ouId"];
94
+ const resolvedSignInConfig = typeof ouId === "string" && ouId !== "" && !req.query["code"] ? {
95
+ ...signInConfig,
96
+ organizationUnitId: ouId
97
+ } : signInConfig;
98
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], resolvedSignInConfig);
94
99
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
95
100
  return {
96
101
  accessToken: "",
@@ -436,4 +441,53 @@ const handleBackchannelLogout = () => {
436
441
  var backchannelLogout_default = handleBackchannelLogout;
437
442
 
438
443
  //#endregion
439
- export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
444
+ //#region src/middleware/requireAccess.ts
445
+ /**
446
+ * Returns Express middleware that asks the server's AuthZEN policy decision point whether the request
447
+ * may proceed, and answers `403` on a denial. The application maps the request to a subject, an action
448
+ * and a resource. Requires `thunderID()` to be mounted first.
449
+ *
450
+ * ```ts
451
+ * app.get(
452
+ * '/bookings/:id',
453
+ * protect(),
454
+ * requireAccess(async (req) => ({
455
+ * subject: {type: 'user', id: (await req.thunderIDAuth.getUserFromRequest(req))?.sub},
456
+ * resource: {type: 'https://api.example.com/booking', id: req.params.id},
457
+ * action: {name: 'booking:read'},
458
+ * })),
459
+ * handler,
460
+ * );
461
+ * ```
462
+ */
463
+ const requireAccess = (toRequest, options = {}) => {
464
+ return async (req, res, next) => {
465
+ const client = req.thunderIDAuth;
466
+ if (!client) {
467
+ logger.error("thunderID() middleware must be mounted before requireAccess()");
468
+ res.status(500).end();
469
+ return;
470
+ }
471
+ let decision;
472
+ try {
473
+ decision = await client.evaluateAccess(await toRequest(req), { secret: options.secret });
474
+ } catch (error) {
475
+ if (options.onError) options.onError(req, res, error);
476
+ else {
477
+ logger.error("Access evaluation failed.");
478
+ res.status(500).end();
479
+ }
480
+ return;
481
+ }
482
+ if (decision?.decision === true) {
483
+ next();
484
+ return;
485
+ }
486
+ if (options.onDenied) options.onDenied(req, res, decision);
487
+ else res.status(403).end();
488
+ };
489
+ };
490
+ var requireAccess_default = requireAccess;
491
+
492
+ //#endregion
493
+ export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, requireAccess_default as requireAccess, thunderID };
@@ -0,0 +1,41 @@
1
+ import { AccessEvaluationRequest, AccessEvaluationResponse } from '@thunderid/node';
2
+ import express from 'express';
3
+ /**
4
+ * Options for {@link requireAccess}.
5
+ */
6
+ export interface RequireAccessOptions {
7
+ /**
8
+ * Called when the decision is a denial. Defaults to answering `403`.
9
+ */
10
+ onDenied?: (req: express.Request, res: express.Response, decision: AccessEvaluationResponse) => void;
11
+ /**
12
+ * Called when the evaluation itself fails (transport or a 4xx/5xx answer). Defaults to answering
13
+ * `500`. The request is never let through on a failure.
14
+ */
15
+ onError?: (req: express.Request, res: express.Response, error: unknown) => void;
16
+ /**
17
+ * The server-level secret, when it is not set as `accessEvaluation.secret` in the configuration.
18
+ */
19
+ secret?: string;
20
+ }
21
+ /**
22
+ * Returns Express middleware that asks the server's AuthZEN policy decision point whether the request
23
+ * may proceed, and answers `403` on a denial. The application maps the request to a subject, an action
24
+ * and a resource. Requires `thunderID()` to be mounted first.
25
+ *
26
+ * ```ts
27
+ * app.get(
28
+ * '/bookings/:id',
29
+ * protect(),
30
+ * requireAccess(async (req) => ({
31
+ * subject: {type: 'user', id: (await req.thunderIDAuth.getUserFromRequest(req))?.sub},
32
+ * resource: {type: 'https://api.example.com/booking', id: req.params.id},
33
+ * action: {name: 'booking:read'},
34
+ * })),
35
+ * handler,
36
+ * );
37
+ * ```
38
+ */
39
+ declare const requireAccess: (toRequest: (req: express.Request) => AccessEvaluationRequest | Promise<AccessEvaluationRequest>, options?: RequireAccessOptions) => ((req: express.Request, res: express.Response, next: express.NextFunction) => Promise<void>);
40
+ export default requireAccess;
41
+ //# sourceMappingURL=requireAccess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requireAccess.d.ts","sourceRoot":"","sources":["../../src/middleware/requireAccess.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,uBAAuB,EAAE,wBAAwB,EAAmB,MAAM,iBAAiB,CAAC;AACpG,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACrG;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAChF;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,aAAa,GACjB,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,EAC/F,UAAS,oBAAyB,KACjC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAoC7F,CAAC;AAEF,eAAe,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thunderid/express",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Express.js SDK for ThunderID",
5
5
  "keywords": [
6
6
  "thunderid",
@@ -47,7 +47,7 @@
47
47
  "vitest": "4.1.11"
48
48
  },
49
49
  "dependencies": {
50
- "@thunderid/node": "^1.11.0",
50
+ "@thunderid/node": "^1.12.0",
51
51
  "uuid": "11.1.1"
52
52
  },
53
53
  "peerDependencies": {