@idp.global/sdk 19.3.0 → 19.4.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.
Files changed (38) hide show
  1. package/changelog.md +40 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts_browser/classes.idpclient.js +1 -1
  4. package/dist_ts_browser/classes.idprequests.js +1 -1
  5. package/dist_ts_browser/classes.relyingpartybearerclient.d.ts +276 -0
  6. package/dist_ts_browser/classes.relyingpartybearerclient.js +591 -0
  7. package/dist_ts_browser/index.d.ts +1 -0
  8. package/dist_ts_browser/index.js +2 -1
  9. package/dist_ts_browser/plugins.js +1 -1
  10. package/dist_ts_browser/secret-delivery.js +1 -1
  11. package/dist_ts_relyingparty/classes.authorityobserver.d.ts +23 -0
  12. package/dist_ts_relyingparty/classes.authorityobserver.js +68 -13
  13. package/dist_ts_relyingparty/classes.errors.d.ts +3 -1
  14. package/dist_ts_relyingparty/classes.errors.js +2 -1
  15. package/dist_ts_relyingparty/classes.relyingpartyroutes.d.ts +12 -0
  16. package/dist_ts_relyingparty/classes.relyingpartyroutes.js +33 -6
  17. package/dist_ts_relyingparty/classes.relyingpartysessions.d.ts +102 -7
  18. package/dist_ts_relyingparty/classes.relyingpartysessions.js +228 -29
  19. package/dist_ts_relyingparty/classes.rpmodels.d.ts +6 -4
  20. package/dist_ts_relyingparty/classes.rpmodels.js +8 -6
  21. package/dist_ts_relyingparty/contract.bearer.d.ts +82 -0
  22. package/dist_ts_relyingparty/contract.bearer.js +9 -0
  23. package/dist_ts_relyingparty/helpers.bearer.d.ts +3 -3
  24. package/dist_ts_relyingparty/helpers.bearer.js +3 -3
  25. package/dist_ts_relyingparty/interfaces.d.ts +34 -24
  26. package/package.json +2 -1
  27. package/readme.md +215 -19
  28. package/ts/00_commitinfo_data.ts +1 -1
  29. package/ts_browser/classes.relyingpartybearerclient.ts +821 -0
  30. package/ts_browser/index.ts +1 -0
  31. package/ts_relyingparty/classes.authorityobserver.ts +80 -12
  32. package/ts_relyingparty/classes.errors.ts +4 -1
  33. package/ts_relyingparty/classes.relyingpartyroutes.ts +38 -7
  34. package/ts_relyingparty/classes.relyingpartysessions.ts +298 -34
  35. package/ts_relyingparty/classes.rpmodels.ts +7 -4
  36. package/ts_relyingparty/contract.bearer.ts +99 -0
  37. package/ts_relyingparty/helpers.bearer.ts +8 -9
  38. package/ts_relyingparty/interfaces.ts +49 -26
@@ -1,6 +1,6 @@
1
1
  import { type IOidcProvider } from './classes.idpoidcprovider.js';
2
2
  import type { IRelyingPartyModels } from './classes.rpmodels.js';
3
- import type { IAuthorityTokenSource, IProviderToken, IRelyingPartyBearerDelivery, IRelyingPartyBearerHandoff, IRelyingPartyBearerLoginRequest, IRelyingPartyBearerLoginStart, IRelyingPartyBearerOptions, IRelyingPartyBearerReturnTargets, IRelyingPartyBearerTokens, IRelyingPartyHooks, IRelyingPartyLiveSession, IRelyingPartyLoginOptions, IRelyingPartyLoginStart, IRelyingPartySession, IRelyingPartySessionOpening, IResolvedRelyingPartySession, TRelyingPartyPrincipals, TRelyingPartySessionEndReason } from './interfaces.js';
3
+ import type { IAuthorityTokenSource, IProviderToken, IRelyingPartyBearerDelivery, IRelyingPartyBearerHandoff, IRelyingPartyBearerLoginRequest, IRelyingPartyBearerLoginStart, IRelyingPartyBearerOptions, IRelyingPartyBearerReturnTargets, IRelyingPartyBearerTokens, IRelyingPartyHooks, IRelyingPartyLiveSession, IRelyingPartyLoginOptions, IRelyingPartyLoginStart, IRelyingPartySession, IRelyingPartySessionOpening, IRelyingPartySessionsPage, IRelyingPartySessionsPageOptions, IResolvedRelyingPartySession, TRelyingPartyPrincipals, TRelyingPartySessionEndReason } from './interfaces.js';
4
4
  export declare const RELYING_PARTY_DEFAULT_SESSION_MAX_AGE_SECONDS: number;
5
5
  export declare const RELYING_PARTY_DEFAULT_LOGIN_PATH_PREFIX = "/auth/";
6
6
  /** The label the CSRF HMAC starts with unless the application names its own. */
@@ -9,6 +9,10 @@ export declare const RELYING_PARTY_DEFAULT_CSRF_LABEL: string;
9
9
  export declare const LOGIN_ATTEMPT_MAX_AGE_SECONDS: number;
10
10
  /** The longest intent a login carries (`IRelyingPartyLoginOptions.intent`), in characters. */
11
11
  export declare const RELYING_PARTY_MAX_INTENT_LENGTH = 4096;
12
+ /** How many sessions one page of `sessionsOfPrincipal` holds unless its `limit` says otherwise. */
13
+ export declare const RELYING_PARTY_DEFAULT_SESSIONS_PAGE_LIMIT = 100;
14
+ /** The largest `limit` of `sessionsOfPrincipal`; the store reads no larger page. */
15
+ export declare const RELYING_PARTY_MAX_SESSIONS_PAGE_LIMIT = 1000;
12
16
  /** How long a bearer hand-off code can be claimed, at most; never past its session. */
13
17
  export declare const BEARER_HANDOFF_MAX_AGE_SECONDS = 60;
14
18
  /** How long a bearer access token lives unless `bearer.accessTokenMaxAgeSeconds` says otherwise. */
@@ -40,6 +44,12 @@ export interface IRelyingPartySessionsOptions<TPrincipal extends object | undefi
40
44
  sessionEncryptionKey: Buffer;
41
45
  /** 32 bytes; the CSRF HMAC key */
42
46
  csrfKey: Buffer;
47
+ /**
48
+ * `true` (the default): cookie sessions with a session-bound CSRF token,
49
+ * beside bearer hand-off mode when `bearer` is given. Bearer hand-off alone,
50
+ * without any cookie, is `IRelyingPartyBearerOnlySessionsOptions`.
51
+ */
52
+ cookieSessions?: true;
43
53
  /** the label the CSRF HMAC starts with; an application with live sessions keeps its own */
44
54
  csrfLabel?: string;
45
55
  sessionMaxAgeSeconds?: number;
@@ -63,15 +73,42 @@ export interface IRelyingPartySessionsOptions<TPrincipal extends object | undefi
63
73
  /** the clock every comparison reads; tests step it instead of sleeping */
64
74
  now?: () => number;
65
75
  }
76
+ /**
77
+ * Bearer-only mode (`cookieSessions: false`): the application's own clients
78
+ * sign in through bearer hand-off and nothing else. The relying party never
79
+ * reads or sets a cookie — not even the clearing intent cookie of a callback
80
+ * that is no bearer login — so it has no CSRF token and takes no CSRF key:
81
+ * `csrfKey`, `csrfLabel` and `cookieNames` are refused, not ignored. The
82
+ * cookie steps (`openSession`, `startLogin`, `completeLogin`, `abandonLogin`,
83
+ * `finalizeLogin`, `clearSessionCookie`, `clearIntentCookie`) throw
84
+ * `RelyingPartyError('login_method_unavailable')` and `resolveCookieSession`
85
+ * answers `null`.
86
+ */
87
+ export interface IRelyingPartyBearerOnlySessionsOptions<TPrincipal extends object | undefined = undefined> extends Omit<IRelyingPartySessionsOptions<TPrincipal>, 'provider' | 'csrfKey' | 'csrfLabel' | 'cookieNames' | 'cookieSessions' | 'bearer'> {
88
+ cookieSessions: false;
89
+ /** every bearer session is a provider login */
90
+ provider: IOidcProvider;
91
+ bearer: IRelyingPartyBearerOptions;
92
+ csrfKey?: never;
93
+ csrfLabel?: never;
94
+ cookieNames?: never;
95
+ }
66
96
  /**
67
97
  * The relying-party session layer: provider logins through the authorization
68
98
  * code flow with PKCE, HttpOnly cookie sessions with a session-bound CSRF
69
99
  * token, provider tokens sealed at rest and rotated under a cross-replica
70
100
  * lock, and session ends that hand the tokens back.
71
101
  *
72
- * Nothing here runs on a timer: expired login attempts are swept when the next
73
- * login starts, and the only timer — the revocation budget — is unreferenced
74
- * and cleared on every path. Nothing here does I/O while it is constructed,
102
+ * With `cookieSessions: false` it runs bearer hand-off mode alone and never
103
+ * touches a cookie (`IRelyingPartyBearerOnlySessionsOptions`).
104
+ *
105
+ * Nothing here runs on a timer: expired login attempts, bearer state and
106
+ * ended sessions are swept one bounded batch at a time when a login starts or
107
+ * the application opens a session, and the only timer — the revocation
108
+ * budget — is unreferenced and cleared on every path. Every session comes
109
+ * from a login start or from `openSession`, and each of those sweeps a batch
110
+ * of at least one first, so removal keeps pace with creation and the ended
111
+ * sessions kept stay bounded. Nothing here does I/O while it is constructed,
75
112
  * so it is built before the database connects.
76
113
  *
77
114
  * `TPrincipal` is what a `principals.resolve` answers; with `isActive`
@@ -85,9 +122,11 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
85
122
  readonly intentCookieName: string;
86
123
  readonly loginPathPrefix: string;
87
124
  readonly sessionMaxAgeSeconds: number;
88
- constructor(optionsArg: IRelyingPartySessionsOptions<TPrincipal>);
125
+ constructor(optionsArg: IRelyingPartySessionsOptions<TPrincipal> | IRelyingPartyBearerOnlySessionsOptions<TPrincipal>);
89
126
  /** Whether provider logins are offered at all. */
90
127
  get providerLoginEnabled(): boolean;
128
+ /** Whether cookie sessions are offered; `false` only in bearer-only mode (`cookieSessions: false`). */
129
+ get cookieSessionsEnabled(): boolean;
91
130
  /** Whether bearer hand-off mode is offered. */
92
131
  get bearerLoginEnabled(): boolean;
93
132
  /** The return targets bearer clients may name, per kind, as configured; `undefined` without bearer mode. */
@@ -109,7 +148,11 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
109
148
  principalId: string;
110
149
  maxAgeSeconds?: number;
111
150
  }): Promise<IRelyingPartySessionOpening>;
112
- /** The live session behind the session cookie, or `null`. A read touches it at most once a minute. */
151
+ /**
152
+ * The live session behind the session cookie, or `null`. A read touches it
153
+ * at most once a minute. Always `null` in bearer-only mode, which reads no
154
+ * cookie.
155
+ */
113
156
  resolveCookieSession(cookieHeaderArg: string | null | undefined): Promise<IResolvedRelyingPartySession<TPrincipal> | null>;
114
157
  /**
115
158
  * Re-checks a session by its id with the same strength as a cookie request:
@@ -142,6 +185,16 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
142
185
  endSessionsOfPrincipal(principalIdArg: string, optionsArg?: {
143
186
  keepSessionId?: string;
144
187
  }): Promise<number>;
188
+ /**
189
+ * One page of a principal's live sessions, newest first: the ones neither
190
+ * ended nor expired, and none at all while the principal is not active —
191
+ * exactly the sessions `isSessionLive` confirms. Pass the page's
192
+ * `nextCursor` as `cursor` to read the next one; the last page has none.
193
+ * The cursor is opaque, and one this method did not answer is refused with
194
+ * `invalid_sessions_cursor`. A session opened while the pages are read is
195
+ * listed only when it sorts after the cursor; none is listed twice.
196
+ */
197
+ sessionsOfPrincipal(principalIdArg: string, optionsArg?: IRelyingPartySessionsPageOptions): Promise<IRelyingPartySessionsPage>;
145
198
  clearSessionCookie(): string;
146
199
  clearIntentCookie(): string;
147
200
  /**
@@ -235,8 +288,25 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
235
288
  * is unknown — is rotated first. The session has to belong to
236
289
  * `expectedPrincipalIdArg`, so a credential is never evaluated with another
237
290
  * person's tokens.
291
+ *
292
+ * With `rejectedAccessToken` (`IAuthorityTokenSource`) the call rotates the
293
+ * session's token whatever is stored, and only a rotation that it starts
294
+ * marks its answer `rotatedAfterRejection`: one that started before it, in
295
+ * this process or another replica's, says nothing about the rejection, so
296
+ * the call awaits it and throws `authority_unavailable`. A refused rotation
297
+ * ends the session (`refresh_token_refused`) and throws
298
+ * `authority_login_required`. A session without a refresh token has nothing
299
+ * to rotate with: it throws `authority_unavailable`, or ends as
300
+ * `access_token_unusable` when its token is dead.
301
+ */
302
+ liveProviderToken(sessionIdArg: string, expectedPrincipalIdArg: string, optionsArg?: {
303
+ rejectedAccessToken?: string;
304
+ }): Promise<IProviderToken>;
305
+ /**
306
+ * One rotation of the session in this process at a time. A forced call that
307
+ * finds one running waits for it, so a refusal still ends it, and is
308
+ * unavailable otherwise: that rotation started before the call.
238
309
  */
239
- liveProviderToken(sessionIdArg: string, expectedPrincipalIdArg: string): Promise<IProviderToken>;
240
310
  private refreshProviderToken;
241
311
  private runProviderRefresh;
242
312
  /**
@@ -246,6 +316,7 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
246
316
  */
247
317
  private tokenAfterForeignRefresh;
248
318
  private requireProvider;
319
+ private requireCookieSessions;
249
320
  private requireBearer;
250
321
  private checkLoginOptions;
251
322
  /** An unsaved attempt with its state, nonce and sealed verifier; the caller binds it and names where it returns. */
@@ -282,6 +353,19 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
282
353
  private endUnclaimedHandoff;
283
354
  /** Revokes a session that was never handed out and erases its sealed tokens. */
284
355
  private discardUnopenedSession;
356
+ /**
357
+ * Revokes a session not revoked yet and erases its sealed tokens; answers
358
+ * the document as it was before, `null` when it was over already. Its
359
+ * expiry comes forward to the revocation, never later, so the sweep finds
360
+ * an ended session by its expiry alone and `revokedAt` keeps when and that
361
+ * it was ended. Two conditional writes keep the earlier of the two times:
362
+ * the first ends a session within its expiry, the second one past it. A
363
+ * crash between them leaves an expired row unrevoked, which reads as over
364
+ * and is swept; a row revoked before 19.4.0 kept its later expiry, which is
365
+ * harmless because every reader checks `revokedAt` or reads live rows only,
366
+ * and it ages out.
367
+ */
368
+ private revokeSessionDocument;
285
369
  /**
286
370
  * Tells the application a session ended. Its hook never stands between the
287
371
  * local revocation and the provider revocation: a throw or a rejection is
@@ -326,6 +410,17 @@ export declare class RelyingPartySessions<TPrincipal extends object | undefined
326
410
  * sign-in.
327
411
  */
328
412
  private sweepBearerState;
413
+ /**
414
+ * Removes sessions an hour past their expiry, oldest first, one bounded
415
+ * page per login start and per session the application opens. An ended
416
+ * session's expiry is its revocation at the latest, so the expiry index
417
+ * finds expired and revoked sessions alike. Each of them ended already: the
418
+ * row goes without an end, a hook or a provider call. A row is deleted only
419
+ * while its expiry is still past the cutoff, so a row whose expiry moved
420
+ * after the page was read stays. A sweep that cannot run never blocks a
421
+ * sign-in.
422
+ */
423
+ private sweepEndedSessions;
329
424
  /**
330
425
  * Reports one diagnostic. Every cleanup step reports through here, so a
331
426
  * throwing sink must not end it: the event and the sink's failure go to