@thunderid/express 1.1.0 → 1.2.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/dist/ThunderIDExpressClient.d.ts.map +1 -1
- package/dist/cjs/index.cjs +82 -1
- package/dist/edge/index.js +82 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -3
- package/dist/middleware/backchannelLogout.d.ts +21 -0
- package/dist/middleware/backchannelLogout.d.ts.map +1 -0
- package/dist/models/config.d.ts +10 -1
- package/dist/models/config.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -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;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;
|
|
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"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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. */
|
|
@@ -94,7 +96,7 @@ var ThunderIDExpressClient = class extends __thunderid_node.ThunderIDNodeClient
|
|
|
94
96
|
res.redirect(url);
|
|
95
97
|
if (typeof next === "function") next();
|
|
96
98
|
};
|
|
97
|
-
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["
|
|
99
|
+
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
|
|
98
100
|
if (authResponse.accessToken || authResponse.idToken) return authResponse;
|
|
99
101
|
return {
|
|
100
102
|
accessToken: "",
|
|
@@ -356,9 +358,88 @@ const handleFlow = () => {
|
|
|
356
358
|
};
|
|
357
359
|
var flow_default = handleFlow;
|
|
358
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
|
+
|
|
359
439
|
//#endregion
|
|
360
440
|
exports.CookieConfig = CookieConfig_default;
|
|
361
441
|
exports.ThunderIDExpressClient = ThunderIDExpressClient_default;
|
|
442
|
+
exports.handleBackchannelLogout = backchannelLogout_default;
|
|
362
443
|
exports.handleFlow = flow_default;
|
|
363
444
|
exports.handleSignIn = handleSignIn;
|
|
364
445
|
exports.handleSignOut = handleSignOut;
|
package/dist/edge/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
|
|
|
@@ -71,7 +72,7 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
|
|
|
71
72
|
res.redirect(url);
|
|
72
73
|
if (typeof next === "function") next();
|
|
73
74
|
};
|
|
74
|
-
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["
|
|
75
|
+
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
|
|
75
76
|
if (authResponse.accessToken || authResponse.idToken) return authResponse;
|
|
76
77
|
return {
|
|
77
78
|
accessToken: "",
|
|
@@ -334,4 +335,82 @@ const handleFlow = () => {
|
|
|
334
335
|
var flow_default = handleFlow;
|
|
335
336
|
|
|
336
337
|
//#endregion
|
|
337
|
-
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
|
@@ -71,7 +72,7 @@ var ThunderIDExpressClient = class extends ThunderIDNodeClient {
|
|
|
71
72
|
res.redirect(url);
|
|
72
73
|
if (typeof next === "function") next();
|
|
73
74
|
};
|
|
74
|
-
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["
|
|
75
|
+
const authResponse = await super.signIn(authRedirectCallback, userId, req.query["code"], req.query["state"], signInConfig);
|
|
75
76
|
if (authResponse.accessToken || authResponse.idToken) return authResponse;
|
|
76
77
|
return {
|
|
77
78
|
accessToken: "",
|
|
@@ -334,4 +335,82 @@ const handleFlow = () => {
|
|
|
334
335
|
var flow_default = handleFlow;
|
|
335
336
|
|
|
336
337
|
//#endregion
|
|
337
|
-
|
|
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"}
|
package/dist/models/config.d.ts
CHANGED
|
@@ -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;
|
|
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.1
|
|
3
|
+
"version": "1.2.1",
|
|
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.
|
|
47
|
+
"vitest": "4.1.11"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@thunderid/node": "^1.1
|
|
50
|
+
"@thunderid/node": "^1.2.1",
|
|
51
51
|
"uuid": "11.1.1"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|