@thunderid/express 1.0.7 → 1.2.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.
@@ -13,6 +13,7 @@ declare class ThunderIDExpressClient<T extends ExpressClientConfig = ExpressClie
13
13
  */
14
14
  getSessionCookieName(): string;
15
15
  getUserFromRequest(req: express.Request): Promise<User | undefined>;
16
+ updateUserCredentialsFromRequest(req: express.Request, payload: Record<string, string>): Promise<void>;
16
17
  signIn(req: express.Request, res: express.Response, next: express.NextFunction, signInConfig?: Record<string, string | boolean>): Promise<TokenResponse>;
17
18
  signOut(userId?: string): Promise<string>;
18
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ThunderIDExpressClient.d.ts","sourceRoot":"","sources":["../src/ThunderIDExpressClient.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,mBAAmB,EAEnB,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;IAK1D,MAAM,CAC1B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,GAAG,EAAE,OAAO,CAAC,QAAQ,EACrB,IAAI,EAAE,OAAO,CAAC,YAAY,EAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAC9C,OAAO,CAAC,aAAa,CAAC;IAwDH,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,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;IAMnE,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,EAAE,OAAO,CAAC,YAAY,EAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAC9C,OAAO,CAAC,aAAa,CAAC;IAuDH,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGhE;AAED,eAAe,sBAAsB,CAAC"}
@@ -25,6 +25,8 @@ let __thunderid_node = require("@thunderid/node");
25
25
  __thunderid_node = __toESM(__thunderid_node);
26
26
  let uuid = require("uuid");
27
27
  uuid = __toESM(uuid);
28
+ let express = require("express");
29
+ express = __toESM(express);
28
30
 
29
31
  //#region src/constants/CookieConfig.ts
30
32
  /** Default cookie configuration values. */
@@ -73,6 +75,10 @@ var ThunderIDExpressClient = class extends __thunderid_node.ThunderIDNodeClient
73
75
  const sessionId = req.cookies?.[this.getSessionCookieName()];
74
76
  return this.getUser(sessionId);
75
77
  }
78
+ async updateUserCredentialsFromRequest(req, payload) {
79
+ const sessionId = req.cookies?.[this.getSessionCookieName()];
80
+ return this.updateUserCredentials(payload, sessionId);
81
+ }
76
82
  async signIn(req, res, next, signInConfig) {
77
83
  if (expressUtils_default(req.originalUrl)) return Promise.reject(new __thunderid_node.ThunderIDAuthException("EXPRESS-CLIENT-SI-IV01", "Invalid login request URL", "Login request contains an error query parameter in the URL"));
78
84
  const cookieName = this.getSessionCookieName();
@@ -90,7 +96,7 @@ var ThunderIDExpressClient = class extends __thunderid_node.ThunderIDNodeClient
90
96
  res.redirect(url);
91
97
  if (typeof next === "function") next();
92
98
  };
93
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["session_state"], req.query["state"], signInConfig);
99
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
94
100
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
95
101
  return {
96
102
  accessToken: "",
@@ -352,9 +358,88 @@ const handleFlow = () => {
352
358
  };
353
359
  var flow_default = handleFlow;
354
360
 
361
+ //#endregion
362
+ //#region src/middleware/backchannelLogout.ts
363
+ const BODY_LIMIT = "16kb";
364
+ /**
365
+ * Returns an Express route handler for OpenID Connect back-channel logout. ThunderID posts a
366
+ * `logout_token` to it when a session this application took part in ends, and the handler ends
367
+ * the matching local sessions, so the next request that presents one is rejected by `protect()`.
368
+ *
369
+ * - Answers `200` for a valid token, including one that names no session held here.
370
+ * - Answers `400` for a request or token that is not valid, and `500` when the logout could not be
371
+ * recorded, which makes the server try again.
372
+ *
373
+ * Must be used after `thunderID()` middleware so that `req.thunderIDAuth` is set. Mount it at the
374
+ * path registered as the application's back-channel logout URI. It needs no body parser and no
375
+ * session cookie: the token's signature is the authentication.
376
+ *
377
+ * ```ts
378
+ * app.post('/backchannel-logout', handleBackchannelLogout());
379
+ * ```
380
+ */
381
+ const handleBackchannelLogout = () => {
382
+ const parseForm = express.default.urlencoded({
383
+ extended: false,
384
+ limit: BODY_LIMIT
385
+ });
386
+ const handle = async (req, res) => {
387
+ const client = req.thunderIDAuth;
388
+ if (!client) {
389
+ __thunderid_node.logger.error("thunderID() middleware must be mounted before handleBackchannelLogout()");
390
+ res.status(500).end();
391
+ return;
392
+ }
393
+ const logoutToken = req.body?.[__thunderid_node.BackchannelLogoutConstants.TOKEN_PARAM];
394
+ if (typeof logoutToken !== "string" || logoutToken.length === 0) {
395
+ res.status(400).end();
396
+ return;
397
+ }
398
+ try {
399
+ const result = await client.handleBackchannelLogout(logoutToken);
400
+ try {
401
+ await client.expressConfig?.onBackchannelLogout?.(result);
402
+ } catch {
403
+ __thunderid_node.logger.error("The onBackchannelLogout hook failed.");
404
+ }
405
+ res.status(200).end();
406
+ } catch (e) {
407
+ if (e instanceof __thunderid_node.InvalidLogoutTokenError) {
408
+ __thunderid_node.logger.warn(`Rejected a back-channel logout token (${e.code}).`);
409
+ res.status(400).end();
410
+ return;
411
+ }
412
+ __thunderid_node.logger.error("Could not complete a back-channel logout.");
413
+ res.status(500).end();
414
+ }
415
+ };
416
+ return (req, res) => {
417
+ res.set("Cache-Control", "no-store");
418
+ if (req.method !== "POST") {
419
+ res.set("Allow", "POST").status(405).end();
420
+ return;
421
+ }
422
+ if (!req.is("application/x-www-form-urlencoded")) {
423
+ res.status(400).end();
424
+ return;
425
+ }
426
+ parseForm(req, res, (parseError) => {
427
+ if (parseError) {
428
+ res.status(400).end();
429
+ return;
430
+ }
431
+ handle(req, res).catch(() => {
432
+ res.status(500).end();
433
+ });
434
+ });
435
+ };
436
+ };
437
+ var backchannelLogout_default = handleBackchannelLogout;
438
+
355
439
  //#endregion
356
440
  exports.CookieConfig = CookieConfig_default;
357
441
  exports.ThunderIDExpressClient = ThunderIDExpressClient_default;
442
+ exports.handleBackchannelLogout = backchannelLogout_default;
358
443
  exports.handleFlow = flow_default;
359
444
  exports.handleSignIn = handleSignIn;
360
445
  exports.handleSignOut = handleSignOut;
@@ -1,5 +1,6 @@
1
- import { CookieConfig, ThunderIDAuthException, ThunderIDNodeClient, ThunderIDRuntimeError, executeEmbeddedSignInFlow, logger, resolveResourceEndpoint } from "@thunderid/node";
1
+ import { BackchannelLogoutConstants, CookieConfig, InvalidLogoutTokenError, ThunderIDAuthException, ThunderIDNodeClient, ThunderIDRuntimeError, executeEmbeddedSignInFlow, logger, resolveResourceEndpoint } from "@thunderid/node";
2
2
  import { v4 } from "uuid";
3
+ import express from "express";
3
4
 
4
5
  export * from "@thunderid/node"
5
6
 
@@ -50,6 +51,10 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
50
51
  const sessionId = req.cookies?.[this.getSessionCookieName()];
51
52
  return this.getUser(sessionId);
52
53
  }
54
+ async updateUserCredentialsFromRequest(req, payload) {
55
+ const sessionId = req.cookies?.[this.getSessionCookieName()];
56
+ return this.updateUserCredentials(payload, sessionId);
57
+ }
53
58
  async signIn(req, res, next, signInConfig) {
54
59
  if (expressUtils_default(req.originalUrl)) return Promise.reject(new ThunderIDAuthException("EXPRESS-CLIENT-SI-IV01", "Invalid login request URL", "Login request contains an error query parameter in the URL"));
55
60
  const cookieName = this.getSessionCookieName();
@@ -67,7 +72,7 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
67
72
  res.redirect(url);
68
73
  if (typeof next === "function") next();
69
74
  };
70
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["session_state"], req.query["state"], signInConfig);
75
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
71
76
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
72
77
  return {
73
78
  accessToken: "",
@@ -330,4 +335,82 @@ const handleFlow = () => {
330
335
  var flow_default = handleFlow;
331
336
 
332
337
  //#endregion
333
- export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
338
+ //#region src/middleware/backchannelLogout.ts
339
+ const BODY_LIMIT = "16kb";
340
+ /**
341
+ * Returns an Express route handler for OpenID Connect back-channel logout. ThunderID posts a
342
+ * `logout_token` to it when a session this application took part in ends, and the handler ends
343
+ * the matching local sessions, so the next request that presents one is rejected by `protect()`.
344
+ *
345
+ * - Answers `200` for a valid token, including one that names no session held here.
346
+ * - Answers `400` for a request or token that is not valid, and `500` when the logout could not be
347
+ * recorded, which makes the server try again.
348
+ *
349
+ * Must be used after `thunderID()` middleware so that `req.thunderIDAuth` is set. Mount it at the
350
+ * path registered as the application's back-channel logout URI. It needs no body parser and no
351
+ * session cookie: the token's signature is the authentication.
352
+ *
353
+ * ```ts
354
+ * app.post('/backchannel-logout', handleBackchannelLogout());
355
+ * ```
356
+ */
357
+ const handleBackchannelLogout = () => {
358
+ const parseForm = express.urlencoded({
359
+ extended: false,
360
+ limit: BODY_LIMIT
361
+ });
362
+ const handle = async (req, res) => {
363
+ const client = req.thunderIDAuth;
364
+ if (!client) {
365
+ logger.error("thunderID() middleware must be mounted before handleBackchannelLogout()");
366
+ res.status(500).end();
367
+ return;
368
+ }
369
+ const logoutToken = req.body?.[BackchannelLogoutConstants.TOKEN_PARAM];
370
+ if (typeof logoutToken !== "string" || logoutToken.length === 0) {
371
+ res.status(400).end();
372
+ return;
373
+ }
374
+ try {
375
+ const result = await client.handleBackchannelLogout(logoutToken);
376
+ try {
377
+ await client.expressConfig?.onBackchannelLogout?.(result);
378
+ } catch {
379
+ logger.error("The onBackchannelLogout hook failed.");
380
+ }
381
+ res.status(200).end();
382
+ } catch (e) {
383
+ if (e instanceof InvalidLogoutTokenError) {
384
+ logger.warn(`Rejected a back-channel logout token (${e.code}).`);
385
+ res.status(400).end();
386
+ return;
387
+ }
388
+ logger.error("Could not complete a back-channel logout.");
389
+ res.status(500).end();
390
+ }
391
+ };
392
+ return (req, res) => {
393
+ res.set("Cache-Control", "no-store");
394
+ if (req.method !== "POST") {
395
+ res.set("Allow", "POST").status(405).end();
396
+ return;
397
+ }
398
+ if (!req.is("application/x-www-form-urlencoded")) {
399
+ res.status(400).end();
400
+ return;
401
+ }
402
+ parseForm(req, res, (parseError) => {
403
+ if (parseError) {
404
+ res.status(400).end();
405
+ return;
406
+ }
407
+ handle(req, res).catch(() => {
408
+ res.status(500).end();
409
+ });
410
+ });
411
+ };
412
+ };
413
+ var backchannelLogout_default = handleBackchannelLogout;
414
+
415
+ //#endregion
416
+ export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { default as ThunderIDExpressClient } from './ThunderIDExpressClient';
2
2
  export { thunderID, handleSignIn, handleSignOut } from './middleware/authentication';
3
3
  export { default as protect } from './middleware/protect';
4
4
  export { default as handleFlow } from './middleware/flow';
5
+ export { default as handleBackchannelLogout } from './middleware/backchannelLogout';
5
6
  export type { ExpressClientConfig, ThunderIDExpressConfig, StrictExpressClientConfig } from './models/config';
6
7
  export { default as CookieConfig } from './constants/CookieConfig';
7
8
  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;AAGxD,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;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"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { CookieConfig, ThunderIDAuthException, ThunderIDNodeClient, ThunderIDRuntimeError, executeEmbeddedSignInFlow, logger, resolveResourceEndpoint } from "@thunderid/node";
1
+ import { BackchannelLogoutConstants, CookieConfig, InvalidLogoutTokenError, ThunderIDAuthException, ThunderIDNodeClient, ThunderIDRuntimeError, executeEmbeddedSignInFlow, logger, resolveResourceEndpoint } from "@thunderid/node";
2
2
  import { v4 } from "uuid";
3
+ import express from "express";
3
4
 
4
5
  export * from "@thunderid/node"
5
6
 
@@ -50,6 +51,10 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
50
51
  const sessionId = req.cookies?.[this.getSessionCookieName()];
51
52
  return this.getUser(sessionId);
52
53
  }
54
+ async updateUserCredentialsFromRequest(req, payload) {
55
+ const sessionId = req.cookies?.[this.getSessionCookieName()];
56
+ return this.updateUserCredentials(payload, sessionId);
57
+ }
53
58
  async signIn(req, res, next, signInConfig) {
54
59
  if (expressUtils_default(req.originalUrl)) return Promise.reject(new ThunderIDAuthException("EXPRESS-CLIENT-SI-IV01", "Invalid login request URL", "Login request contains an error query parameter in the URL"));
55
60
  const cookieName = this.getSessionCookieName();
@@ -67,7 +72,7 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
67
72
  res.redirect(url);
68
73
  if (typeof next === "function") next();
69
74
  };
70
- const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["session_state"], req.query["state"], signInConfig);
75
+ const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
71
76
  if (authResponse.accessToken || authResponse.idToken) return authResponse;
72
77
  return {
73
78
  accessToken: "",
@@ -330,4 +335,82 @@ const handleFlow = () => {
330
335
  var flow_default = handleFlow;
331
336
 
332
337
  //#endregion
333
- export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
338
+ //#region src/middleware/backchannelLogout.ts
339
+ const BODY_LIMIT = "16kb";
340
+ /**
341
+ * Returns an Express route handler for OpenID Connect back-channel logout. ThunderID posts a
342
+ * `logout_token` to it when a session this application took part in ends, and the handler ends
343
+ * the matching local sessions, so the next request that presents one is rejected by `protect()`.
344
+ *
345
+ * - Answers `200` for a valid token, including one that names no session held here.
346
+ * - Answers `400` for a request or token that is not valid, and `500` when the logout could not be
347
+ * recorded, which makes the server try again.
348
+ *
349
+ * Must be used after `thunderID()` middleware so that `req.thunderIDAuth` is set. Mount it at the
350
+ * path registered as the application's back-channel logout URI. It needs no body parser and no
351
+ * session cookie: the token's signature is the authentication.
352
+ *
353
+ * ```ts
354
+ * app.post('/backchannel-logout', handleBackchannelLogout());
355
+ * ```
356
+ */
357
+ const handleBackchannelLogout = () => {
358
+ const parseForm = express.urlencoded({
359
+ extended: false,
360
+ limit: BODY_LIMIT
361
+ });
362
+ const handle = async (req, res) => {
363
+ const client = req.thunderIDAuth;
364
+ if (!client) {
365
+ logger.error("thunderID() middleware must be mounted before handleBackchannelLogout()");
366
+ res.status(500).end();
367
+ return;
368
+ }
369
+ const logoutToken = req.body?.[BackchannelLogoutConstants.TOKEN_PARAM];
370
+ if (typeof logoutToken !== "string" || logoutToken.length === 0) {
371
+ res.status(400).end();
372
+ return;
373
+ }
374
+ try {
375
+ const result = await client.handleBackchannelLogout(logoutToken);
376
+ try {
377
+ await client.expressConfig?.onBackchannelLogout?.(result);
378
+ } catch {
379
+ logger.error("The onBackchannelLogout hook failed.");
380
+ }
381
+ res.status(200).end();
382
+ } catch (e) {
383
+ if (e instanceof InvalidLogoutTokenError) {
384
+ logger.warn(`Rejected a back-channel logout token (${e.code}).`);
385
+ res.status(400).end();
386
+ return;
387
+ }
388
+ logger.error("Could not complete a back-channel logout.");
389
+ res.status(500).end();
390
+ }
391
+ };
392
+ return (req, res) => {
393
+ res.set("Cache-Control", "no-store");
394
+ if (req.method !== "POST") {
395
+ res.set("Allow", "POST").status(405).end();
396
+ return;
397
+ }
398
+ if (!req.is("application/x-www-form-urlencoded")) {
399
+ res.status(400).end();
400
+ return;
401
+ }
402
+ parseForm(req, res, (parseError) => {
403
+ if (parseError) {
404
+ res.status(400).end();
405
+ return;
406
+ }
407
+ handle(req, res).catch(() => {
408
+ res.status(500).end();
409
+ });
410
+ });
411
+ };
412
+ };
413
+ var backchannelLogout_default = handleBackchannelLogout;
414
+
415
+ //#endregion
416
+ export { CookieConfig_default as CookieConfig, ThunderIDExpressClient_default as ThunderIDExpressClient, backchannelLogout_default as handleBackchannelLogout, flow_default as handleFlow, handleSignIn, handleSignOut, protect_default as protect, thunderID };
@@ -0,0 +1,21 @@
1
+ import express from 'express';
2
+ /**
3
+ * Returns an Express route handler for OpenID Connect back-channel logout. ThunderID posts a
4
+ * `logout_token` to it when a session this application took part in ends, and the handler ends
5
+ * the matching local sessions, so the next request that presents one is rejected by `protect()`.
6
+ *
7
+ * - Answers `200` for a valid token, including one that names no session held here.
8
+ * - Answers `400` for a request or token that is not valid, and `500` when the logout could not be
9
+ * recorded, which makes the server try again.
10
+ *
11
+ * Must be used after `thunderID()` middleware so that `req.thunderIDAuth` is set. Mount it at the
12
+ * path registered as the application's back-channel logout URI. It needs no body parser and no
13
+ * session cookie: the token's signature is the authentication.
14
+ *
15
+ * ```ts
16
+ * app.post('/backchannel-logout', handleBackchannelLogout());
17
+ * ```
18
+ */
19
+ declare const handleBackchannelLogout: () => express.RequestHandler;
20
+ export default handleBackchannelLogout;
21
+ //# sourceMappingURL=backchannelLogout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backchannelLogout.d.ts","sourceRoot":"","sources":["../../src/middleware/backchannelLogout.ts"],"names":[],"mappings":"AASA,OAAO,OAAO,MAAM,SAAS,CAAC;AAQ9B;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,uBAAuB,QAAO,OAAO,CAAC,cAmE3C,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ThunderIDNodeConfig, ThunderIDRuntimeError, TokenResponse } from '@thunderid/node';
1
+ import { BackchannelLogoutResult, ThunderIDNodeConfig, ThunderIDRuntimeError, TokenResponse } from '@thunderid/node';
2
2
  import express from 'express';
3
3
  /**
4
4
  * Express-specific configuration fields.
@@ -12,6 +12,15 @@ export interface StrictExpressClientConfig {
12
12
  onError?: (res: express.Response, exception: ThunderIDRuntimeError) => void;
13
13
  /** Called with the response when a protected route is accessed without a valid session. */
14
14
  onUnauthenticated?: (res: express.Response) => void;
15
+ /**
16
+ * Called after a back-channel logout ended this SDK's sessions, so the application can clear
17
+ * what it holds for them. Best effort: a failure is logged and the handler still answers 200,
18
+ * since the sessions are already ended and a retry would find nothing.
19
+ *
20
+ * This SDK mounts the endpoint with `handleBackchannelLogout()` and does not read the
21
+ * `backchannelLogout` configuration key of the SDK specification.
22
+ */
23
+ onBackchannelLogout?: (result: BackchannelLogoutResult) => void | Promise<void>;
15
24
  }
16
25
  /**
17
26
  * Full configuration type for `ThunderIDExpressClient`.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/models/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC1F,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,aAAa,KAAK,IAAI,CAAC;IACzE,uDAAuD;IACvD,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC5C,oEAAoE;IACpE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5E,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG,yBAAyB,CAAC;AAElF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/models/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,uBAAuB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AACnH,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,aAAa,KAAK,IAAI,CAAC;IACzE,uDAAuD;IACvD,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC5C,oEAAoE;IACpE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5E,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;IACpD;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG,yBAAyB,CAAC;AAElF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thunderid/express",
3
- "version": "1.0.7",
3
+ "version": "1.2.0",
4
4
  "description": "Express.js SDK for ThunderID",
5
5
  "keywords": [
6
6
  "thunderid",
@@ -44,10 +44,10 @@
44
44
  "rimraf": "6.1.3",
45
45
  "rolldown": "1.0.0-beta.45",
46
46
  "typescript": "5.9.3",
47
- "vitest": "4.1.10"
47
+ "vitest": "4.1.11"
48
48
  },
49
49
  "dependencies": {
50
- "@thunderid/node": "^1.0.7",
50
+ "@thunderid/node": "^1.2.0",
51
51
  "uuid": "11.1.1"
52
52
  },
53
53
  "peerDependencies": {