@aooth/auth 0.1.17 → 0.1.19

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/authz.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_clock = require("./clock-Bl-H3eqE.cjs");
3
- const require_auth_code_store = require("./auth-code-store-B_m51OSB.cjs");
3
+ const require_dynamic_client_store = require("./dynamic-client-store-DAStgv2j.cjs");
4
4
  let node_crypto = require("node:crypto");
5
5
  let jose = require("jose");
6
6
  //#region src/authz/authz-errors.ts
@@ -116,9 +116,14 @@ var RegisteredClientPolicy = class {
116
116
  idToken,
117
117
  accessToken: client.accessToken === true,
118
118
  tokenPolicy: client.tokenPolicy ? structuredClone(client.tokenPolicy) : {},
119
+ ...client.clientName !== void 0 && { clientName: client.clientName },
119
120
  ...scope !== void 0 && { scope }
120
121
  };
121
122
  }
123
+ /** Known-ness probe for `CompositeClientPolicy` dispatch (static registry, sync). */
124
+ hasClient(clientId) {
125
+ return this.clients.has(clientId);
126
+ }
122
127
  authenticateClient(args) {
123
128
  const client = this.requireClient(args.clientId);
124
129
  if ((client.type ?? "public") !== "confidential") return;
@@ -162,29 +167,290 @@ function timingSafeEqualStr(a, b) {
162
167
  //#endregion
163
168
  //#region src/authz/composite-client-policy.ts
164
169
  /**
165
- * Runs Tier-1 and Tier-2 side by side, dispatching on the **presence of
166
- * `client_id`** (AUTH-SERVER.md §10): a request with a `client_id` is a
167
- * registered client, one without is a loopback CLI. The split is the safety
168
- * boundary a registered client is routed only to {@link RegisteredClientPolicy}
169
- * (which enforces ITS redirect allowlist, so it cannot smuggle a loopback
170
- * redirect), and a no-`client_id` request is routed only to the loopback policy
171
- * (so it cannot claim to be a registered client). Each sub-policy still owns its
172
- * own redirect validation; this only picks which one runs.
170
+ * Runs the tiers side by side, dispatching on the **presence and ownership of
171
+ * `client_id`** (AUTH-SERVER.md §10, OAUTH.md R2): a request without a
172
+ * `client_id` is a loopback CLI; one with a `client_id` belongs to the static
173
+ * registry when it knows the id (`hasClient`), else to the dynamic policy. The
174
+ * split is the safety boundary each sub-policy still enforces ITS OWN
175
+ * redirect allowlist (so a registered client cannot smuggle a loopback
176
+ * redirect and a no-`client_id` request cannot claim to be registered), and
177
+ * static-first dispatch means a dynamic registration can never shadow a static
178
+ * client id. `authenticateClient` routes through the SAME picker: a dynamic
179
+ * `client_id` must be authenticated by the dynamic policy (existence check,
180
+ * PKCE binds), never rejected by the static registry that doesn't know it.
173
181
  */
174
182
  var CompositeClientPolicy = class {
175
183
  loopback;
176
184
  registered;
185
+ dynamic;
177
186
  constructor(opts) {
187
+ if (!opts.registered && !opts.dynamic) throw new Error("CompositeClientPolicy requires at least one of `registered` / `dynamic` — with only loopback there is nothing to compose");
188
+ if (opts.registered && opts.dynamic && typeof opts.registered.hasClient !== "function") throw new Error("CompositeClientPolicy: `registered` must implement hasClient() when composed with `dynamic`");
178
189
  this.loopback = opts.loopback;
179
190
  this.registered = opts.registered;
191
+ this.dynamic = opts.dynamic;
180
192
  }
181
193
  resolveClient(args) {
182
- return args.clientId ? this.registered.resolveClient(args) : this.loopback.resolveClient(args);
194
+ if (!args.clientId) return this.loopback.resolveClient(args);
195
+ const picked = this.pickForClientId(args.clientId);
196
+ return picked instanceof Promise ? picked.then((p) => p.resolveClient(args)) : picked.resolveClient(args);
183
197
  }
184
198
  authenticateClient(args) {
185
- if (args.clientId) return this.registered.authenticateClient?.(args);
199
+ if (!args.clientId) return;
200
+ const picked = this.pickForClientId(args.clientId);
201
+ return picked instanceof Promise ? picked.then((p) => p.authenticateClient?.(args)) : picked.authenticateClient?.(args);
202
+ }
203
+ /**
204
+ * Ownership dispatch for a presented `client_id` — static registry first
205
+ * (when it knows the id), dynamic otherwise. Sync when the probe is sync
206
+ * (the static registry's is), preserving the callers' sync fast path.
207
+ */
208
+ pickForClientId(clientId) {
209
+ if (!this.registered) return this.dynamic;
210
+ if (!this.dynamic) return this.registered;
211
+ const known = this.registered.hasClient(clientId);
212
+ return known instanceof Promise ? known.then((k) => k ? this.registered : this.dynamic) : known ? this.registered : this.dynamic;
213
+ }
214
+ };
215
+ //#endregion
216
+ //#region src/authz/client-registration.ts
217
+ /** A typed RFC 7591 registration failure; `message` becomes `error_description`. */
218
+ var ClientRegistrationError = class extends Error {
219
+ code;
220
+ constructor(code, message) {
221
+ super(message);
222
+ this.name = "ClientRegistrationError";
223
+ this.code = code;
224
+ }
225
+ };
226
+ /** Grant/response types the v1 authorization server supports (auth-code + PKCE only). */
227
+ const SUPPORTED_GRANT_TYPES = ["authorization_code"];
228
+ const SUPPORTED_RESPONSE_TYPES = ["code"];
229
+ /** RFC 6749 §3.3 scope-token charset: printable ASCII minus space, `"` and `\`. */
230
+ const SCOPE_TOKEN_RE = /^[\x21\x23-\x5B\x5D-\x7E]+$/u;
231
+ const DEFAULT_MAX_REDIRECT_URIS = 5;
232
+ const DEFAULT_MAX_REDIRECT_URI_LENGTH = 512;
233
+ const DEFAULT_MAX_CLIENT_NAME_LENGTH = 128;
234
+ const DEFAULT_MAX_SCOPE_LENGTH = 256;
235
+ /**
236
+ * `client_name` is attacker-supplied text that ends up on the consent prompt.
237
+ * Strip every control (Cc) and format (Cf) character — that kills CR/LF, the
238
+ * Unicode bidi overrides (U+202A–202E, U+2066–2069) used to visually reverse
239
+ * a name, and zero-width characters used to forge lookalikes — then trim and
240
+ * cap. Rendering it as a TEXT node (never markup) is the consent form's job;
241
+ * this is defense in depth, not the whole defense.
242
+ */
243
+ function sanitizeClientName(name, maxLength) {
244
+ const cleaned = name.replace(/[\p{Cc}\p{Cf}]/gu, "").trim();
245
+ if (cleaned.length === 0) return void 0;
246
+ return cleaned.slice(0, maxLength);
247
+ }
248
+ function asStringArray(value, field) {
249
+ if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) throw new ClientRegistrationError("invalid_client_metadata", `${field} must be an array of strings`);
250
+ return value;
251
+ }
252
+ /**
253
+ * `true` when `uri` is an acceptable dynamic-client redirect target (OAUTH.md
254
+ * R2): an `https://` URL with an explicit host (stored for EXACT matching — no
255
+ * wildcards, no prefixes) or a loopback literal per the Tier-1 RFC 8252 rules.
256
+ * Custom schemes are rejected in v1. A fragment is rejected outright (RFC 6749
257
+ * §3.1.2: the redirection endpoint URI MUST NOT include a fragment component).
258
+ */
259
+ function isAcceptableRedirectUri(uri) {
260
+ let url;
261
+ try {
262
+ url = new URL(uri);
263
+ } catch {
264
+ return false;
265
+ }
266
+ if (url.hash !== "") return false;
267
+ if (isLoopbackRedirectUri(uri)) return true;
268
+ return url.protocol === "https:" && url.hostname !== "" && url.username === "" && url.password === "";
269
+ }
270
+ /**
271
+ * Validate + normalize an RFC 7591 registration request body into a
272
+ * {@link NewDynamicClient}, or THROW a {@link ClientRegistrationError}.
273
+ *
274
+ * Normalization is allowed by RFC 7591 §2 (the server MAY replace requested
275
+ * metadata with its own values) and is load-bearing for real connectors:
276
+ * `grant_types` / `response_types` are INTERSECTED with what the server
277
+ * supports (a connector requesting `["authorization_code", "refresh_token"]`
278
+ * registers with `["authorization_code"]` — the 201 echo of the narrowed set
279
+ * is the contract) rather than rejected. `token_endpoint_auth_method` defaults
280
+ * to `"none"` when absent (v1 is public-clients-only, so the RFC's
281
+ * `client_secret_basic` default is unsupported), but an EXPLICIT non-`"none"`
282
+ * ask is rejected — never silently downgrade a client that asked for a secret.
283
+ * Unknown fields are ignored and never echoed.
284
+ */
285
+ function validateClientRegistration(body, opts) {
286
+ if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ClientRegistrationError("invalid_client_metadata", "registration request must be a JSON object");
287
+ const req = body;
288
+ const maxUris = opts?.maxRedirectUris ?? DEFAULT_MAX_REDIRECT_URIS;
289
+ const maxUriLength = opts?.maxRedirectUriLength ?? DEFAULT_MAX_REDIRECT_URI_LENGTH;
290
+ if (req.redirect_uris === void 0) throw new ClientRegistrationError("invalid_redirect_uri", "redirect_uris is required");
291
+ const redirectUris = [...new Set(asStringArray(req.redirect_uris, "redirect_uris"))];
292
+ if (redirectUris.length === 0) throw new ClientRegistrationError("invalid_redirect_uri", "redirect_uris must be non-empty");
293
+ if (redirectUris.length > maxUris) throw new ClientRegistrationError("invalid_redirect_uri", `redirect_uris accepts at most ${maxUris} entries`);
294
+ 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");
295
+ if ((req.token_endpoint_auth_method ?? "none") !== "none") throw new ClientRegistrationError("invalid_client_metadata", "only token_endpoint_auth_method \"none\" is supported");
296
+ const grantTypes = req.grant_types === void 0 ? [...SUPPORTED_GRANT_TYPES] : asStringArray(req.grant_types, "grant_types").filter((g) => SUPPORTED_GRANT_TYPES.includes(g));
297
+ if (grantTypes.length === 0) throw new ClientRegistrationError("invalid_client_metadata", "grant_types must include authorization_code");
298
+ const responseTypes = req.response_types === void 0 ? [...SUPPORTED_RESPONSE_TYPES] : asStringArray(req.response_types, "response_types").filter((r) => SUPPORTED_RESPONSE_TYPES.includes(r));
299
+ if (responseTypes.length === 0) throw new ClientRegistrationError("invalid_client_metadata", "response_types must include code");
300
+ let clientName;
301
+ if (req.client_name !== void 0) {
302
+ if (typeof req.client_name !== "string") throw new ClientRegistrationError("invalid_client_metadata", "client_name must be a string");
303
+ clientName = sanitizeClientName(req.client_name, opts?.maxClientNameLength ?? DEFAULT_MAX_CLIENT_NAME_LENGTH);
304
+ }
305
+ let scope;
306
+ if (req.scope !== void 0) {
307
+ if (typeof req.scope !== "string") throw new ClientRegistrationError("invalid_client_metadata", "scope must be a string");
308
+ if (req.scope.length > (opts?.maxScopeLength ?? DEFAULT_MAX_SCOPE_LENGTH)) throw new ClientRegistrationError("invalid_client_metadata", "scope is too long");
309
+ const tokens = req.scope.split(/\s+/u).filter(Boolean);
310
+ if (tokens.some((t) => !SCOPE_TOKEN_RE.test(t))) throw new ClientRegistrationError("invalid_client_metadata", "scope contains invalid characters");
311
+ const kept = opts?.allowedScopes ? tokens.filter((t) => opts.allowedScopes.includes(t)) : tokens;
312
+ if (kept.length > 0) scope = kept.join(" ");
313
+ }
314
+ return {
315
+ redirectUris,
316
+ tokenEndpointAuthMethod: "none",
317
+ grantTypes,
318
+ responseTypes,
319
+ ...clientName !== void 0 && { clientName },
320
+ ...scope !== void 0 && { scope }
321
+ };
322
+ }
323
+ const DEFAULT_MAX_CLIENTS = 1e3;
324
+ /**
325
+ * The RFC 7591 registration operation behind `POST {issuer}/register`
326
+ * (OAUTH.md R2): validate → guard → lazy GC of never-used rows → hard cap →
327
+ * persist. Framework-free — `@aooth/auth-moost`'s controller endpoint is a
328
+ * thin HTTP adapter over `register()`, and non-moost servers can call it
329
+ * directly.
330
+ */
331
+ var DynamicClientRegistration = class {
332
+ store;
333
+ maxClients;
334
+ unusedClientTtlMs;
335
+ guard;
336
+ validation;
337
+ clock;
338
+ constructor(opts) {
339
+ this.store = opts.store;
340
+ this.maxClients = opts.maxClients ?? DEFAULT_MAX_CLIENTS;
341
+ this.unusedClientTtlMs = opts.unusedClientTtlMs;
342
+ this.guard = opts.guard;
343
+ this.validation = opts.validation;
344
+ this.clock = opts.clock ?? require_clock.defaultClock;
345
+ }
346
+ /** Validate and persist a registration request body; returns the minted client. */
347
+ async register(body) {
348
+ const metadata = validateClientRegistration(body, this.validation);
349
+ await this.guard?.({ metadata });
350
+ if (this.unusedClientTtlMs !== void 0) await this.store.deleteUnusedBefore(this.clock.now() - this.unusedClientTtlMs);
351
+ if (await this.store.count() >= this.maxClients) throw new ClientRegistrationError("invalid_client_metadata", "registration limit reached");
352
+ return this.store.create(metadata);
353
+ }
354
+ };
355
+ //#endregion
356
+ //#region src/authz/dynamic-client-policy.ts
357
+ const DEFAULT_DYNAMIC_TOKEN_POLICY = {
358
+ kind: "dynamic-session",
359
+ ttl: 720 * 60 * 6e4
360
+ };
361
+ /**
362
+ * Policy for RFC 7591 dynamically-registered public clients (OAUTH.md R2) on
363
+ * the `ClientRedirectPolicy` seam. `resolveClient` authorizes the client +
364
+ * `redirect_uri` against ITS registered allowlist and resolves the granted
365
+ * scope + token policy; `authenticateClient` re-checks existence at `/token`
366
+ * (`token_endpoint_auth_method: "none"` ⇒ no secret — PKCE is the binding, and
367
+ * a registration garbage-collected mid-flight fails closed). Dynamic clients
368
+ * receive an access token only — NO `id_token` in v1 (OAUTH.md R6).
369
+ *
370
+ * INVARIANT: the returned {@link ResolvedClient} always carries `clientId`, so
371
+ * the minted code records it and the token endpoint's symmetric client binding
372
+ * applies — a dynamic code redeemed without (or with another) `client_id` is
373
+ * rejected exactly like a Tier-2 code.
374
+ */
375
+ var DynamicClientPolicy = class {
376
+ store;
377
+ tokenPolicy;
378
+ allowedScopes;
379
+ clock;
380
+ constructor(opts) {
381
+ this.store = opts.store;
382
+ this.tokenPolicy = opts.tokenPolicy ?? DEFAULT_DYNAMIC_TOKEN_POLICY;
383
+ this.allowedScopes = opts.allowedScopes;
384
+ this.clock = opts.clock ?? require_clock.defaultClock;
385
+ }
386
+ async resolveClient(args) {
387
+ const client = await this.requireClient(args.clientId);
388
+ if (!redirectAllowed(client, args.redirectUri)) throw new AuthorizeError("invalid_redirect", "redirect_uri is not registered for this client");
389
+ const scope = this.grantScope(client, args.scope);
390
+ try {
391
+ await this.store.touch(client.clientId, this.clock.now());
392
+ } catch {}
393
+ return {
394
+ clientId: client.clientId,
395
+ redirectUri: args.redirectUri,
396
+ accessToken: true,
397
+ tokenPolicy: structuredClone(this.tokenPolicy),
398
+ ...client.clientName !== void 0 && { clientName: client.clientName },
399
+ ...scope !== void 0 && { scope }
400
+ };
401
+ }
402
+ /**
403
+ * `/token`-side check: the client must still exist (fail closed when the
404
+ * registration was deleted/GC'd between authorize and redemption). No secret
405
+ * is checked — public client, PKCE is the binding; a spurious
406
+ * `client_secret` is ignored.
407
+ */
408
+ async authenticateClient(args) {
409
+ await this.requireClient(args.clientId);
410
+ }
411
+ /** Known-ness probe for `CompositeClientPolicy` dispatch. */
412
+ async hasClient(clientId) {
413
+ return await this.store.get(clientId) !== null;
414
+ }
415
+ async requireClient(clientId) {
416
+ const client = clientId ? await this.store.get(clientId) : null;
417
+ if (!client) throw new AuthorizeError("invalid_client", "unknown client");
418
+ return client;
419
+ }
420
+ grantScope(client, requested) {
421
+ if (!requested) return void 0;
422
+ let granted = requested.split(/\s+/u).filter(Boolean);
423
+ if (this.allowedScopes) granted = granted.filter((s) => this.allowedScopes.includes(s));
424
+ if (client.scope !== void 0) {
425
+ const registered = new Set(client.scope.split(/\s+/u).filter(Boolean));
426
+ granted = granted.filter((s) => registered.has(s));
427
+ }
428
+ return granted.length > 0 ? granted.join(" ") : void 0;
186
429
  }
187
430
  };
431
+ /**
432
+ * Exact match against the registered allowlist, with ONE relaxation: for a
433
+ * loopback redirect the PORT is ignored (RFC 8252 §7.3 — native/CLI clients
434
+ * bind an ephemeral port per run, so the AS MUST allow a variable port).
435
+ * Scheme, host, path and query still must match a registered loopback entry
436
+ * exactly; `https` entries never get the relaxation.
437
+ */
438
+ function redirectAllowed(client, uri) {
439
+ if (client.redirectUris.includes(uri)) return true;
440
+ if (!isLoopbackRedirectUri(uri)) return false;
441
+ let presented;
442
+ try {
443
+ presented = new URL(uri);
444
+ } catch {
445
+ return false;
446
+ }
447
+ if (presented.hash !== "") return false;
448
+ return client.redirectUris.some((registered) => {
449
+ if (!isLoopbackRedirectUri(registered)) return false;
450
+ const reg = new URL(registered);
451
+ return reg.protocol === presented.protocol && reg.hostname === presented.hostname && reg.pathname === presented.pathname && reg.search === presented.search;
452
+ });
453
+ }
188
454
  //#endregion
189
455
  //#region src/authz/id-token-signer.ts
190
456
  /**
@@ -253,17 +519,115 @@ var IdTokenSigner = class {
253
519
  }
254
520
  };
255
521
  //#endregion
256
- exports.AuthCodeStore = require_auth_code_store.AuthCodeStore;
257
- exports.AuthCodeStoreMemory = require_auth_code_store.AuthCodeStoreMemory;
522
+ //#region src/authz/server-metadata.ts
523
+ /**
524
+ * Strip a trailing slash from an issuer identifier. RFC 8414 mandates EXACT
525
+ * (byte-identical) string comparison of issuer values, so every document that
526
+ * spells the issuer must canonicalize it the same way — same rule as
527
+ * `IdTokenSigner`. Shared by the AS metadata and protected-resource builders.
528
+ */
529
+ function canonicalizeIssuer(issuer) {
530
+ return issuer.replace(/\/$/u, "");
531
+ }
532
+ /**
533
+ * Build the RFC 8414 discovery document for this authorization server. Pure —
534
+ * no I/O, no framework: mount the result on any HTTP stack (including the
535
+ * RFC 8414 path-insertion form rooted at the HTTP origin,
536
+ * `/.well-known/oauth-authorization-server/<issuer-path>`).
537
+ *
538
+ * Capability fields are fixed to what the server actually implements:
539
+ * `response_types_supported` `["code"]`, `grant_types_supported`
540
+ * `["authorization_code"]`, `code_challenge_methods_supported` `["S256"]`
541
+ * (PKCE is mandatory — it is the binding for public clients). Optional fields
542
+ * are omitted entirely (no `undefined` keys) so the serialized JSON carries
543
+ * only what was configured.
544
+ */
545
+ function buildAuthorizationServerMetadata(opts) {
546
+ const issuer = canonicalizeIssuer(opts.issuer);
547
+ const authMethods = opts.tokenEndpointAuthMethodsSupported ?? ["none", "client_secret_post"];
548
+ return {
549
+ issuer,
550
+ authorization_endpoint: opts.authorizationEndpoint ?? `${issuer}/authorize`,
551
+ token_endpoint: opts.tokenEndpoint ?? `${issuer}/token`,
552
+ ...opts.registrationEndpoint !== void 0 && { registration_endpoint: opts.registrationEndpoint },
553
+ ...opts.jwksUri !== void 0 && { jwks_uri: opts.jwksUri },
554
+ response_types_supported: ["code"],
555
+ grant_types_supported: ["authorization_code"],
556
+ code_challenge_methods_supported: ["S256"],
557
+ token_endpoint_auth_methods_supported: authMethods.includes("none") ? authMethods : ["none", ...authMethods],
558
+ ...opts.scopesSupported !== void 0 && { scopes_supported: opts.scopesSupported }
559
+ };
560
+ }
561
+ //#endregion
562
+ //#region src/authz/resource-metadata.ts
563
+ /**
564
+ * Build the RFC 9728 protected-resource document. Pure — no I/O, no framework;
565
+ * mount it on any HTTP stack. Optional fields are omitted entirely (no
566
+ * `undefined` keys) so the serialized JSON carries only what was configured.
567
+ */
568
+ function buildProtectedResourceMetadata(opts) {
569
+ return {
570
+ resource: opts.resource,
571
+ authorization_servers: opts.authorizationServers.map((iss) => canonicalizeIssuer(iss)),
572
+ bearer_methods_supported: opts.bearerMethodsSupported ?? ["header"],
573
+ ...opts.scopesSupported !== void 0 && { scopes_supported: opts.scopesSupported },
574
+ ...opts.resourceName !== void 0 && { resource_name: opts.resourceName }
575
+ };
576
+ }
577
+ /**
578
+ * Header-injection defense: challenge parameter values can carry
579
+ * attacker-influenced input (an `error_description` echoing request data, a
580
+ * scope from a token). A raw CR/LF would terminate the header and let that
581
+ * input inject arbitrary response headers (response splitting), so CR/LF — and
582
+ * every other C0 control char plus DEL — are STRIPPED outright. `\` and `"`
583
+ * are then backslash-escaped per the RFC 7235 quoted-string grammar so a value
584
+ * cannot close its quote and smuggle extra challenge params.
585
+ */
586
+ function sanitizeQuotedStringValue(value) {
587
+ return value.replace(/[\u0000-\u001F\u007F]/gu, "").replace(/(["\\])/gu, "\\$1");
588
+ }
589
+ /**
590
+ * Build the VALUE of an RFC 6750 `WWW-Authenticate` Bearer challenge, e.g.
591
+ * `Bearer resource_metadata="https://...", error="invalid_token"`. With no
592
+ * options (or all absent) it degrades to the bare scheme `Bearer`. Every
593
+ * parameter is emitted as a quoted-string (RFC 6750 style) in a fixed order:
594
+ * `realm`, `error`, `error_description`, `resource_metadata`, `scope`. All
595
+ * values pass through {@link sanitizeQuotedStringValue} — see its security note.
596
+ */
597
+ function buildWwwAuthenticateBearerChallenge(opts) {
598
+ const params = [];
599
+ const push = (name, value) => {
600
+ if (value !== void 0) params.push(`${name}="${sanitizeQuotedStringValue(value)}"`);
601
+ };
602
+ push("realm", opts?.realm);
603
+ push("error", opts?.error);
604
+ push("error_description", opts?.errorDescription);
605
+ push("resource_metadata", opts?.resourceMetadataUrl);
606
+ push("scope", opts?.scope);
607
+ return params.length === 0 ? "Bearer" : `Bearer ${params.join(", ")}`;
608
+ }
609
+ //#endregion
610
+ exports.AuthCodeStore = require_dynamic_client_store.AuthCodeStore;
611
+ exports.AuthCodeStoreMemory = require_dynamic_client_store.AuthCodeStoreMemory;
258
612
  exports.AuthorizeError = AuthorizeError;
613
+ exports.ClientRegistrationError = ClientRegistrationError;
259
614
  exports.CompositeClientPolicy = CompositeClientPolicy;
260
- exports.DEFAULT_PENDING_TTL_MS = require_auth_code_store.DEFAULT_PENDING_TTL_MS;
615
+ exports.DEFAULT_PENDING_TTL_MS = require_dynamic_client_store.DEFAULT_PENDING_TTL_MS;
616
+ exports.DynamicClientPolicy = DynamicClientPolicy;
617
+ exports.DynamicClientRegistration = DynamicClientRegistration;
618
+ exports.DynamicClientStore = require_dynamic_client_store.DynamicClientStore;
619
+ exports.DynamicClientStoreMemory = require_dynamic_client_store.DynamicClientStoreMemory;
261
620
  exports.IdTokenSigner = IdTokenSigner;
262
621
  exports.LoopbackClientPolicy = LoopbackClientPolicy;
263
622
  exports.NoopOidcClaimsResolver = NoopOidcClaimsResolver;
264
623
  exports.OidcClaimsResolver = OidcClaimsResolver;
265
- exports.PendingAuthorizationStore = require_auth_code_store.PendingAuthorizationStore;
266
- exports.PendingAuthorizationStoreMemory = require_auth_code_store.PendingAuthorizationStoreMemory;
624
+ exports.PendingAuthorizationStore = require_dynamic_client_store.PendingAuthorizationStore;
625
+ exports.PendingAuthorizationStoreMemory = require_dynamic_client_store.PendingAuthorizationStoreMemory;
267
626
  exports.RegisteredClientPolicy = RegisteredClientPolicy;
627
+ exports.buildAuthorizationServerMetadata = buildAuthorizationServerMetadata;
628
+ exports.buildProtectedResourceMetadata = buildProtectedResourceMetadata;
629
+ exports.buildWwwAuthenticateBearerChallenge = buildWwwAuthenticateBearerChallenge;
630
+ exports.canonicalizeIssuer = canonicalizeIssuer;
268
631
  exports.isLoopbackRedirectUri = isLoopbackRedirectUri;
269
632
  exports.scopeGrants = scopeGrants;
633
+ exports.validateClientRegistration = validateClientRegistration;