@aooth/auth 0.1.47 → 0.1.48
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/atscript-db.cjs +3 -1
- package/dist/atscript-db.d.cts +4 -3
- package/dist/atscript-db.d.mts +4 -3
- package/dist/atscript-db.mjs +3 -1
- package/dist/auth-credential-C3qebKuy.d.mts +245 -0
- package/dist/auth-credential-CEijxoGU.d.cts +245 -0
- package/dist/authz.cjs +124 -35
- package/dist/authz.d.cts +78 -24
- package/dist/authz.d.mts +78 -24
- package/dist/authz.mjs +121 -37
- package/dist/{store-BG6m6oSJ.d.mts → clock-DJ_eroHW.d.cts} +50 -1
- package/dist/{store-BG6m6oSJ.d.cts → clock-DJ_eroHW.d.mts} +50 -1
- package/dist/{dynamic-client-store-DLRfntHr.mjs → dynamic-client-store-B1JwFHdP.mjs} +1 -0
- package/dist/{dynamic-client-store-DzqdUwnw.d.cts → dynamic-client-store-DJgtMjPw.d.mts} +58 -9
- package/dist/{dynamic-client-store-oHEsQaJg.d.mts → dynamic-client-store-DbiSLOj0.d.cts} +58 -9
- package/dist/{dynamic-client-store-DAStgv2j.cjs → dynamic-client-store-DykM9QOT.cjs} +1 -0
- package/dist/index.cjs +39 -26
- package/dist/index.d.cts +12 -218
- package/dist/index.d.mts +12 -218
- package/dist/index.mjs +39 -27
- package/dist/opaque-token-BaMOrC1A.mjs +13 -0
- package/dist/opaque-token-CaZgD2i6.cjs +18 -0
- package/dist/redis.d.cts +2 -2
- package/dist/redis.d.mts +2 -2
- package/dist/{store-N9daSmHS.d.cts → store-BYnpA7VK.d.cts} +1 -1
- package/dist/{store-jFgby4bK.d.mts → store-pKh0ZaGG.d.mts} +1 -1
- package/package.json +2 -2
- package/src/atscript-db/auth-credential.as +16 -0
- package/src/atscript-db/auth-credential.as.d.ts +4 -1
- package/src/atscript-db/dynamic-client.as +11 -1
- package/src/atscript-db/dynamic-client.as.d.ts +3 -0
- package/dist/clock-BjXa0LXb.d.cts +0 -14
- package/dist/clock-BjXa0LXb.d.mts +0 -14
package/dist/authz.mjs
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
import { t as defaultClock } from "./clock-Bdsep_1j.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { t as generateOpaqueToken } from "./opaque-token-BaMOrC1A.mjs";
|
|
3
|
+
import { c as PendingAuthorizationStoreMemory, i as AuthCodeStoreMemory, n as DynamicClientStoreMemory, o as DEFAULT_PENDING_TTL_MS, r as AuthCodeStore, s as PendingAuthorizationStore, t as DynamicClientStore } from "./dynamic-client-store-B1JwFHdP.mjs";
|
|
4
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
4
5
|
import { SignJWT, exportJWK, importPKCS8, importSPKI } from "jose";
|
|
6
|
+
//#region src/authz/token-policy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Default refresh-token (grant) lifetime for {@link TokenPolicy.refresh} when
|
|
9
|
+
* no `ttl` is given: 30 days. Deliberately independent of the session tier's
|
|
10
|
+
* `AuthCredentialOptions.refresh.ttl` — an authz grant's lifetime is the
|
|
11
|
+
* policy's decision, never inherited from browser-session posture.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_AUTHZ_REFRESH_TTL_MS = 720 * 60 * 6e4;
|
|
14
|
+
/**
|
|
15
|
+
* Flatten a {@link TokenPolicy} into the `AuthCredential.issue()` options a
|
|
16
|
+
* token endpoint forwards. The refresh dimension applies only to client-bound
|
|
17
|
+
* grants — the family is stamped with `metadata.authzClientId`, the binding
|
|
18
|
+
* the `refresh_token` grant enforces — so a clientless (loopback) grant
|
|
19
|
+
* ignores `policy.refresh`. `refresh: false` otherwise: an authz mint never
|
|
20
|
+
* rides the instance-level session refresh config (no orphaned refresh row
|
|
21
|
+
* for a policy that didn't opt in). Lives HERE, next to the policy type and
|
|
22
|
+
* its default ttl, so every HTTP adapter applies the same semantics.
|
|
23
|
+
*/
|
|
24
|
+
function tokenPolicyToIssueOptions(policy, clientId) {
|
|
25
|
+
const withRefresh = policy.refresh !== void 0 && clientId !== void 0;
|
|
26
|
+
return {
|
|
27
|
+
...policy.payload,
|
|
28
|
+
...policy.kind !== void 0 && { kind: policy.kind },
|
|
29
|
+
...policy.ttl !== void 0 && { ttl: policy.ttl },
|
|
30
|
+
refresh: withRefresh ? { ttl: policy.refresh?.ttl ?? 2592e6 } : false,
|
|
31
|
+
...withRefresh && { metadata: { authzClientId: clientId } }
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
5
35
|
//#region src/authz/authz-errors.ts
|
|
6
36
|
/** A typed authorization-server failure. */
|
|
7
37
|
var AuthorizeError = class extends Error {
|
|
@@ -59,6 +89,15 @@ var LoopbackClientPolicy = class {
|
|
|
59
89
|
}
|
|
60
90
|
};
|
|
61
91
|
//#endregion
|
|
92
|
+
//#region src/utils/timing-safe.ts
|
|
93
|
+
/** Constant-time string compare that also fails closed on a length mismatch. */
|
|
94
|
+
function timingSafeEqualStr(a, b) {
|
|
95
|
+
const ab = Buffer.from(a, "utf8");
|
|
96
|
+
const bb = Buffer.from(b, "utf8");
|
|
97
|
+
if (ab.length !== bb.length) return false;
|
|
98
|
+
return timingSafeEqual(ab, bb);
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
62
101
|
//#region src/authz/oidc-claims-resolver.ts
|
|
63
102
|
/**
|
|
64
103
|
* Resolves the OIDC profile claims to embed in an `id_token` for a given user +
|
|
@@ -156,13 +195,6 @@ var RegisteredClientPolicy = class {
|
|
|
156
195
|
return granted.length > 0 ? granted.join(" ") : void 0;
|
|
157
196
|
}
|
|
158
197
|
};
|
|
159
|
-
/** Constant-time string compare that also fails closed on a length mismatch. */
|
|
160
|
-
function timingSafeEqualStr(a, b) {
|
|
161
|
-
const ab = Buffer.from(a, "utf8");
|
|
162
|
-
const bb = Buffer.from(b, "utf8");
|
|
163
|
-
if (ab.length !== bb.length) return false;
|
|
164
|
-
return timingSafeEqual(ab, bb);
|
|
165
|
-
}
|
|
166
198
|
//#endregion
|
|
167
199
|
//#region src/authz/composite-client-policy.ts
|
|
168
200
|
/**
|
|
@@ -212,6 +244,30 @@ var CompositeClientPolicy = class {
|
|
|
212
244
|
}
|
|
213
245
|
};
|
|
214
246
|
//#endregion
|
|
247
|
+
//#region src/authz/client-secret.ts
|
|
248
|
+
/**
|
|
249
|
+
* Mint a confidential dynamic client's secret (RFC 7591 `client_secret`) — a
|
|
250
|
+
* plain {@link generateOpaqueToken} mint. Returned to the registrant ONCE in
|
|
251
|
+
* the registration response; only its {@link hashClientSecret} digest is
|
|
252
|
+
* stored.
|
|
253
|
+
*/
|
|
254
|
+
function mintClientSecret() {
|
|
255
|
+
return generateOpaqueToken();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Storage digest for a client secret — plain SHA-256 (hex). No KDF/salt on
|
|
259
|
+
* purpose: the secret is high-entropy server-minted material (never a human
|
|
260
|
+
* password), so brute-forcing the digest is infeasible and a per-row salt
|
|
261
|
+
* buys nothing.
|
|
262
|
+
*/
|
|
263
|
+
function hashClientSecret(secret) {
|
|
264
|
+
return createHash("sha256").update(secret, "utf8").digest("hex");
|
|
265
|
+
}
|
|
266
|
+
/** Constant-time check of a presented secret against a stored digest. */
|
|
267
|
+
function verifyClientSecret(secret, hash) {
|
|
268
|
+
return timingSafeEqualStr(hashClientSecret(secret), hash);
|
|
269
|
+
}
|
|
270
|
+
//#endregion
|
|
215
271
|
//#region src/authz/client-registration.ts
|
|
216
272
|
/** A typed RFC 7591 registration failure; `message` becomes `error_description`. */
|
|
217
273
|
var ClientRegistrationError = class extends Error {
|
|
@@ -222,9 +278,11 @@ var ClientRegistrationError = class extends Error {
|
|
|
222
278
|
this.code = code;
|
|
223
279
|
}
|
|
224
280
|
};
|
|
225
|
-
/** Grant/response types the
|
|
226
|
-
const SUPPORTED_GRANT_TYPES = ["authorization_code"];
|
|
281
|
+
/** Grant/response types the authorization server supports (auth-code + PKCE, plus refresh). */
|
|
282
|
+
const SUPPORTED_GRANT_TYPES = ["authorization_code", "refresh_token"];
|
|
227
283
|
const SUPPORTED_RESPONSE_TYPES = ["code"];
|
|
284
|
+
/** Token-endpoint auth methods DCR accepts (both advertised in the RFC 8414 document). */
|
|
285
|
+
const SUPPORTED_TOKEN_ENDPOINT_AUTH_METHODS = ["none", "client_secret_post"];
|
|
228
286
|
/** RFC 6749 §3.3 scope-token charset: printable ASCII minus space, `"` and `\`. */
|
|
229
287
|
const SCOPE_TOKEN_RE = /^[\x21\x23-\x5B\x5D-\x7E]+$/u;
|
|
230
288
|
const DEFAULT_MAX_REDIRECT_URIS = 5;
|
|
@@ -273,13 +331,14 @@ function isAcceptableRedirectUri(uri) {
|
|
|
273
331
|
* Normalization is allowed by RFC 7591 §2 (the server MAY replace requested
|
|
274
332
|
* metadata with its own values) and is load-bearing for real connectors:
|
|
275
333
|
* `grant_types` / `response_types` are INTERSECTED with what the server
|
|
276
|
-
* supports (
|
|
277
|
-
*
|
|
278
|
-
* is the
|
|
279
|
-
*
|
|
280
|
-
* `
|
|
281
|
-
*
|
|
282
|
-
*
|
|
334
|
+
* supports (an unsupported grant is dropped from the echo — the 201 echo of
|
|
335
|
+
* the narrowed set is the contract) rather than rejected, but the result must
|
|
336
|
+
* still include `authorization_code` (it is the only way to establish a grant;
|
|
337
|
+
* `refresh_token` alone mints nothing). `token_endpoint_auth_method` defaults
|
|
338
|
+
* to `"none"` when absent (the RFC's `client_secret_basic` default is
|
|
339
|
+
* unsupported — secrets travel in the POST form body); an EXPLICIT ask outside
|
|
340
|
+
* the allowed set is rejected — never silently downgrade a client that asked
|
|
341
|
+
* for a secret. Unknown fields are ignored and never echoed.
|
|
283
342
|
*/
|
|
284
343
|
function validateClientRegistration(body, opts) {
|
|
285
344
|
if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ClientRegistrationError("invalid_client_metadata", "registration request must be a JSON object");
|
|
@@ -291,9 +350,11 @@ function validateClientRegistration(body, opts) {
|
|
|
291
350
|
if (redirectUris.length === 0) throw new ClientRegistrationError("invalid_redirect_uri", "redirect_uris must be non-empty");
|
|
292
351
|
if (redirectUris.length > maxUris) throw new ClientRegistrationError("invalid_redirect_uri", `redirect_uris accepts at most ${maxUris} entries`);
|
|
293
352
|
for (const uri of redirectUris) if (uri.length > maxUriLength || !isAcceptableRedirectUri(uri)) throw new ClientRegistrationError("invalid_redirect_uri", "each redirect_uri must be an https URL with an explicit host or a loopback literal, without a fragment");
|
|
294
|
-
|
|
353
|
+
const allowedAuthMethods = (opts?.allowedTokenEndpointAuthMethods ?? SUPPORTED_TOKEN_ENDPOINT_AUTH_METHODS).filter((m) => SUPPORTED_TOKEN_ENDPOINT_AUTH_METHODS.includes(m));
|
|
354
|
+
const authMethod = req.token_endpoint_auth_method ?? "none";
|
|
355
|
+
if (typeof authMethod !== "string" || !allowedAuthMethods.includes(authMethod)) throw new ClientRegistrationError("invalid_client_metadata", `only token_endpoint_auth_method ${allowedAuthMethods.map((m) => `"${m}"`).join(" / ")} is supported`);
|
|
295
356
|
const grantTypes = req.grant_types === void 0 ? [...SUPPORTED_GRANT_TYPES] : asStringArray(req.grant_types, "grant_types").filter((g) => SUPPORTED_GRANT_TYPES.includes(g));
|
|
296
|
-
if (grantTypes.
|
|
357
|
+
if (!grantTypes.includes("authorization_code")) throw new ClientRegistrationError("invalid_client_metadata", "grant_types must include authorization_code");
|
|
297
358
|
const responseTypes = req.response_types === void 0 ? [...SUPPORTED_RESPONSE_TYPES] : asStringArray(req.response_types, "response_types").filter((r) => SUPPORTED_RESPONSE_TYPES.includes(r));
|
|
298
359
|
if (responseTypes.length === 0) throw new ClientRegistrationError("invalid_client_metadata", "response_types must include code");
|
|
299
360
|
let clientName;
|
|
@@ -312,7 +373,7 @@ function validateClientRegistration(body, opts) {
|
|
|
312
373
|
}
|
|
313
374
|
return {
|
|
314
375
|
redirectUris,
|
|
315
|
-
tokenEndpointAuthMethod:
|
|
376
|
+
tokenEndpointAuthMethod: authMethod,
|
|
316
377
|
grantTypes,
|
|
317
378
|
responseTypes,
|
|
318
379
|
...clientName !== void 0 && { clientName },
|
|
@@ -323,9 +384,9 @@ const DEFAULT_MAX_CLIENTS = 1e3;
|
|
|
323
384
|
/**
|
|
324
385
|
* The RFC 7591 registration operation behind `POST {issuer}/register`
|
|
325
386
|
* (OAUTH.md R2): validate → guard → lazy GC of never-used rows → hard cap →
|
|
326
|
-
* persist. Framework-free — `@aooth/auth-moost`'s
|
|
327
|
-
* thin HTTP adapter over `register()`, and non-moost
|
|
328
|
-
* directly.
|
|
387
|
+
* mint secret (confidential) → persist. Framework-free — `@aooth/auth-moost`'s
|
|
388
|
+
* controller endpoint is a thin HTTP adapter over `register()`, and non-moost
|
|
389
|
+
* servers can call it directly.
|
|
329
390
|
*/
|
|
330
391
|
var DynamicClientRegistration = class {
|
|
331
392
|
store;
|
|
@@ -342,12 +403,28 @@ var DynamicClientRegistration = class {
|
|
|
342
403
|
this.validation = opts.validation;
|
|
343
404
|
this.clock = opts.clock ?? defaultClock;
|
|
344
405
|
}
|
|
345
|
-
/**
|
|
406
|
+
/**
|
|
407
|
+
* Validate and persist a registration request body; returns the minted
|
|
408
|
+
* client. For a `client_secret_post` registration the returned record
|
|
409
|
+
* additionally carries the plaintext `clientSecret` (the ONE disclosure —
|
|
410
|
+
* only its SHA-256 digest is stored).
|
|
411
|
+
*/
|
|
346
412
|
async register(body) {
|
|
347
413
|
const metadata = validateClientRegistration(body, this.validation);
|
|
348
414
|
await this.guard?.({ metadata });
|
|
349
415
|
if (this.unusedClientTtlMs !== void 0) await this.store.deleteUnusedBefore(this.clock.now() - this.unusedClientTtlMs);
|
|
350
416
|
if (await this.store.count() >= this.maxClients) throw new ClientRegistrationError("invalid_client_metadata", "registration limit reached");
|
|
417
|
+
if (metadata.tokenEndpointAuthMethod === "client_secret_post") {
|
|
418
|
+
const clientSecret = mintClientSecret();
|
|
419
|
+
return {
|
|
420
|
+
...await this.store.create({
|
|
421
|
+
...metadata,
|
|
422
|
+
clientSecretHash: hashClientSecret(clientSecret)
|
|
423
|
+
}),
|
|
424
|
+
clientSecret,
|
|
425
|
+
clientSecretExpiresAt: 0
|
|
426
|
+
};
|
|
427
|
+
}
|
|
351
428
|
return this.store.create(metadata);
|
|
352
429
|
}
|
|
353
430
|
};
|
|
@@ -358,13 +435,14 @@ const DEFAULT_DYNAMIC_TOKEN_POLICY = {
|
|
|
358
435
|
ttl: 720 * 60 * 6e4
|
|
359
436
|
};
|
|
360
437
|
/**
|
|
361
|
-
* Policy for RFC 7591 dynamically-registered
|
|
362
|
-
*
|
|
438
|
+
* Policy for RFC 7591 dynamically-registered clients (OAUTH.md R2) on the
|
|
439
|
+
* `ClientRedirectPolicy` seam. `resolveClient` authorizes the client +
|
|
363
440
|
* `redirect_uri` against ITS registered allowlist and resolves the granted
|
|
364
441
|
* scope + token policy; `authenticateClient` re-checks existence at `/token`
|
|
365
|
-
* (
|
|
366
|
-
*
|
|
367
|
-
*
|
|
442
|
+
* (a registration garbage-collected mid-flight fails closed) and, for a
|
|
443
|
+
* `client_secret_post` registration, validates the presented secret against
|
|
444
|
+
* the stored digest (public `"none"` clients: PKCE is the binding). Dynamic
|
|
445
|
+
* clients receive an access token only — NO `id_token` in v1 (OAUTH.md R6).
|
|
368
446
|
*
|
|
369
447
|
* INVARIANT: the returned {@link ResolvedClient} always carries `clientId`, so
|
|
370
448
|
* the minted code records it and the token endpoint's symmetric client binding
|
|
@@ -400,12 +478,16 @@ var DynamicClientPolicy = class {
|
|
|
400
478
|
}
|
|
401
479
|
/**
|
|
402
480
|
* `/token`-side check: the client must still exist (fail closed when the
|
|
403
|
-
* registration was deleted/GC'd between authorize and redemption).
|
|
404
|
-
*
|
|
405
|
-
* `client_secret`
|
|
481
|
+
* registration was deleted/GC'd between authorize and redemption). A client
|
|
482
|
+
* registered with `token_endpoint_auth_method: "client_secret_post"` must
|
|
483
|
+
* additionally present its minted `client_secret` (constant-time check
|
|
484
|
+
* against the stored digest). For a public (`"none"`) client no secret is
|
|
485
|
+
* checked — PKCE is the binding; a spurious `client_secret` is ignored.
|
|
406
486
|
*/
|
|
407
487
|
async authenticateClient(args) {
|
|
408
|
-
await this.requireClient(args.clientId);
|
|
488
|
+
const client = await this.requireClient(args.clientId);
|
|
489
|
+
if (client.tokenEndpointAuthMethod !== "client_secret_post") return;
|
|
490
|
+
if (client.clientSecretHash === void 0 || args.clientSecret === void 0 || !verifyClientSecret(args.clientSecret, client.clientSecretHash)) throw new AuthorizeError("invalid_client", "client authentication failed");
|
|
409
491
|
}
|
|
410
492
|
/** Known-ness probe for `CompositeClientPolicy` dispatch. */
|
|
411
493
|
async hasClient(clientId) {
|
|
@@ -536,7 +618,9 @@ function canonicalizeIssuer(issuer) {
|
|
|
536
618
|
*
|
|
537
619
|
* Capability fields are fixed to what the server actually implements:
|
|
538
620
|
* `response_types_supported` `["code"]`, `grant_types_supported`
|
|
539
|
-
* `["authorization_code"
|
|
621
|
+
* `["authorization_code", "refresh_token"]` (the grant is implemented server-
|
|
622
|
+
* wide; whether a given client's grant mints a refresh token is per-policy —
|
|
623
|
+
* `TokenPolicy.refresh`), `code_challenge_methods_supported` `["S256"]`
|
|
540
624
|
* (PKCE is mandatory — it is the binding for public clients). Optional fields
|
|
541
625
|
* are omitted entirely (no `undefined` keys) so the serialized JSON carries
|
|
542
626
|
* only what was configured.
|
|
@@ -551,7 +635,7 @@ function buildAuthorizationServerMetadata(opts) {
|
|
|
551
635
|
...opts.registrationEndpoint !== void 0 && { registration_endpoint: opts.registrationEndpoint },
|
|
552
636
|
...opts.jwksUri !== void 0 && { jwks_uri: opts.jwksUri },
|
|
553
637
|
response_types_supported: ["code"],
|
|
554
|
-
grant_types_supported: ["authorization_code"],
|
|
638
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
555
639
|
code_challenge_methods_supported: ["S256"],
|
|
556
640
|
token_endpoint_auth_methods_supported: authMethods.includes("none") ? authMethods : ["none", ...authMethods],
|
|
557
641
|
...opts.scopesSupported !== void 0 && { scopes_supported: opts.scopesSupported }
|
|
@@ -606,4 +690,4 @@ function buildWwwAuthenticateBearerChallenge(opts) {
|
|
|
606
690
|
return params.length === 0 ? "Bearer" : `Bearer ${params.join(", ")}`;
|
|
607
691
|
}
|
|
608
692
|
//#endregion
|
|
609
|
-
export { AuthCodeStore, AuthCodeStoreMemory, AuthorizeError, ClientRegistrationError, CompositeClientPolicy, DEFAULT_PENDING_TTL_MS, DynamicClientPolicy, DynamicClientRegistration, DynamicClientStore, DynamicClientStoreMemory, IdTokenSigner, LoopbackClientPolicy, NoopOidcClaimsResolver, OidcClaimsResolver, PendingAuthorizationStore, PendingAuthorizationStoreMemory, RegisteredClientPolicy, buildAuthorizationServerMetadata, buildProtectedResourceMetadata, buildWwwAuthenticateBearerChallenge, canonicalizeIssuer, isLoopbackRedirectUri, scopeGrants, validateClientRegistration };
|
|
693
|
+
export { AuthCodeStore, AuthCodeStoreMemory, AuthorizeError, ClientRegistrationError, CompositeClientPolicy, DEFAULT_AUTHZ_REFRESH_TTL_MS, DEFAULT_PENDING_TTL_MS, DynamicClientPolicy, DynamicClientRegistration, DynamicClientStore, DynamicClientStoreMemory, IdTokenSigner, LoopbackClientPolicy, NoopOidcClaimsResolver, OidcClaimsResolver, PendingAuthorizationStore, PendingAuthorizationStoreMemory, RegisteredClientPolicy, buildAuthorizationServerMetadata, buildProtectedResourceMetadata, buildWwwAuthenticateBearerChallenge, canonicalizeIssuer, hashClientSecret, isLoopbackRedirectUri, mintClientSecret, scopeGrants, tokenPolicyToIssueOptions, validateClientRegistration, verifyClientSecret };
|
|
@@ -50,6 +50,34 @@ interface CredentialMetadata {
|
|
|
50
50
|
* the `listSessions({ kind })` filter. Absent ⇒ an ordinary interactive session.
|
|
51
51
|
*/
|
|
52
52
|
credentialKind?: string;
|
|
53
|
+
/**
|
|
54
|
+
* OAuth `client_id` this token family was minted for by the authorization
|
|
55
|
+
* server's token endpoint (`@aooth/auth-moost`'s `AuthorizeController`) —
|
|
56
|
+
* the binding the `refresh_token` grant enforces: a refresh token redeems
|
|
57
|
+
* only for the client it was issued to, and a family WITHOUT this stamp
|
|
58
|
+
* (e.g. a browser session) is never redeemable at the OAuth token endpoint.
|
|
59
|
+
* Written by the authz tier, carried across rotation like all metadata.
|
|
60
|
+
*/
|
|
61
|
+
authzClientId?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Per-family access-token lifetime in ms — stamped by `issue()` when a
|
|
64
|
+
* refresh token is minted with a per-mint `ttl` override, and consulted on
|
|
65
|
+
* every refresh so rotated access tokens keep the authority fixed at mint
|
|
66
|
+
* time (a 15-min-policy token must not come back as the instance default
|
|
67
|
+
* after a refresh). Absent ⇒ refresh mints with the instance `accessTtl`.
|
|
68
|
+
*/
|
|
69
|
+
accessTtl?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Per-family rotation semantics — stamped `"always"` by `issue()` whenever a
|
|
72
|
+
* refresh token is minted via the per-mint `IssueOptions.refresh` object, and
|
|
73
|
+
* honored by `refresh()` OVER the instance config. Same mint-time-authority
|
|
74
|
+
* mechanism as {@link accessTtl}: a per-mint family (e.g. an OAuth grant with
|
|
75
|
+
* a 60-day ceiling) keeps its fixed ceiling even on an instance whose
|
|
76
|
+
* browser sessions rotate `'sliding'` — rotation must never extend the
|
|
77
|
+
* grant's lifetime. Absent ⇒ the instance rotation applies (ordinary
|
|
78
|
+
* sessions are unaffected).
|
|
79
|
+
*/
|
|
80
|
+
refreshRotation?: "none" | "always" | "sliding";
|
|
53
81
|
}
|
|
54
82
|
/**
|
|
55
83
|
* Persisted state of a credential — the fixed **envelope**. A consumer's
|
|
@@ -145,6 +173,14 @@ interface IssueResult {
|
|
|
145
173
|
accessExpiresAt: number;
|
|
146
174
|
refreshExpiresAt?: number;
|
|
147
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Result of redeeming a refresh token: the fresh pair plus the id of the user
|
|
178
|
+
* whose family was rotated — the caller (e.g. the OAuth `refresh_token` grant)
|
|
179
|
+
* presented only an opaque token, so this is where it learns the subject.
|
|
180
|
+
*/
|
|
181
|
+
interface RefreshResult extends IssueResult {
|
|
182
|
+
userId: string;
|
|
183
|
+
}
|
|
148
184
|
/**
|
|
149
185
|
* Refresh token configuration.
|
|
150
186
|
*/
|
|
@@ -260,4 +296,17 @@ interface DenylistStore {
|
|
|
260
296
|
cleanup(): Promise<number>;
|
|
261
297
|
}
|
|
262
298
|
//#endregion
|
|
263
|
-
|
|
299
|
+
//#region src/utils/clock.d.ts
|
|
300
|
+
/**
|
|
301
|
+
* Minimal time abstraction shared across stores and orchestrators.
|
|
302
|
+
*
|
|
303
|
+
* Defaults to wall-clock; tests inject a fake clock to deterministically
|
|
304
|
+
* advance time. Kept tiny (single `now()` method) so any timing primitive
|
|
305
|
+
* — Date, performance.now-ish, monotonic, etc. — can be plugged in.
|
|
306
|
+
*/
|
|
307
|
+
interface Clock {
|
|
308
|
+
now(): number;
|
|
309
|
+
}
|
|
310
|
+
declare const defaultClock: Clock;
|
|
311
|
+
//#endregion
|
|
312
|
+
export { AuthContext as a, EnrichedSession as c, RefreshResult as d, SessionEnricher as f, DenylistStore as i, IssueResult as l, defaultClock as n, CredentialMetadata as o, SessionInfo as p, CredentialStore as r, CredentialState as s, Clock as t, RefreshConfig as u };
|
|
@@ -50,6 +50,34 @@ interface CredentialMetadata {
|
|
|
50
50
|
* the `listSessions({ kind })` filter. Absent ⇒ an ordinary interactive session.
|
|
51
51
|
*/
|
|
52
52
|
credentialKind?: string;
|
|
53
|
+
/**
|
|
54
|
+
* OAuth `client_id` this token family was minted for by the authorization
|
|
55
|
+
* server's token endpoint (`@aooth/auth-moost`'s `AuthorizeController`) —
|
|
56
|
+
* the binding the `refresh_token` grant enforces: a refresh token redeems
|
|
57
|
+
* only for the client it was issued to, and a family WITHOUT this stamp
|
|
58
|
+
* (e.g. a browser session) is never redeemable at the OAuth token endpoint.
|
|
59
|
+
* Written by the authz tier, carried across rotation like all metadata.
|
|
60
|
+
*/
|
|
61
|
+
authzClientId?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Per-family access-token lifetime in ms — stamped by `issue()` when a
|
|
64
|
+
* refresh token is minted with a per-mint `ttl` override, and consulted on
|
|
65
|
+
* every refresh so rotated access tokens keep the authority fixed at mint
|
|
66
|
+
* time (a 15-min-policy token must not come back as the instance default
|
|
67
|
+
* after a refresh). Absent ⇒ refresh mints with the instance `accessTtl`.
|
|
68
|
+
*/
|
|
69
|
+
accessTtl?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Per-family rotation semantics — stamped `"always"` by `issue()` whenever a
|
|
72
|
+
* refresh token is minted via the per-mint `IssueOptions.refresh` object, and
|
|
73
|
+
* honored by `refresh()` OVER the instance config. Same mint-time-authority
|
|
74
|
+
* mechanism as {@link accessTtl}: a per-mint family (e.g. an OAuth grant with
|
|
75
|
+
* a 60-day ceiling) keeps its fixed ceiling even on an instance whose
|
|
76
|
+
* browser sessions rotate `'sliding'` — rotation must never extend the
|
|
77
|
+
* grant's lifetime. Absent ⇒ the instance rotation applies (ordinary
|
|
78
|
+
* sessions are unaffected).
|
|
79
|
+
*/
|
|
80
|
+
refreshRotation?: "none" | "always" | "sliding";
|
|
53
81
|
}
|
|
54
82
|
/**
|
|
55
83
|
* Persisted state of a credential — the fixed **envelope**. A consumer's
|
|
@@ -145,6 +173,14 @@ interface IssueResult {
|
|
|
145
173
|
accessExpiresAt: number;
|
|
146
174
|
refreshExpiresAt?: number;
|
|
147
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Result of redeeming a refresh token: the fresh pair plus the id of the user
|
|
178
|
+
* whose family was rotated — the caller (e.g. the OAuth `refresh_token` grant)
|
|
179
|
+
* presented only an opaque token, so this is where it learns the subject.
|
|
180
|
+
*/
|
|
181
|
+
interface RefreshResult extends IssueResult {
|
|
182
|
+
userId: string;
|
|
183
|
+
}
|
|
148
184
|
/**
|
|
149
185
|
* Refresh token configuration.
|
|
150
186
|
*/
|
|
@@ -260,4 +296,17 @@ interface DenylistStore {
|
|
|
260
296
|
cleanup(): Promise<number>;
|
|
261
297
|
}
|
|
262
298
|
//#endregion
|
|
263
|
-
|
|
299
|
+
//#region src/utils/clock.d.ts
|
|
300
|
+
/**
|
|
301
|
+
* Minimal time abstraction shared across stores and orchestrators.
|
|
302
|
+
*
|
|
303
|
+
* Defaults to wall-clock; tests inject a fake clock to deterministically
|
|
304
|
+
* advance time. Kept tiny (single `now()` method) so any timing primitive
|
|
305
|
+
* — Date, performance.now-ish, monotonic, etc. — can be plugged in.
|
|
306
|
+
*/
|
|
307
|
+
interface Clock {
|
|
308
|
+
now(): number;
|
|
309
|
+
}
|
|
310
|
+
declare const defaultClock: Clock;
|
|
311
|
+
//#endregion
|
|
312
|
+
export { AuthContext as a, EnrichedSession as c, RefreshResult as d, SessionEnricher as f, DenylistStore as i, IssueResult as l, defaultClock as n, CredentialMetadata as o, SessionInfo as p, CredentialStore as r, CredentialState as s, Clock as t, RefreshConfig as u };
|
|
@@ -150,6 +150,7 @@ var DynamicClientStoreMemory = class extends DynamicClientStore {
|
|
|
150
150
|
responseTypes: [...rec.responseTypes],
|
|
151
151
|
createdAt: this.clock.now(),
|
|
152
152
|
...rec.clientName !== void 0 && { clientName: rec.clientName },
|
|
153
|
+
...rec.clientSecretHash !== void 0 && { clientSecretHash: rec.clientSecretHash },
|
|
153
154
|
...rec.scope !== void 0 && { scope: rec.scope }
|
|
154
155
|
};
|
|
155
156
|
this.store.set(row.clientId, structuredClone(row));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { t as Clock } from "./clock-
|
|
1
|
+
import { t as Clock } from "./clock-DJ_eroHW.mjs";
|
|
2
|
+
import { r as IssueOptions } from "./auth-credential-C3qebKuy.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/authz/token-policy.d.ts
|
|
4
5
|
/**
|
|
@@ -24,7 +25,41 @@ interface TokenPolicy {
|
|
|
24
25
|
* is persisted on the pending-authorization + auth-code records.
|
|
25
26
|
*/
|
|
26
27
|
payload?: Record<string, unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Opt-in refresh dimension (OAuth 2.1 `refresh_token` grant): when set, the
|
|
30
|
+
* token endpoint mints a rotating refresh token alongside the access token
|
|
31
|
+
* and redeems `grant_type=refresh_token` for the family. `ttl` is the
|
|
32
|
+
* refresh-token (i.e. grant) lifetime in ms — defaults to
|
|
33
|
+
* {@link DEFAULT_AUTHZ_REFRESH_TTL_MS} (30 days). Absent ⇒ today's behavior:
|
|
34
|
+
* access token only, re-consent at expiry.
|
|
35
|
+
*
|
|
36
|
+
* Honored only for grants bound to a registered `client_id` (Tier-2 /
|
|
37
|
+
* dynamic clients) — the family is stamped with `metadata.authzClientId` and
|
|
38
|
+
* a refresh token redeems only for that client. A clientless (Tier-1
|
|
39
|
+
* loopback) grant has nothing to bind to, so the flag is IGNORED there.
|
|
40
|
+
*/
|
|
41
|
+
refresh?: {
|
|
42
|
+
ttl?: number;
|
|
43
|
+
};
|
|
27
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Default refresh-token (grant) lifetime for {@link TokenPolicy.refresh} when
|
|
47
|
+
* no `ttl` is given: 30 days. Deliberately independent of the session tier's
|
|
48
|
+
* `AuthCredentialOptions.refresh.ttl` — an authz grant's lifetime is the
|
|
49
|
+
* policy's decision, never inherited from browser-session posture.
|
|
50
|
+
*/
|
|
51
|
+
declare const DEFAULT_AUTHZ_REFRESH_TTL_MS: number;
|
|
52
|
+
/**
|
|
53
|
+
* Flatten a {@link TokenPolicy} into the `AuthCredential.issue()` options a
|
|
54
|
+
* token endpoint forwards. The refresh dimension applies only to client-bound
|
|
55
|
+
* grants — the family is stamped with `metadata.authzClientId`, the binding
|
|
56
|
+
* the `refresh_token` grant enforces — so a clientless (loopback) grant
|
|
57
|
+
* ignores `policy.refresh`. `refresh: false` otherwise: an authz mint never
|
|
58
|
+
* rides the instance-level session refresh config (no orphaned refresh row
|
|
59
|
+
* for a policy that didn't opt in). Lives HERE, next to the policy type and
|
|
60
|
+
* its default ttl, so every HTTP adapter applies the same semantics.
|
|
61
|
+
*/
|
|
62
|
+
declare function tokenPolicyToIssueOptions(policy: TokenPolicy, clientId: string | undefined): IssueOptions;
|
|
28
63
|
//#endregion
|
|
29
64
|
//#region src/authz/pending-authorization-store.d.ts
|
|
30
65
|
/**
|
|
@@ -239,13 +274,15 @@ declare class AuthCodeStoreMemory extends AuthCodeStore {
|
|
|
239
274
|
}
|
|
240
275
|
//#endregion
|
|
241
276
|
//#region src/authz/dynamic-client-store.d.ts
|
|
277
|
+
/** Token-endpoint auth methods a dynamic client may register with. */
|
|
278
|
+
type DynamicClientAuthMethod = "none" | "client_secret_post";
|
|
242
279
|
/**
|
|
243
280
|
* One dynamically-registered OAuth client (RFC 7591) — the record behind a
|
|
244
|
-
* connector-style
|
|
281
|
+
* connector-style client that self-registered at `POST /register`.
|
|
245
282
|
* Everything here is **registrant-supplied** (post-validation) except
|
|
246
|
-
* `clientId` and the timestamps: treat `clientName` as
|
|
247
|
-
* and `redirectUris` as the exact-match delivery
|
|
248
|
-
* `DynamicClientPolicy` enforces at `/authorize`.
|
|
283
|
+
* `clientId`, `clientSecretHash` and the timestamps: treat `clientName` as
|
|
284
|
+
* untrusted display text and `redirectUris` as the exact-match delivery
|
|
285
|
+
* allowlist that `DynamicClientPolicy` enforces at `/authorize`.
|
|
249
286
|
*/
|
|
250
287
|
interface DynamicClient {
|
|
251
288
|
/** Store-minted opaque client identifier (the DCR response `client_id`). */
|
|
@@ -254,8 +291,18 @@ interface DynamicClient {
|
|
|
254
291
|
clientName?: string;
|
|
255
292
|
/** Validated redirect allowlist — https exact-match entries and/or loopback literals. */
|
|
256
293
|
redirectUris: string[];
|
|
257
|
-
/**
|
|
258
|
-
|
|
294
|
+
/**
|
|
295
|
+
* `"none"` (public — PKCE is the binding) or `"client_secret_post"`
|
|
296
|
+
* (confidential — a server-minted secret is additionally checked at `/token`).
|
|
297
|
+
*/
|
|
298
|
+
tokenEndpointAuthMethod: DynamicClientAuthMethod;
|
|
299
|
+
/**
|
|
300
|
+
* SHA-256 hex digest of the minted `client_secret` — present iff
|
|
301
|
+
* `tokenEndpointAuthMethod` is `"client_secret_post"`. The plaintext secret
|
|
302
|
+
* is returned ONCE in the registration response and never stored; see
|
|
303
|
+
* `hashClientSecret` / `verifyClientSecret`.
|
|
304
|
+
*/
|
|
305
|
+
clientSecretHash?: string;
|
|
259
306
|
/** Registered grant types (narrowed to what the server supports). */
|
|
260
307
|
grantTypes: string[];
|
|
261
308
|
/** Registered response types (narrowed to what the server supports). */
|
|
@@ -275,7 +322,9 @@ interface DynamicClient {
|
|
|
275
322
|
interface NewDynamicClient {
|
|
276
323
|
clientName?: string;
|
|
277
324
|
redirectUris: string[];
|
|
278
|
-
tokenEndpointAuthMethod:
|
|
325
|
+
tokenEndpointAuthMethod: DynamicClientAuthMethod;
|
|
326
|
+
/** Digest of the minted secret (confidential clients) — see {@link DynamicClient.clientSecretHash}. */
|
|
327
|
+
clientSecretHash?: string;
|
|
279
328
|
grantTypes: string[];
|
|
280
329
|
responseTypes: string[];
|
|
281
330
|
scope?: string;
|
|
@@ -327,4 +376,4 @@ declare class DynamicClientStoreMemory extends DynamicClientStore {
|
|
|
327
376
|
deleteUnusedBefore(cutoff: number): Promise<number>;
|
|
328
377
|
}
|
|
329
378
|
//#endregion
|
|
330
|
-
export {
|
|
379
|
+
export { PendingAuthorizationStoreMemoryOptions as _, DynamicClientStoreMemoryOptions as a, tokenPolicyToIssueOptions as b, AuthCodeStore as c, NewAuthCode as d, DEFAULT_PENDING_TTL_MS as f, PendingAuthorizationStoreMemory as g, PendingAuthorizationStore as h, DynamicClientStoreMemory as i, AuthCodeStoreMemory as l, PendingAuthorization as m, DynamicClientAuthMethod as n, NewDynamicClient as o, NewPendingAuthorization as p, DynamicClientStore as r, AuthCode as s, DynamicClient as t, AuthCodeStoreMemoryOptions as u, DEFAULT_AUTHZ_REFRESH_TTL_MS as v, TokenPolicy as y };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { t as Clock } from "./clock-
|
|
1
|
+
import { t as Clock } from "./clock-DJ_eroHW.cjs";
|
|
2
|
+
import { r as IssueOptions } from "./auth-credential-CEijxoGU.cjs";
|
|
2
3
|
|
|
3
4
|
//#region src/authz/token-policy.d.ts
|
|
4
5
|
/**
|
|
@@ -24,7 +25,41 @@ interface TokenPolicy {
|
|
|
24
25
|
* is persisted on the pending-authorization + auth-code records.
|
|
25
26
|
*/
|
|
26
27
|
payload?: Record<string, unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Opt-in refresh dimension (OAuth 2.1 `refresh_token` grant): when set, the
|
|
30
|
+
* token endpoint mints a rotating refresh token alongside the access token
|
|
31
|
+
* and redeems `grant_type=refresh_token` for the family. `ttl` is the
|
|
32
|
+
* refresh-token (i.e. grant) lifetime in ms — defaults to
|
|
33
|
+
* {@link DEFAULT_AUTHZ_REFRESH_TTL_MS} (30 days). Absent ⇒ today's behavior:
|
|
34
|
+
* access token only, re-consent at expiry.
|
|
35
|
+
*
|
|
36
|
+
* Honored only for grants bound to a registered `client_id` (Tier-2 /
|
|
37
|
+
* dynamic clients) — the family is stamped with `metadata.authzClientId` and
|
|
38
|
+
* a refresh token redeems only for that client. A clientless (Tier-1
|
|
39
|
+
* loopback) grant has nothing to bind to, so the flag is IGNORED there.
|
|
40
|
+
*/
|
|
41
|
+
refresh?: {
|
|
42
|
+
ttl?: number;
|
|
43
|
+
};
|
|
27
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Default refresh-token (grant) lifetime for {@link TokenPolicy.refresh} when
|
|
47
|
+
* no `ttl` is given: 30 days. Deliberately independent of the session tier's
|
|
48
|
+
* `AuthCredentialOptions.refresh.ttl` — an authz grant's lifetime is the
|
|
49
|
+
* policy's decision, never inherited from browser-session posture.
|
|
50
|
+
*/
|
|
51
|
+
declare const DEFAULT_AUTHZ_REFRESH_TTL_MS: number;
|
|
52
|
+
/**
|
|
53
|
+
* Flatten a {@link TokenPolicy} into the `AuthCredential.issue()` options a
|
|
54
|
+
* token endpoint forwards. The refresh dimension applies only to client-bound
|
|
55
|
+
* grants — the family is stamped with `metadata.authzClientId`, the binding
|
|
56
|
+
* the `refresh_token` grant enforces — so a clientless (loopback) grant
|
|
57
|
+
* ignores `policy.refresh`. `refresh: false` otherwise: an authz mint never
|
|
58
|
+
* rides the instance-level session refresh config (no orphaned refresh row
|
|
59
|
+
* for a policy that didn't opt in). Lives HERE, next to the policy type and
|
|
60
|
+
* its default ttl, so every HTTP adapter applies the same semantics.
|
|
61
|
+
*/
|
|
62
|
+
declare function tokenPolicyToIssueOptions(policy: TokenPolicy, clientId: string | undefined): IssueOptions;
|
|
28
63
|
//#endregion
|
|
29
64
|
//#region src/authz/pending-authorization-store.d.ts
|
|
30
65
|
/**
|
|
@@ -239,13 +274,15 @@ declare class AuthCodeStoreMemory extends AuthCodeStore {
|
|
|
239
274
|
}
|
|
240
275
|
//#endregion
|
|
241
276
|
//#region src/authz/dynamic-client-store.d.ts
|
|
277
|
+
/** Token-endpoint auth methods a dynamic client may register with. */
|
|
278
|
+
type DynamicClientAuthMethod = "none" | "client_secret_post";
|
|
242
279
|
/**
|
|
243
280
|
* One dynamically-registered OAuth client (RFC 7591) — the record behind a
|
|
244
|
-
* connector-style
|
|
281
|
+
* connector-style client that self-registered at `POST /register`.
|
|
245
282
|
* Everything here is **registrant-supplied** (post-validation) except
|
|
246
|
-
* `clientId` and the timestamps: treat `clientName` as
|
|
247
|
-
* and `redirectUris` as the exact-match delivery
|
|
248
|
-
* `DynamicClientPolicy` enforces at `/authorize`.
|
|
283
|
+
* `clientId`, `clientSecretHash` and the timestamps: treat `clientName` as
|
|
284
|
+
* untrusted display text and `redirectUris` as the exact-match delivery
|
|
285
|
+
* allowlist that `DynamicClientPolicy` enforces at `/authorize`.
|
|
249
286
|
*/
|
|
250
287
|
interface DynamicClient {
|
|
251
288
|
/** Store-minted opaque client identifier (the DCR response `client_id`). */
|
|
@@ -254,8 +291,18 @@ interface DynamicClient {
|
|
|
254
291
|
clientName?: string;
|
|
255
292
|
/** Validated redirect allowlist — https exact-match entries and/or loopback literals. */
|
|
256
293
|
redirectUris: string[];
|
|
257
|
-
/**
|
|
258
|
-
|
|
294
|
+
/**
|
|
295
|
+
* `"none"` (public — PKCE is the binding) or `"client_secret_post"`
|
|
296
|
+
* (confidential — a server-minted secret is additionally checked at `/token`).
|
|
297
|
+
*/
|
|
298
|
+
tokenEndpointAuthMethod: DynamicClientAuthMethod;
|
|
299
|
+
/**
|
|
300
|
+
* SHA-256 hex digest of the minted `client_secret` — present iff
|
|
301
|
+
* `tokenEndpointAuthMethod` is `"client_secret_post"`. The plaintext secret
|
|
302
|
+
* is returned ONCE in the registration response and never stored; see
|
|
303
|
+
* `hashClientSecret` / `verifyClientSecret`.
|
|
304
|
+
*/
|
|
305
|
+
clientSecretHash?: string;
|
|
259
306
|
/** Registered grant types (narrowed to what the server supports). */
|
|
260
307
|
grantTypes: string[];
|
|
261
308
|
/** Registered response types (narrowed to what the server supports). */
|
|
@@ -275,7 +322,9 @@ interface DynamicClient {
|
|
|
275
322
|
interface NewDynamicClient {
|
|
276
323
|
clientName?: string;
|
|
277
324
|
redirectUris: string[];
|
|
278
|
-
tokenEndpointAuthMethod:
|
|
325
|
+
tokenEndpointAuthMethod: DynamicClientAuthMethod;
|
|
326
|
+
/** Digest of the minted secret (confidential clients) — see {@link DynamicClient.clientSecretHash}. */
|
|
327
|
+
clientSecretHash?: string;
|
|
279
328
|
grantTypes: string[];
|
|
280
329
|
responseTypes: string[];
|
|
281
330
|
scope?: string;
|
|
@@ -327,4 +376,4 @@ declare class DynamicClientStoreMemory extends DynamicClientStore {
|
|
|
327
376
|
deleteUnusedBefore(cutoff: number): Promise<number>;
|
|
328
377
|
}
|
|
329
378
|
//#endregion
|
|
330
|
-
export {
|
|
379
|
+
export { PendingAuthorizationStoreMemoryOptions as _, DynamicClientStoreMemoryOptions as a, tokenPolicyToIssueOptions as b, AuthCodeStore as c, NewAuthCode as d, DEFAULT_PENDING_TTL_MS as f, PendingAuthorizationStoreMemory as g, PendingAuthorizationStore as h, DynamicClientStoreMemory as i, AuthCodeStoreMemory as l, PendingAuthorization as m, DynamicClientAuthMethod as n, NewDynamicClient as o, NewPendingAuthorization as p, DynamicClientStore as r, AuthCode as s, DynamicClient as t, AuthCodeStoreMemoryOptions as u, DEFAULT_AUTHZ_REFRESH_TTL_MS as v, TokenPolicy as y };
|
|
@@ -150,6 +150,7 @@ var DynamicClientStoreMemory = class extends DynamicClientStore {
|
|
|
150
150
|
responseTypes: [...rec.responseTypes],
|
|
151
151
|
createdAt: this.clock.now(),
|
|
152
152
|
...rec.clientName !== void 0 && { clientName: rec.clientName },
|
|
153
|
+
...rec.clientSecretHash !== void 0 && { clientSecretHash: rec.clientSecretHash },
|
|
153
154
|
...rec.scope !== void 0 && { scope: rec.scope }
|
|
154
155
|
};
|
|
155
156
|
this.store.set(row.clientId, structuredClone(row));
|