@idp.global/sdk 18.1.0 → 18.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.
@@ -57,7 +57,13 @@ export declare const RELYING_PARTY_DEFAULT_COLLECTION_NAMES: {
57
57
  readonly loginAttempts: "IdpRelyingPartyLoginAttempt";
58
58
  };
59
59
  export interface IRelyingPartyModelsOptions {
60
- db: plugins.nosqldb.SmartdataDb;
60
+ /**
61
+ * The database, or a function that answers it once it exists. The function
62
+ * is called on every use of a model — never while the models or
63
+ * `RelyingPartySessions` are constructed — so an application creates both
64
+ * before its database connects and binds the database it connects later.
65
+ */
66
+ db: plugins.nosqldb.SmartdataDb | (() => plugins.nosqldb.SmartdataDb);
61
67
  /**
62
68
  * The persisted collection names. An application that already stores these
63
69
  * documents names its existing collections here.
@@ -74,6 +80,8 @@ export interface IRelyingPartyModels {
74
80
  /**
75
81
  * Binds the two relying-party models to one database. Each call declares its
76
82
  * own model classes, so several databases — and several applications in one
77
- * process — never share a binding.
83
+ * process — never share a binding. A deferred `db` is resolved on every use,
84
+ * the way `@Collection(() => db)` resolves it; a use before it answers a
85
+ * `SmartdataDb` throws `RelyingPartyDatabaseUnavailableError`.
78
86
  */
79
87
  export declare const createRelyingPartyModels: (optionsArg: IRelyingPartyModelsOptions) => IRelyingPartyModels;
@@ -33,6 +33,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
33
33
  return useValue ? value : void 0;
34
34
  };
35
35
  import * as plugins from './plugins.js';
36
+ import { RelyingPartyDatabaseUnavailableError } from './classes.errors.js';
36
37
  /**
37
38
  * One browser session of a relying party. The persisted field names, their
38
39
  * types and the index declarations are the ones nevermind.finance has stored
@@ -232,18 +233,36 @@ export const RELYING_PARTY_DEFAULT_COLLECTION_NAMES = {
232
233
  /**
233
234
  * Binds the two relying-party models to one database. Each call declares its
234
235
  * own model classes, so several databases — and several applications in one
235
- * process — never share a binding.
236
+ * process — never share a binding. A deferred `db` is resolved on every use,
237
+ * the way `@Collection(() => db)` resolves it; a use before it answers a
238
+ * `SmartdataDb` throws `RelyingPartyDatabaseUnavailableError`.
236
239
  */
237
240
  export const createRelyingPartyModels = (optionsArg) => {
238
- const db = optionsArg.db;
239
- if (!(db instanceof plugins.nosqldb.SmartdataDb)) {
240
- throw new Error('createRelyingPartyModels needs a SmartdataDb of @lossless.org/client/nosqldb');
241
+ const dbOption = optionsArg.db;
242
+ if (!(dbOption instanceof plugins.nosqldb.SmartdataDb) && typeof dbOption !== 'function') {
243
+ throw new Error('createRelyingPartyModels needs a SmartdataDb of @lossless.org/client/nosqldb or a function answering one');
241
244
  }
245
+ const resolveDb = () => {
246
+ if (dbOption instanceof plugins.nosqldb.SmartdataDb) {
247
+ return dbOption;
248
+ }
249
+ let resolved;
250
+ try {
251
+ resolved = dbOption();
252
+ }
253
+ catch (error) {
254
+ throw new RelyingPartyDatabaseUnavailableError(error);
255
+ }
256
+ if (!(resolved instanceof plugins.nosqldb.SmartdataDb)) {
257
+ throw new RelyingPartyDatabaseUnavailableError();
258
+ }
259
+ return resolved;
260
+ };
242
261
  const sessions = optionsArg.collectionNames?.sessions ?? RELYING_PARTY_DEFAULT_COLLECTION_NAMES.sessions;
243
262
  const loginAttempts = optionsArg.collectionNames?.loginAttempts
244
263
  ?? RELYING_PARTY_DEFAULT_COLLECTION_NAMES.loginAttempts;
245
264
  let BoundSessionDoc = (() => {
246
- let _classDecorators = [plugins.nosqldb.Collection(() => db, { collectionName: sessions })];
265
+ let _classDecorators = [plugins.nosqldb.Collection(resolveDb, { collectionName: sessions })];
247
266
  let _classDescriptor;
248
267
  let _classExtraInitializers = [];
249
268
  let _classThis;
@@ -261,7 +280,7 @@ export const createRelyingPartyModels = (optionsArg) => {
261
280
  return BoundSessionDoc = _classThis;
262
281
  })();
263
282
  let BoundLoginAttemptDoc = (() => {
264
- let _classDecorators = [plugins.nosqldb.Collection(() => db, { collectionName: loginAttempts })];
283
+ let _classDecorators = [plugins.nosqldb.Collection(resolveDb, { collectionName: loginAttempts })];
265
284
  let _classDescriptor;
266
285
  let _classExtraInitializers = [];
267
286
  let _classThis;
@@ -280,4 +299,4 @@ export const createRelyingPartyModels = (optionsArg) => {
280
299
  })();
281
300
  return { Session: BoundSessionDoc, LoginAttempt: BoundLoginAttemptDoc };
282
301
  };
283
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ycG1vZGVscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3JlbHlpbmdwYXJ0eS9jbGFzc2VzLnJwbW9kZWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4Qzs7Ozs7R0FLRztJQUNVLHNCQUFzQjtzQkFBUyxPQUFPLENBQUMsT0FBTyxDQUFDLGNBQWM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7aUJBQTdELHNCQUF1QixTQUFRLFdBQThFOzs7OEJBQ3ZILE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFO3FDQUlyQixPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRTtrQ0FJckIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFDdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUU7cUNBR3ZCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3NDQUd0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtxQ0FHdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7cUNBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3NDQUl0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTswQ0FJdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7NkNBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFOzhDQUd0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtnREFJdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7NENBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO1lBNUN2Qix3SkFBTyxFQUFFLDZCQUFGLEVBQUUsK0VBQWM7WUFJdkIsNktBQU8sU0FBUyw2QkFBVCxTQUFTLDZGQUFjO1lBSzlCLG9LQUFPLE1BQU0sNkJBQU4sTUFBTSx1RkFBYztZQUczQiw2S0FBTyxTQUFTLDZCQUFULFNBQVMsNkZBQWE7WUFHN0IsZ0xBQU8sVUFBVSw2QkFBVixVQUFVLCtGQUFhO1lBRzlCLDZLQUFPLFNBQVMsNkJBQVQsU0FBUyw2RkFBYTtZQUk3Qiw2S0FBTyxTQUFTLDZCQUFULFNBQVMsNkZBQVU7WUFJMUIsZ0xBQU8sVUFBVSw2QkFBVixVQUFVLCtGQUFVO1lBSTNCLDRMQUFPLGNBQWMsNkJBQWQsY0FBYyx1R0FBVTtZQUkvQixxTUFBTyxpQkFBaUIsNkJBQWpCLGlCQUFpQiw2R0FBVTtZQUdsQyx3TUFBTyxrQkFBa0IsNkJBQWxCLGtCQUFrQiwrR0FBVTtZQUluQyw4TUFBTyxvQkFBb0IsNkJBQXBCLG9CQUFvQixtSEFBVTtZQUlyQyxrTUFBTyxnQkFBZ0IsNkJBQWhCLGdCQUFnQiwyR0FBVTs7O1FBN0MxQixFQUFFLDZDQUFXLEVBQUUsRUFBQztRQUV2QixtRUFBbUU7UUFFNUQsU0FBUyxxR0FBVyxFQUFFLEdBQUM7UUFFOUIsOERBQThEO1FBR3ZELE1BQU0seUdBQVcsRUFBRSxHQUFDO1FBR3BCLFNBQVMseUdBQVcsQ0FBQyxHQUFDO1FBR3RCLFVBQVUsNkdBQVcsQ0FBQyxHQUFDO1FBR3ZCLFNBQVMsNkdBQVcsQ0FBQyxHQUFDO1FBRTdCLHdDQUF3QztRQUVqQyxTQUFTLHFIQUFVO1FBRTFCLG1HQUFtRztRQUU1RixVQUFVLHNIQUFVO1FBRTNCLHNFQUFzRTtRQUUvRCxjQUFjLDJIQUFVO1FBRS9CLG1GQUFtRjtRQUU1RSxpQkFBaUIsa0lBQVU7UUFHM0Isa0JBQWtCLHNJQUFVO1FBRW5DLHFHQUFxRztRQUU5RixvQkFBb0IseUlBQVU7UUFFckMsaUdBQWlHO1FBRTFGLGdCQUFnQix1SUFBVTs7Ozs7OztTQS9DdEIsc0JBQXNCO0FBa0RuQzs7Ozs7O0dBTUc7SUFDVSwyQkFBMkI7c0JBQVMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxjQUFjOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O2lCQUFsRSwyQkFBNEIsU0FBUSxXQUF3Rjs7OzhCQUN0SSxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRTtxQ0FJckIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUU7NENBSXJCLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFOzhDQUdyQixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtpQ0FHdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7c0NBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3FDQUd0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtxQ0FHdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7c0NBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFOzRDQUl0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtZQS9CdkIsd0pBQU8sRUFBRSw2QkFBRixFQUFFLCtFQUFjO1lBSXZCLDZLQUFPLFNBQVMsNkJBQVQsU0FBUyw2RkFBYztZQUk5QixrTUFBTyxnQkFBZ0IsNkJBQWhCLGdCQUFnQiwyR0FBYztZQUdyQyx3TUFBTyxrQkFBa0IsNkJBQWxCLGtCQUFrQiwrR0FBYztZQUd2QyxpS0FBTyxLQUFLLDZCQUFMLEtBQUsscUZBQWM7WUFJMUIsZ0xBQU8sVUFBVSw2QkFBVixVQUFVLCtGQUFlO1lBR2hDLDZLQUFPLFNBQVMsNkJBQVQsU0FBUyw2RkFBYTtZQUc3Qiw2S0FBTyxTQUFTLDZCQUFULFNBQVMsNkZBQWE7WUFJN0IsZ0xBQU8sVUFBVSw2QkFBVixVQUFVLCtGQUFpQjtZQUlsQyxrTUFBTyxnQkFBZ0IsNkJBQWhCLGdCQUFnQiwyR0FBaUI7OztRQWhDakMsRUFBRSw2Q0FBVyxFQUFFLEVBQUM7UUFFdkIsNEVBQTRFO1FBRXJFLFNBQVMscUdBQVcsRUFBRSxHQUFDO1FBRTlCLDJGQUEyRjtRQUVwRixnQkFBZ0IsbUhBQVcsRUFBRSxHQUFDO1FBRzlCLGtCQUFrQiw0SEFBVyxFQUFFLEdBQUM7UUFHaEMsS0FBSyxpSEFBVyxFQUFFLEdBQUM7UUFFMUIsOENBQThDO1FBRXZDLFVBQVUseUdBQVcsR0FBRyxHQUFDO1FBR3pCLFNBQVMsNkdBQVcsQ0FBQyxHQUFDO1FBR3RCLFNBQVMsNEdBQVcsQ0FBQyxHQUFDO1FBRTdCLGdHQUFnRztRQUV6RixVQUFVLHNIQUFpQjtRQUVsQyxxRkFBcUY7UUFFOUUsZ0JBQWdCLDZIQUFpQjs7Ozs7OztTQWxDN0IsMkJBQTJCO0FBcUN4QyxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRztJQUNwRCxRQUFRLEVBQUUsd0JBQXdCO0lBQ2xDLGFBQWEsRUFBRSw2QkFBNkI7Q0FDcEMsQ0FBQztBQW1CWDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsQ0FBQyxVQUFzQyxFQUF1QixFQUFFO0lBQ3RHLE1BQU0sRUFBRSxHQUFHLFVBQVUsQ0FBQyxFQUFFLENBQUM7SUFDekIsSUFBSSxDQUFDLENBQUMsRUFBRSxZQUFZLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztRQUNqRCxNQUFNLElBQUksS0FBSyxDQUFDLDhFQUE4RSxDQUFDLENBQUM7SUFDbEcsQ0FBQztJQUNELE1BQU0sUUFBUSxHQUFHLFVBQVUsQ0FBQyxlQUFlLEVBQUUsUUFBUSxJQUFJLHNDQUFzQyxDQUFDLFFBQVEsQ0FBQztJQUN6RyxNQUFNLGFBQWEsR0FBRyxVQUFVLENBQUMsZUFBZSxFQUFFLGFBQWE7V0FDMUQsc0NBQXNDLENBQUMsYUFBYSxDQUFDO1FBR3BELGVBQWU7Z0NBRHBCLE9BQU8sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsQ0FBQzs7OzswQkFDckMsc0JBQXNCO21DQUE5QixTQUFRLFdBQXNCOzs7O2dCQUFwRCw2S0FBdUQ7OztnQkFBakQsdURBQWU7Ozs7O1FBR2Ysb0JBQW9CO2dDQUR6QixPQUFPLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsYUFBYSxFQUFFLENBQUM7Ozs7MEJBQ3JDLDJCQUEyQjt3Q0FBbkMsU0FBUSxXQUEyQjs7OztnQkFBOUQsNktBQWlFOzs7Z0JBQTNELHVEQUFvQjs7Ozs7SUFFMUIsT0FBTyxFQUFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLG9CQUFvQixFQUFFLENBQUM7QUFDMUUsQ0FBQyxDQUFDIn0=
302
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ycG1vZGVscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3JlbHlpbmdwYXJ0eS9jbGFzc2VzLnJwbW9kZWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsb0NBQW9DLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUUzRTs7Ozs7R0FLRztJQUNVLHNCQUFzQjtzQkFBUyxPQUFPLENBQUMsT0FBTyxDQUFDLGNBQWM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7aUJBQTdELHNCQUF1QixTQUFRLFdBQThFOzs7OEJBQ3ZILE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFO3FDQUlyQixPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRTtrQ0FJckIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFDdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUU7cUNBR3ZCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3NDQUd0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtxQ0FHdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7cUNBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO3NDQUl0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTswQ0FJdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7NkNBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFOzhDQUd0QixPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRTtnREFJdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7NENBSXRCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO1lBNUN2Qix3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
@@ -4,6 +4,10 @@ import type { IRelyingPartySession } from './interfaces.js';
4
4
  /** The part of a server `TypedSocket` the binding uses. */
5
5
  export type TRelyingPartySocketTransport = Pick<plugins.typedsocket.TypedSocket, 'setServerTag' | 'findAllTargetConnectionsByTag'>;
6
6
  export type TRelyingPartySocketConnection = plugins.typedsocket.ISmartServeConnectionWrapper;
7
+ /** The SmartServe WebSocket peer underneath a connection, as a TypedSocket server hands it out. */
8
+ export type TRelyingPartySocketPeer = TRelyingPartySocketConnection['peer'];
9
+ /** A connection wrapper or its peer; `TypedSocket.setServerTag` takes either. */
10
+ export type TRelyingPartySocketTarget = TRelyingPartySocketConnection | TRelyingPartySocketPeer;
7
11
  /** What a browser connection is pinned to for its whole life; no cookie and no CSRF token is kept. */
8
12
  export interface IRelyingPartyPinnedSession {
9
13
  sessionId: string;
@@ -23,8 +27,8 @@ export type TRelyingPartySocketRegistration = {
23
27
  registered: false;
24
28
  refusal: IRelyingPartySocketRefusal;
25
29
  };
26
- export interface IRelyingPartySocketBindingOptions {
27
- sessions: RelyingPartySessions;
30
+ export interface IRelyingPartySocketBindingOptions<TPrincipal extends object | undefined = undefined> {
31
+ sessions: RelyingPartySessions<TPrincipal>;
28
32
  /** the transport typedserver created; `undefined` before the server runs */
29
33
  transport: () => TRelyingPartySocketTransport | undefined;
30
34
  /** the server-owned tag prefix that finds the connections of a session */
@@ -39,12 +43,12 @@ export interface IRelyingPartySocketBindingOptions {
39
43
  * request. Connection limits, rate limits and sweeps belong to the
40
44
  * application's socket gate.
41
45
  */
42
- export declare class RelyingPartySocketBinding {
46
+ export declare class RelyingPartySocketBinding<TPrincipal extends object | undefined = undefined> {
43
47
  #private;
44
48
  readonly tagPrefix: string;
45
- constructor(optionsArg: IRelyingPartySocketBindingOptions);
46
- /** The upgrade headers a connection arrived with. */
47
- upgradeOf(connectionArg: TRelyingPartySocketConnection): {
49
+ constructor(optionsArg: IRelyingPartySocketBindingOptions<TPrincipal>);
50
+ /** The upgrade headers a connection (or its peer) arrived with. */
51
+ upgradeOf(targetArg: TRelyingPartySocketTarget): {
48
52
  origin: string | null;
49
53
  cookie: string | null;
50
54
  };
@@ -58,16 +62,21 @@ export declare class RelyingPartySocketBinding {
58
62
  cookie: string | null;
59
63
  }): Promise<TRelyingPartySocketRegistration>;
60
64
  tagOf(sessionIdArg: string): string;
61
- /** Tags a connection with its session; throws when the transport is not running or the connection is gone. */
62
- tagConnection(connectionArg: TRelyingPartySocketConnection, sessionIdArg: string): void;
65
+ /**
66
+ * Tags a connection — its wrapper or its peer — with its session; throws
67
+ * when the transport is not running or the connection is gone.
68
+ */
69
+ tagConnection(targetArg: TRelyingPartySocketTarget, sessionIdArg: string): void;
63
70
  /**
64
71
  * The pinned session as it stands right now: re-read, live, its principal
65
- * active and unchanged, touched under the liveness it was read with. The
72
+ * active and unchanged (and resolved, with `principals.resolve`), touched
73
+ * under the liveness it was read with. The
66
74
  * browser keeps proving itself with its CSRF token, compared against the
67
75
  * hash the registration kept. Nothing of this is cached.
68
76
  */
69
77
  revalidate(pinnedArg: IRelyingPartyPinnedSession): Promise<{
70
78
  session: IRelyingPartySession;
79
+ principal: TPrincipal;
71
80
  verifyCsrf(candidateArg: unknown): boolean;
72
81
  } | null>;
73
82
  /** The live connections of one session, for closing them when it ends. */
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { sha256, timingSafeEqualString } from './helpers.crypto.js';
3
+ const peerOf = (targetArg) => 'peer' in targetArg ? targetArg.peer : targetArg;
3
4
  /**
4
5
  * Binds typed socket connections to relying-party browser sessions. A
5
6
  * connection registers once with the CSRF token of the page; the proof is the
@@ -18,9 +19,9 @@ export class RelyingPartySocketBinding {
18
19
  this.#transport = optionsArg.transport;
19
20
  this.tagPrefix = optionsArg.tagPrefix ?? 'idp_rp_session:';
20
21
  }
21
- /** The upgrade headers a connection arrived with. */
22
- upgradeOf(connectionArg) {
23
- const headers = connectionArg.peer.context.headers;
22
+ /** The upgrade headers a connection (or its peer) arrived with. */
23
+ upgradeOf(targetArg) {
24
+ const headers = peerOf(targetArg).context.headers;
24
25
  return { origin: headers.get('origin'), cookie: headers.get('cookie') };
25
26
  }
26
27
  /**
@@ -55,28 +56,33 @@ export class RelyingPartySocketBinding {
55
56
  tagOf(sessionIdArg) {
56
57
  return this.tagPrefix + sessionIdArg;
57
58
  }
58
- /** Tags a connection with its session; throws when the transport is not running or the connection is gone. */
59
- tagConnection(connectionArg, sessionIdArg) {
59
+ /**
60
+ * Tags a connection — its wrapper or its peer — with its session; throws
61
+ * when the transport is not running or the connection is gone.
62
+ */
63
+ tagConnection(targetArg, sessionIdArg) {
60
64
  const transport = this.#transport();
61
65
  if (!transport) {
62
66
  throw new Error('The socket transport is not running');
63
67
  }
64
- transport.setServerTag(connectionArg, this.tagOf(sessionIdArg));
68
+ transport.setServerTag(targetArg, this.tagOf(sessionIdArg));
65
69
  }
66
70
  /**
67
71
  * The pinned session as it stands right now: re-read, live, its principal
68
- * active and unchanged, touched under the liveness it was read with. The
72
+ * active and unchanged (and resolved, with `principals.resolve`), touched
73
+ * under the liveness it was read with. The
69
74
  * browser keeps proving itself with its CSRF token, compared against the
70
75
  * hash the registration kept. Nothing of this is cached.
71
76
  */
72
77
  async revalidate(pinnedArg) {
73
- const session = await this.#sessions.revalidateSession(pinnedArg.sessionId);
74
- if (!session || session.principalId !== pinnedArg.principalId) {
78
+ const live = await this.#sessions.revalidateLiveSession(pinnedArg.sessionId);
79
+ if (!live || live.session.principalId !== pinnedArg.principalId) {
75
80
  return null;
76
81
  }
77
82
  const expectedHash = pinnedArg.csrfTokenHash;
78
83
  return {
79
- session,
84
+ session: live.session,
85
+ principal: live.principal,
80
86
  verifyCsrf: (candidateArg) => typeof candidateArg === 'string'
81
87
  && expectedHash.length > 0
82
88
  && timingSafeEqualString(expectedHash, sha256(candidateArg)),
@@ -91,4 +97,4 @@ export class RelyingPartySocketBinding {
91
97
  return transport.findAllTargetConnectionsByTag(this.tagOf(sessionIdArg));
92
98
  }
93
99
  }
94
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zb2NrZXRiaW5kaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfcmVseWluZ3BhcnR5L2NsYXNzZXMuc29ja2V0YmluZGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFvQ3BFOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxPQUFPLHlCQUF5QjtJQUMzQixTQUFTLENBQXVCO0lBQ2hDLFVBQVUsQ0FBaUQ7SUFDcEQsU0FBUyxDQUFTO0lBRWxDLFlBQVksVUFBNkM7UUFDdkQsSUFBSSxDQUFDLFNBQVMsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDO1FBQ3JDLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQztRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLFVBQVUsQ0FBQyxTQUFTLElBQUksaUJBQWlCLENBQUM7SUFDN0QsQ0FBQztJQUVELHFEQUFxRDtJQUM5QyxTQUFTLENBQUMsYUFBNEM7UUFDM0QsTUFBTSxPQUFPLEdBQUcsYUFBYSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDO1FBQ25ELE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO0lBQzFFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLDBCQUEwQixDQUNyQyxVQUFtQixFQUNuQixVQUE0RDtRQUU1RCxJQUFJLENBQUMsVUFBVSxJQUFJLE9BQU8sVUFBVSxLQUFLLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDM0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNoRixDQUFDO1FBQ0QsSUFBSSxVQUFVLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUM1RSxDQUFDO1FBQ0QsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsU0FBUyxDQUFDLG9CQUFvQixDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM5RSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDZCxPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsY0FBYyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO1FBQy9FLENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNqRixPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO1FBQzVFLENBQUM7UUFDRCxPQUFPO1lBQ0wsVUFBVSxFQUFFLElBQUk7WUFDaEIsTUFBTSxFQUFFO2dCQUNOLFNBQVMsRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQzlCLFdBQVcsRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLFdBQVc7Z0JBQ3pDLGFBQWEsRUFBRSxNQUFNLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQzthQUMxQztZQUNELFNBQVMsRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLFNBQVM7U0FDdEMsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsWUFBb0I7UUFDL0IsT0FBTyxJQUFJLENBQUMsU0FBUyxHQUFHLFlBQVksQ0FBQztJQUN2QyxDQUFDO0lBRUQsOEdBQThHO0lBQ3ZHLGFBQWEsQ0FBQyxhQUE0QyxFQUFFLFlBQW9CO1FBQ3JGLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNwQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLHFDQUFxQyxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUNELFNBQVMsQ0FBQyxZQUFZLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUNyQixTQUFxQztRQUVyQyxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsaUJBQWlCLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQyxPQUFPLElBQUksT0FBTyxDQUFDLFdBQVcsS0FBSyxTQUFTLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDOUQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQ0QsTUFBTSxZQUFZLEdBQUcsU0FBUyxDQUFDLGFBQWEsQ0FBQztRQUM3QyxPQUFPO1lBQ0wsT0FBTztZQUNQLFVBQVUsRUFBRSxDQUFDLFlBQVksRUFBRSxFQUFFLENBQzNCLE9BQU8sWUFBWSxLQUFLLFFBQVE7bUJBQzdCLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQzttQkFDdkIscUJBQXFCLENBQUMsWUFBWSxFQUFFLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztTQUMvRCxDQUFDO0lBQ0osQ0FBQztJQUVELDBFQUEwRTtJQUNuRSxLQUFLLENBQUMsb0JBQW9CLENBQUMsWUFBb0I7UUFDcEQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNmLE9BQU8sRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUNELE9BQU8sU0FBUyxDQUFDLDZCQUE2QixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztJQUMzRSxDQUFDO0NBQ0YifQ==
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zb2NrZXRiaW5kaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfcmVseWluZ3BhcnR5L2NsYXNzZXMuc29ja2V0YmluZGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFpQnBFLE1BQU0sTUFBTSxHQUFHLENBQUMsU0FBb0MsRUFBMkIsRUFBRSxDQUMvRSxNQUFNLElBQUksU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUEyQm5EOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxPQUFPLHlCQUF5QjtJQUMzQixTQUFTLENBQW1DO0lBQzVDLFVBQVUsQ0FBaUQ7SUFDcEQsU0FBUyxDQUFTO0lBRWxDLFlBQVksVUFBeUQ7UUFDbkUsSUFBSSxDQUFDLFNBQVMsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDO1FBQ3JDLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQztRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLFVBQVUsQ0FBQyxTQUFTLElBQUksaUJBQWlCLENBQUM7SUFDN0QsQ0FBQztJQUVELG1FQUFtRTtJQUM1RCxTQUFTLENBQUMsU0FBb0M7UUFDbkQsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDbEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7SUFDMUUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsMEJBQTBCLENBQ3JDLFVBQW1CLEVBQ25C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
@@ -11,13 +11,23 @@ export interface IRelyingPartySession {
11
11
  idpSubject?: string;
12
12
  loginAttemptId?: string;
13
13
  }
14
- /** A live session behind a cookie, with the CSRF token derived for it. */
15
- export interface IResolvedRelyingPartySession {
14
+ /**
15
+ * A live session with the principal it acts as. `principal` is what
16
+ * `principals.resolve` answered for it in the same read; with `isActive`
17
+ * principals it is `undefined`.
18
+ */
19
+ export interface IRelyingPartyLiveSession<TPrincipal extends object | undefined = undefined> {
16
20
  session: IRelyingPartySession;
21
+ principal: TPrincipal;
22
+ }
23
+ /** A live session behind a cookie, with its principal and the CSRF token derived for it. */
24
+ export interface IResolvedRelyingPartySession<TPrincipal extends object | undefined = undefined> extends IRelyingPartyLiveSession<TPrincipal> {
17
25
  csrfToken: string;
18
26
  }
19
27
  /** A session just opened: the answer, the CSRF token and the `Set-Cookie` value that carries it. */
20
- export interface IRelyingPartySessionOpening extends IResolvedRelyingPartySession {
28
+ export interface IRelyingPartySessionOpening {
29
+ session: IRelyingPartySession;
30
+ csrfToken: string;
21
31
  sessionCookie: string;
22
32
  }
23
33
  /** The redirect that starts a provider login, and the cookie that binds it to this browser. */
@@ -54,6 +64,25 @@ export interface IRelyingPartyPrincipals {
54
64
  /** Whether a principal may act right now; a session of an inactive principal resolves to nothing. */
55
65
  isActive(principalIdArg: string): Promise<boolean>;
56
66
  }
67
+ /**
68
+ * Principals that answer the principal itself instead of a yes or no, so a
69
+ * request reads its user once: `resolve` answers the principal when it may act
70
+ * right now and `null` otherwise, and every live session carries the answer as
71
+ * `principal`. Give either `isActive` or `resolve`, never both.
72
+ */
73
+ export interface IRelyingPartyPrincipalResolver<TPrincipal extends object | undefined> {
74
+ admit(completionArg: IOidcCompletion): Promise<{
75
+ principalId: string;
76
+ }>;
77
+ /** The principal object when it may act right now, `null` otherwise; any answer that is not an object (including `false`) resolves the session to nothing. */
78
+ resolve(principalIdArg: string): Promise<TPrincipal | null>;
79
+ }
80
+ /**
81
+ * What `RelyingPartySessions` accepts as its principals: a resolver whose
82
+ * principal type every live session carries, or — only while that type is
83
+ * left `undefined` — the `isActive` form.
84
+ */
85
+ export type TRelyingPartyPrincipals<TPrincipal extends object | undefined> = IRelyingPartyPrincipalResolver<TPrincipal> | (undefined extends TPrincipal ? IRelyingPartyPrincipals : never);
57
86
  export type TRelyingPartySessionEndReason =
58
87
  /** the application or the user ended it */
59
88
  'logout'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idp.global/sdk",
3
- "version": "18.1.0",
3
+ "version": "18.2.0",
4
4
  "private": false,
5
5
  "description": "Reusable browser and server SDK for idp.global authentication.",
6
6
  "type": "module",
package/readme.md CHANGED
@@ -561,19 +561,76 @@ A deployment without a provider (`provider` omitted) keeps the same session
561
561
  store for sessions it opens itself with `openSession({ principalId })`, for
562
562
  example after a local password check.
563
563
 
564
+ **Building before the database connects.** `db` may be a function that answers
565
+ the database once it exists. It is called on every use of a model, the way
566
+ `@Collection(() => db)` resolves it, and never while the models,
567
+ `RelyingPartySessions`, `RelyingPartyRoutes` or `RelyingPartySocketBinding` are
568
+ constructed — none of them does I/O in its constructor. A use before the
569
+ function answers a `SmartdataDb` (or while it throws) rejects with
570
+ `RelyingPartyDatabaseUnavailableError`, whose `cause` is the function's error:
571
+
572
+ ```ts
573
+ const models = createRelyingPartyModels({ db: () => appDatabase.db });
574
+ const sessions = new RelyingPartySessions({ models, /* … */ });
575
+ // later, at startup
576
+ await appDatabase.init();
577
+ await models.Session.ensureInitialized();
578
+ await models.LoginAttempt.ensureInitialized();
579
+ ```
580
+
581
+ **Reading the principal once.** `principals.isActive` answers a yes or no, so a
582
+ request that needs the user reads it a second time. Principals may give
583
+ `resolve` instead: it answers the principal when it may act right now and
584
+ `null` otherwise, and every live session carries that answer as `principal` —
585
+ on `resolveCookieSession`, `revalidateLiveSession`, `RelyingPartySocketBinding.revalidate`
586
+ and the routes' `sessionAnswer`. The principal type is inferred from `resolve`;
587
+ with `isActive` it is `undefined`. Give exactly one of the two; the constructor
588
+ refuses both or neither.
589
+
590
+ ```ts
591
+ const sessions = new RelyingPartySessions({
592
+ // …
593
+ principals: {
594
+ admit: async (completion) => ({ principalId: await users.upsertFromIdp(completion) }),
595
+ resolve: async (principalId) => {
596
+ const user = await users.get(principalId);
597
+ return user?.status === 'active' ? user : null;
598
+ },
599
+ },
600
+ });
601
+ const resolved = await sessions.resolveCookieSession(cookieHeader);
602
+ resolved?.principal.displayName; // typed as the user
603
+ ```
604
+
605
+ **Recording a logout.** `RelyingPartyRoutes` takes
606
+ `onLogout({ session, endedLiveSession })`. It runs only for a `POST
607
+ <prefix>logout` that passed the origin and CSRF checks, after the session was
608
+ ended, and the answer waits for it, so an audit record is written before the
609
+ browser learns the logout is done. `endedLiveSession` is `true` only when this
610
+ call ended a live session: a logout that lost a race to another end gets
611
+ `false`, and a repeated logout finds no session and does not call it. A hook
612
+ that throws or rejects never keeps the session alive and never changes the
613
+ answer — the session is already over — and is reported as a `hook_failed`
614
+ route event (`hook: 'onLogout'`) through `onRouteEvent`. The hook runs without
615
+ a deadline of its own; bound a slow audit store inside it.
616
+
617
+ **Socket peers.** `RelyingPartySocketBinding.tagConnection` and `upgradeOf` take
618
+ the connection wrapper or the bare SmartServe peer (`TRelyingPartySocketPeer`),
619
+ the two forms `TypedSocket.setServerTag` accepts.
620
+
564
621
  ### Relying-Party API Surface
565
622
 
566
623
  | API | Description |
567
624
  | --- | --- |
568
625
  | `IdpOidcProvider` | Discovery, PKCE authorization URL, code exchange with UserInfo, refresh, revocation and organization introspection for idp.global. Implements `IOidcProvider`. |
569
- | `createRelyingPartyModels()` | Binds the session and login-attempt models to one `SmartdataDb`, optionally under existing collection names. |
570
- | `RelyingPartySessions` | Login start/complete/abandon/finalize, `openSession`, `resolveCookieSession`, `revalidateSession`, `isSessionLive`, `validateCsrf`, `logout`/`endSession`, `endSessionsOfPrincipal`, `liveProviderToken`. |
626
+ | `createRelyingPartyModels()` | Binds the session and login-attempt models to one `SmartdataDb`, or to a function asked for it on every use, optionally under existing collection names. |
627
+ | `RelyingPartySessions<TPrincipal>` | Login start/complete/abandon/finalize, `openSession`, `resolveCookieSession`, `revalidateSession`, `revalidateLiveSession`, `isSessionLive`, `validateCsrf`, `logout`/`endSession`, `endSessionsOfPrincipal`, `liveProviderToken`. Principals give `isActive` or `resolve` (`IRelyingPartyPrincipals`, `IRelyingPartyPrincipalResolver<TPrincipal>`); with `resolve`, live sessions carry `principal`. |
571
628
  | `AuthorityObserver` | Live organization authority with opt-in positive and negative reuse. |
572
- | `RelyingPartyRoutes` | `handle(ctx)` for `/auth/login`, `/auth/callback`, `/auth/finalize`, `/auth/session` and `/auth/logout`; `null` for anything else. Takes a typedserver route context. |
629
+ | `RelyingPartyRoutes` | `handle(ctx)` for `/auth/login`, `/auth/callback`, `/auth/finalize`, `/auth/session` and `/auth/logout`; `null` for anything else. Takes a typedserver route context. `onLogout` is awaited after a CSRF-checked logout ended the session. |
573
630
  | `createActorScope()` | AsyncLocalStorage actor context with a TypedRouter request scope. |
574
- | `RelyingPartySocketBinding` | TypedSocket browser registration, per-request re-validation and session tags. |
631
+ | `RelyingPartySocketBinding` | TypedSocket browser registration, per-request re-validation and session tags; takes a connection wrapper or its peer. |
575
632
  | `SecretBox`, `deriveCsrfToken()`, `decodeSecretKey()`, `parseCookies()`, `serializeCookie()`, `sanitizeReturnPath()` | The primitives underneath. |
576
- | `RelyingPartyError`, `RelyingPartyRefusal`, `LoginCallbackError`, `AuthorityTokenError`, `AuthorityRefusedError` | Errors with stable codes; none carries token material. |
633
+ | `RelyingPartyError`, `RelyingPartyRefusal`, `LoginCallbackError`, `AuthorityTokenError`, `AuthorityRefusedError`, `RelyingPartyDatabaseUnavailableError` | Errors with stable codes; none carries token material. |
577
634
 
578
635
  ## Runtime Notes
579
636
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@idp.global/sdk',
6
- version: '18.1.0',
6
+ version: '18.2.0',
7
7
  description: 'Reusable browser and server SDK for idp.global authentication.'
8
8
  }
@@ -67,3 +67,11 @@ export class AuthorityTokenError extends Error {
67
67
  this.status = code === 'authority_login_required' ? 403 : 503;
68
68
  }
69
69
  }
70
+
71
+ /** A relying-party model was used before the database it is bound to exists. */
72
+ export class RelyingPartyDatabaseUnavailableError extends Error {
73
+ constructor(causeArg?: unknown) {
74
+ super('The relying-party models were used before their database exists', { cause: causeArg });
75
+ this.name = 'RelyingPartyDatabaseUnavailableError';
76
+ }
77
+ }
@@ -1,7 +1,7 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { LoginCallbackError, RelyingPartyError, RelyingPartyRefusal } from './classes.errors.js';
3
3
  import type { RelyingPartySessions } from './classes.relyingpartysessions.js';
4
- import type { IResolvedRelyingPartySession } from './interfaces.js';
4
+ import type { IRelyingPartySession, IResolvedRelyingPartySession } from './interfaces.js';
5
5
 
6
6
  /**
7
7
  * The part of an HTTP request context the routes read. `IRequestContext` of
@@ -16,8 +16,8 @@ export interface IRelyingPartyHttpContext {
16
16
  readonly request: Request;
17
17
  }
18
18
 
19
- export interface IRelyingPartyRoutesOptions {
20
- sessions: RelyingPartySessions;
19
+ export interface IRelyingPartyRoutesOptions<TPrincipal extends object | undefined = undefined> {
20
+ sessions: RelyingPartySessions<TPrincipal>;
21
21
  /** where a sign-in that did not finish sends the browser; no provider text is ever echoed into it */
22
22
  failureLocation: string;
23
23
  /**
@@ -30,7 +30,17 @@ export interface IRelyingPartyRoutesOptions {
30
30
  * The body `GET <prefix>session` answers with. Default:
31
31
  * `{ session: { sessionId, principalId, expiresAt, csrfToken } | null }`.
32
32
  */
33
- sessionAnswer?(resolvedArg: IResolvedRelyingPartySession | null): Promise<object>;
33
+ sessionAnswer?(resolvedArg: IResolvedRelyingPartySession<TPrincipal> | null): Promise<object>;
34
+ /**
35
+ * A `POST <prefix>logout` passed the origin and CSRF checks and the session
36
+ * was ended. Awaited before the answer, so an audit record is written
37
+ * before the browser learns the logout is done. `endedLiveSession` is `true`
38
+ * only when this call ended a live session; a logout that lost a race to
39
+ * another end answers `false` and records nothing. The session is already
40
+ * over when this runs: a throw (or a rejection) never keeps it alive and
41
+ * never changes the answer — it is reported as a `hook_failed` route event.
42
+ */
43
+ onLogout?(eventArg: { session: IRelyingPartySession; endedLiveSession: boolean }): Promise<void>;
34
44
  /** the largest logout body read, in bytes; a longer one is cancelled as soon as it passes the limit */
35
45
  maxBodyBytes?: number;
36
46
  /**
@@ -39,7 +49,15 @@ export interface IRelyingPartyRoutesOptions {
39
49
  * event and its failure go to `console.error` instead.
40
50
  */
41
51
  onRouteEvent?(eventArg: {
42
- event: 'auth_login_start_failed' | 'auth_callback_failed' | 'auth_callback_declined' | 'auth_callback_refused';
52
+ event:
53
+ | 'auth_login_start_failed'
54
+ | 'auth_callback_failed'
55
+ | 'auth_callback_declined'
56
+ | 'auth_callback_refused'
57
+ /** an application hook of the routes threw; the step it belonged to is complete */
58
+ | 'hook_failed';
59
+ hook?: 'onLogout';
60
+ sessionId?: string;
43
61
  stage?: string;
44
62
  code?: string;
45
63
  correlationId?: string;
@@ -78,18 +96,19 @@ const redirect = (locationArg: string, headersArg?: Record<string, string>): Res
78
96
  * - `GET finalize` releases the return path only to the browser holding the
79
97
  * session this attempt created
80
98
  * - `GET session` answers the current session
81
- * - `POST logout` ends the session; it needs this origin and the CSRF token
99
+ * - `POST logout` ends the session; it needs this origin and the CSRF token,
100
+ * and `onLogout` runs before it answers
82
101
  *
83
102
  * `handle()` answers `null` for every other request, so the application
84
103
  * chains its own routes (a local password login) around it. Cookies are only
85
104
  * ever set here: a `/typedrequest` answer cannot carry `Set-Cookie`.
86
105
  */
87
- export class RelyingPartyRoutes {
88
- readonly #sessions: RelyingPartySessions;
89
- readonly #options: IRelyingPartyRoutesOptions;
106
+ export class RelyingPartyRoutes<TPrincipal extends object | undefined = undefined> {
107
+ readonly #sessions: RelyingPartySessions<TPrincipal>;
108
+ readonly #options: IRelyingPartyRoutesOptions<TPrincipal>;
90
109
  readonly #maxBodyBytes: number;
91
110
 
92
- constructor(optionsArg: IRelyingPartyRoutesOptions) {
111
+ constructor(optionsArg: IRelyingPartyRoutesOptions<TPrincipal>) {
93
112
  this.#sessions = optionsArg.sessions;
94
113
  this.#options = optionsArg;
95
114
  this.#maxBodyBytes = optionsArg.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
@@ -200,10 +219,24 @@ export class RelyingPartyRoutes {
200
219
  if (!this.#sessions.validateCsrf(resolved, csrfToken)) {
201
220
  return jsonResponse({ error: 'forbidden' }, 403);
202
221
  }
203
- return jsonResponse({ endedLiveSession: await this.#sessions.logout(resolved.session.id) }, 200, clearSession);
222
+ // the session ends first: whatever the hook does, it cannot keep it alive
223
+ const endedLiveSession = await this.#sessions.logout(resolved.session.id);
224
+ if (this.#options.onLogout) {
225
+ try {
226
+ await this.#options.onLogout({ session: resolved.session, endedLiveSession });
227
+ } catch (error) {
228
+ this.emit({
229
+ event: 'hook_failed',
230
+ hook: 'onLogout',
231
+ sessionId: resolved.session.id,
232
+ message: error instanceof Error ? error.message : String(error),
233
+ });
234
+ }
235
+ }
236
+ return jsonResponse({ endedLiveSession }, 200, clearSession);
204
237
  }
205
238
 
206
- private async sessionAnswer(resolvedArg: IResolvedRelyingPartySession | null): Promise<object> {
239
+ private async sessionAnswer(resolvedArg: IResolvedRelyingPartySession<TPrincipal> | null): Promise<object> {
207
240
  if (this.#options.sessionAnswer) {
208
241
  return this.#options.sessionAnswer(resolvedArg);
209
242
  }
@@ -261,7 +294,7 @@ export class RelyingPartyRoutes {
261
294
  }
262
295
  }
263
296
 
264
- private emit(eventArg: Parameters<NonNullable<IRelyingPartyRoutesOptions['onRouteEvent']>>[0]): void {
297
+ private emit(eventArg: Parameters<NonNullable<IRelyingPartyRoutesOptions<TPrincipal>['onRouteEvent']>>[0]): void {
265
298
  if (!this.#options.onRouteEvent) {
266
299
  console.error(JSON.stringify(eventArg));
267
300
  return;