@_mustachio/openauth 0.13.3 → 0.15.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.
- package/dist/esm/client.js +61 -62
- package/dist/esm/domain/authorize.js +10 -0
- package/dist/esm/domain/callback.js +21 -19
- package/dist/esm/domain/client-credentials.js +16 -0
- package/dist/esm/domain/method-route.js +10 -0
- package/dist/esm/domain/refresh.js +16 -5
- package/dist/esm/domain/register.js +8 -2
- package/dist/esm/domain/state-envelope.js +19 -0
- package/dist/esm/domain/subject.js +35 -0
- package/dist/esm/domain/token.js +28 -0
- package/dist/esm/http/handlers/token.js +6 -0
- package/dist/esm/http/middleware/tenant.js +5 -20
- package/dist/esm/index.js +2 -0
- package/dist/types/client.d.ts +61 -40
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/domain/authorize.d.ts.map +1 -1
- package/dist/types/domain/callback.d.ts.map +1 -1
- package/dist/types/domain/client-credentials.d.ts +6 -2
- package/dist/types/domain/client-credentials.d.ts.map +1 -1
- package/dist/types/domain/method-route.d.ts.map +1 -1
- package/dist/types/domain/refresh.d.ts +3 -0
- package/dist/types/domain/refresh.d.ts.map +1 -1
- package/dist/types/domain/register.d.ts.map +1 -1
- package/dist/types/domain/state-envelope.d.ts +22 -0
- package/dist/types/domain/state-envelope.d.ts.map +1 -1
- package/dist/types/domain/subject.d.ts +48 -0
- package/dist/types/domain/subject.d.ts.map +1 -0
- package/dist/types/domain/token-exchange.d.ts +3 -1
- package/dist/types/domain/token-exchange.d.ts.map +1 -1
- package/dist/types/domain/token.d.ts +11 -2
- package/dist/types/domain/token.d.ts.map +1 -1
- package/dist/types/http/context.d.ts +6 -0
- package/dist/types/http/context.d.ts.map +1 -1
- package/dist/types/http/handlers/token.d.ts.map +1 -1
- package/dist/types/http/middleware/tenant.d.ts.map +1 -1
- package/dist/types/http/schemas/revocation.d.ts +4 -4
- package/dist/types/http/schemas/token.d.ts +12 -12
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/ports/audit-log.d.ts +25 -1
- package/dist/types/ports/audit-log.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +105 -38
- package/dist/types/types/idp.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +145 -129
- package/src/domain/authorize.ts +10 -0
- package/src/domain/callback.ts +22 -29
- package/src/domain/client-credentials.ts +27 -2
- package/src/domain/method-route.ts +10 -0
- package/src/domain/refresh.ts +42 -10
- package/src/domain/register.ts +27 -8
- package/src/domain/state-envelope.ts +40 -0
- package/src/domain/subject.ts +103 -0
- package/src/domain/token-exchange.ts +3 -1
- package/src/domain/token.ts +54 -2
- package/src/http/context.ts +6 -0
- package/src/http/handlers/token.ts +6 -0
- package/src/http/middleware/tenant.ts +5 -24
- package/src/index.ts +3 -2
- package/src/ports/audit-log.ts +26 -1
- package/src/types/idp.ts +107 -41
package/src/types/idp.ts
CHANGED
|
@@ -46,30 +46,6 @@ export type SuccessMapInput = {
|
|
|
46
46
|
context: Record<string, unknown> | null
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* Optional observation hook payload — fires after the subject claim has
|
|
51
|
-
* already been minted. **Does not** influence the issued subject; use it
|
|
52
|
-
* for audit, analytics, side effects only.
|
|
53
|
-
*/
|
|
54
|
-
export type SuccessEvent = SuccessMapInput & {
|
|
55
|
-
/** The final subject claim that became the JWT `sub`. */
|
|
56
|
-
claim: SubjectClaim
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Optional observation hook payload — fires on a failed auth attempt.
|
|
61
|
-
* Carries enough id information for operators to find the offending flow
|
|
62
|
-
* / config row without leaking secrets.
|
|
63
|
-
*/
|
|
64
|
-
export type FailureEvent = {
|
|
65
|
-
tenantId: TenantId | null
|
|
66
|
-
clientId: string | null
|
|
67
|
-
methodId?: string
|
|
68
|
-
methodKind?: string
|
|
69
|
-
flowId?: string
|
|
70
|
-
error: AuthError
|
|
71
|
-
}
|
|
72
|
-
|
|
73
49
|
/**
|
|
74
50
|
* Input to the optional `IdPOptions.onLogout` hook.
|
|
75
51
|
*
|
|
@@ -120,6 +96,63 @@ export type LogoutEventInput = {
|
|
|
120
96
|
*/
|
|
121
97
|
export type LogoutHookResult = { revokeSubject?: string } | void
|
|
122
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Fired when tokens are minted, naming the derived subject id.
|
|
101
|
+
*
|
|
102
|
+
* This is the only way a host can learn `subjectId` — the value the
|
|
103
|
+
* library signs as `sub`, and the key `TokenStore.revokeBySubject` and
|
|
104
|
+
* `revokeAllForSubject` take. It is derived inside the library from the
|
|
105
|
+
* claim and the receiving client's `sectorIdentifier`, so without this
|
|
106
|
+
* hook a host holds a revocation primitive it can never call, and
|
|
107
|
+
* `onLogout`'s `revokeSubject` has no map to resolve against.
|
|
108
|
+
*
|
|
109
|
+
* That matters because refresh rotation never re-consults the host:
|
|
110
|
+
* `refreshTokens` mints from the claim captured on the stored payload.
|
|
111
|
+
* Revoking the chain is the only way to stop an offboarded user's tokens
|
|
112
|
+
* from being renewed indefinitely.
|
|
113
|
+
*
|
|
114
|
+
* **A principal maps to many subject ids, not one.** Record them; do not
|
|
115
|
+
* overwrite. Two reasons, both silent if you assume otherwise:
|
|
116
|
+
*
|
|
117
|
+
* - **Pairwise** — with `ClientConfig.sectorIdentifier` set, derivation
|
|
118
|
+
* mixes the sector in (OIDC Core §8.1), so the same person has a
|
|
119
|
+
* distinct `sub` per sector. Revoking one leaves the others minting.
|
|
120
|
+
* - **Claim contents** — derivation hashes `claim.properties`. If your
|
|
121
|
+
* `success()` returns anything mutable (an email, a role), the subject
|
|
122
|
+
* id changes when that value changes, and chains issued under the old
|
|
123
|
+
* one survive. Returning a single immutable id is the way to avoid
|
|
124
|
+
* this; OIDC Core §2 wants `sub` never reassigned.
|
|
125
|
+
*
|
|
126
|
+
* Runs **before** the refresh token is persisted, so throwing aborts
|
|
127
|
+
* issuance with `server_error` and leaves no chain behind. That is
|
|
128
|
+
* deliberate: recording the mapping after the token is durable would let
|
|
129
|
+
* a hook failure produce exactly the unrevokable token this exists to
|
|
130
|
+
* prevent. Like `success` and `persistUpstreamTokens`, and unlike
|
|
131
|
+
* `AuditLog`, a failure here stops the grant.
|
|
132
|
+
*/
|
|
133
|
+
export type OnTokenIssued = (input: {
|
|
134
|
+
tenant: TenantContext
|
|
135
|
+
/** Client the tokens were minted for. */
|
|
136
|
+
clientId: string
|
|
137
|
+
/**
|
|
138
|
+
* The `sub` of the issued access token — the key `revokeBySubject` and
|
|
139
|
+
* `revokeAllForSubject` take.
|
|
140
|
+
*/
|
|
141
|
+
subjectId: string
|
|
142
|
+
/**
|
|
143
|
+
* The claim your `success()` (or token-exchange audience mapping)
|
|
144
|
+
* returned, already validated against `subjects`. Use it to attribute
|
|
145
|
+
* `subjectId` to one of your own principals.
|
|
146
|
+
*/
|
|
147
|
+
claim: SubjectClaim
|
|
148
|
+
/**
|
|
149
|
+
* Refresh-token family, for `TokenStore.revokeFamily`. Absent when the
|
|
150
|
+
* grant issued no refresh token (`client_credentials`, RFC 6749
|
|
151
|
+
* §4.4.3), because then there is no chain to revoke.
|
|
152
|
+
*/
|
|
153
|
+
family?: string
|
|
154
|
+
}) => Promise<void> | void
|
|
155
|
+
|
|
123
156
|
/**
|
|
124
157
|
* Optional hook called at `/token` time, after PKCE has succeeded and
|
|
125
158
|
* after the `success` callback has produced a `SubjectClaim`, but
|
|
@@ -217,19 +250,41 @@ export type RegisterClientResponse = {
|
|
|
217
250
|
/**
|
|
218
251
|
* Optional Dynamic Client Registration hook. Hosts that want to expose
|
|
219
252
|
* RFC 7591 client provisioning supply this; the framework validates the
|
|
220
|
-
* wire format, then defers persistence to the
|
|
221
|
-
* `/register` endpoint returns `invalid_request` so
|
|
222
|
-
* "not enabled" signal rather than a 404.
|
|
253
|
+
* wire format and mints credentials, then defers **persistence** to the
|
|
254
|
+
* host. If absent, the `/register` endpoint returns `invalid_request` so
|
|
255
|
+
* RPs receive a clear "not enabled" signal rather than a 404.
|
|
256
|
+
*
|
|
257
|
+
* The library owns credential generation — entropy, hashing, and the
|
|
258
|
+
* `ClientConfig` discriminated union, which requires `pkceRequired: true`
|
|
259
|
+
* as a literal on public clients and a `secretHash` on confidential ones.
|
|
260
|
+
* Those are protocol and security concerns, and making every host
|
|
261
|
+
* reimplement them is how they get done wrong. The host owns the table:
|
|
262
|
+
* write `client` through your own `ConfigStore` and return it.
|
|
263
|
+
*
|
|
264
|
+
* Before 0.14.0 this hook received only `{ tenant, request }` and the
|
|
265
|
+
* framework discarded what it had generated, so hosts had to mint their
|
|
266
|
+
* own — contradicting both this doc comment and `ARCHITECTURE.md`.
|
|
223
267
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* `RegisterClientResponse` so the RP can record it.
|
|
268
|
+
* Return the config you actually persisted. Adjusting it first is fine
|
|
269
|
+
* (narrowing `scopes`, substituting your own `id`); if you replace `id`
|
|
270
|
+
* or `secretHash`, return the matching plaintext as `secret` so the RP
|
|
271
|
+
* receives something that works.
|
|
229
272
|
*/
|
|
230
273
|
export type RegisterClient = (input: {
|
|
231
274
|
tenant: TenantContext
|
|
232
275
|
request: RegisterClientRequest
|
|
276
|
+
/**
|
|
277
|
+
* Framework-minted `ClientConfig`, ready to persist as-is. Public
|
|
278
|
+
* clients carry `pkceRequired: true`; confidential clients carry
|
|
279
|
+
* `secretHash` for `secret` below.
|
|
280
|
+
*/
|
|
281
|
+
client: ClientConfig
|
|
282
|
+
/**
|
|
283
|
+
* Plaintext secret matching `client.secretHash`. Present only for
|
|
284
|
+
* confidential clients. Return it in the result so the RP can record
|
|
285
|
+
* it — this is the only time it exists.
|
|
286
|
+
*/
|
|
287
|
+
secret?: string
|
|
233
288
|
}) => Promise<Result<{ client: ClientConfig; secret?: string }, AuthError>>
|
|
234
289
|
|
|
235
290
|
/**
|
|
@@ -328,12 +383,6 @@ export type IdPOptions = {
|
|
|
328
383
|
|
|
329
384
|
theme?: ThemeConfig
|
|
330
385
|
|
|
331
|
-
hooks?: {
|
|
332
|
-
/** Observation only — does NOT influence the subject. */
|
|
333
|
-
onSuccess?: (event: SuccessEvent) => Promise<void>
|
|
334
|
-
onFailure?: (event: FailureEvent) => Promise<void>
|
|
335
|
-
}
|
|
336
|
-
|
|
337
386
|
/**
|
|
338
387
|
* Optional hook fired when an upstream provider signals that a
|
|
339
388
|
* federated session ended — SAML front-channel Single Logout today.
|
|
@@ -342,9 +391,9 @@ export type IdPOptions = {
|
|
|
342
391
|
* OIDC subject (if any) whose library-issued tokens to revoke. See
|
|
343
392
|
* the `LogoutEventInput` / `LogoutHookResult` type docs.
|
|
344
393
|
*
|
|
345
|
-
* Unlike `
|
|
346
|
-
*
|
|
347
|
-
*
|
|
394
|
+
* Unlike `AuditLog` (observation only) this hook **influences**
|
|
395
|
+
* library behaviour — its return drives token revocation — so it sits
|
|
396
|
+
* at the top level alongside `success`.
|
|
348
397
|
*
|
|
349
398
|
* Absent ⇒ the library still verifies the logout, emits a
|
|
350
399
|
* `session_logout` audit event, and returns the protocol
|
|
@@ -364,6 +413,23 @@ export type IdPOptions = {
|
|
|
364
413
|
*/
|
|
365
414
|
persistUpstreamTokens?: PersistUpstreamTokens
|
|
366
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Optional hook fired as tokens are minted, carrying the derived
|
|
418
|
+
* `subjectId` alongside the claim it came from.
|
|
419
|
+
*
|
|
420
|
+
* Supply this if you need to revoke a user's tokens later — on
|
|
421
|
+
* offboarding, deactivation, or a membership change. `subjectId` is
|
|
422
|
+
* computed inside the library and appears nowhere else, so without
|
|
423
|
+
* this hook `TokenStore.revokeBySubject` / `revokeAllForSubject` are
|
|
424
|
+
* uncallable and `onLogout`'s `revokeSubject` has nothing to resolve
|
|
425
|
+
* against. See {@link OnTokenIssued} — in particular, one principal
|
|
426
|
+
* has many subject ids and they must be accumulated, not overwritten.
|
|
427
|
+
*
|
|
428
|
+
* Throwing aborts the grant; it runs before the refresh token is
|
|
429
|
+
* persisted so no unrecorded chain can survive a failure.
|
|
430
|
+
*/
|
|
431
|
+
onTokenIssued?: OnTokenIssued
|
|
432
|
+
|
|
367
433
|
/**
|
|
368
434
|
* Optional RFC 8693 token-exchange hook. If absent, exchange
|
|
369
435
|
* requests at `/token` return `unsupported_grant_type`. See the
|