@aooth/auth 0.1.18 → 0.1.20

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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as Clock } from "./clock-BjXa0LXb.cjs";
2
- import { a as NewAuthCode, c as PendingAuthorization, d as PendingAuthorizationStoreMemoryOptions, f as TokenPolicy, i as AuthCodeStoreMemoryOptions, l as PendingAuthorizationStore, n as AuthCodeStore, o as DEFAULT_PENDING_TTL_MS, r as AuthCodeStoreMemory, s as NewPendingAuthorization, t as AuthCode, u as PendingAuthorizationStoreMemory } from "./auth-code-store-BZ88d_tq.cjs";
2
+ import { _ as TokenPolicy, a as NewDynamicClient, c as AuthCodeStoreMemory, d as DEFAULT_PENDING_TTL_MS, f as NewPendingAuthorization, g as PendingAuthorizationStoreMemoryOptions, h as PendingAuthorizationStoreMemory, i as DynamicClientStoreMemoryOptions, l as AuthCodeStoreMemoryOptions, m as PendingAuthorizationStore, n as DynamicClientStore, o as AuthCode, p as PendingAuthorization, r as DynamicClientStoreMemory, s as AuthCodeStore, t as DynamicClient, u as NewAuthCode } from "./dynamic-client-store-DzqdUwnw.cjs";
3
3
  import { JWK } from "jose";
4
4
 
5
5
  //#region src/authz/authz-errors.d.ts
@@ -10,7 +10,12 @@ import { JWK } from "jose";
10
10
  * code. Messages are benign — they must not disclose whether a failure was an
11
11
  * unknown client vs. a bad redirect vs. an expired code.
12
12
  */
13
- type AuthorizeErrorCode = /** Missing or malformed parameter at `/authorize` or `/token`. */"invalid_request" /** `redirect_uri` is not an allowed (loopback / registered) target. */ | "invalid_redirect" /** Code unknown / expired / already-redeemed, or PKCE verifier mismatch (`/token`). */ | "invalid_grant" /** Unknown or unauthenticated client (Tier 2). */ | "invalid_client" /** A known client used a grant/response it is not allowed (Tier 2). */ | "unauthorized_client" /** The user declined the authorization (consent). */ | "access_denied" /** An unexpected server-side failure. */ | "server_error";
13
+ type AuthorizeErrorCode = /** Missing or malformed parameter at `/authorize` or `/token`. */"invalid_request" /** `redirect_uri` is not an allowed (loopback / registered) target. */ | "invalid_redirect" /** Code unknown / expired / already-redeemed, or PKCE verifier mismatch (`/token`). */ | "invalid_grant" /** Unknown or unauthenticated client (Tier 2). */ | "invalid_client" /** A known client used a grant/response it is not allowed (Tier 2). */ | "unauthorized_client"
14
+ /**
15
+ * RFC 8707: more than one `resource` value on a leg, or the `/token`
16
+ * `resource` differs from the one recorded at `/authorize`.
17
+ */
18
+ | "invalid_target" /** The user declined the authorization (consent). */ | "access_denied" /** An unexpected server-side failure. */ | "server_error";
14
19
  /** A typed authorization-server failure. */
15
20
  declare class AuthorizeError extends Error {
16
21
  readonly code: AuthorizeErrorCode;
@@ -25,6 +30,13 @@ declare class AuthorizeError extends Error {
25
30
  interface ResolvedClient {
26
31
  /** Registered client id (Tier 2), absent for a public/loopback client. */
27
32
  clientId?: string;
33
+ /**
34
+ * Display name for the consent prompt. For a dynamically-registered client
35
+ * this is the (sanitized) DCR `client_name` — UNTRUSTED text supplied by the
36
+ * registrant: render it as a text node, never as markup/links, and show the
37
+ * validated redirect host next to it as the trustworthy identity.
38
+ */
39
+ clientName?: string;
28
40
  /** The validated `redirect_uri` the code will be delivered to. */
29
41
  redirectUri: string;
30
42
  /** What the grant mints (fixed here, recorded on the pending authorization). */
@@ -75,6 +87,14 @@ interface ClientRedirectPolicy {
75
87
  clientId?: string;
76
88
  clientSecret?: string;
77
89
  }): void | Promise<void>;
90
+ /**
91
+ * Known-ness probe: does this policy recognize `clientId`? Optional in
92
+ * general, but REQUIRED on the `registered` slot when a
93
+ * `CompositeClientPolicy` composes it WITH a `dynamic` slot — the composite
94
+ * dispatches a presented `client_id` to whichever policy owns it (static
95
+ * registry first), for `resolveClient` and `authenticateClient` alike.
96
+ */
97
+ hasClient?(clientId: string): boolean | Promise<boolean>;
78
98
  }
79
99
  /**
80
100
  * `true` when `uri` is a syntactically valid http(s) URL whose host is a
@@ -118,6 +138,8 @@ declare class LoopbackClientPolicy implements ClientRedirectPolicy {
118
138
  interface RegisteredClient {
119
139
  /** Stable client identifier; the `id_token` `aud`. */
120
140
  clientId: string;
141
+ /** Display name for the consent prompt (rendered as text). Falls back to `clientId`. */
142
+ clientName?: string;
121
143
  /** Exact-match `redirect_uri` allowlist (the safe default). */
122
144
  redirectUris?: string[];
123
145
  /**
@@ -158,6 +180,8 @@ declare class RegisteredClientPolicy implements ClientRedirectPolicy {
158
180
  redirectUri: string;
159
181
  scope?: string;
160
182
  }): ResolvedClient;
183
+ /** Known-ness probe for `CompositeClientPolicy` dispatch (static registry, sync). */
184
+ hasClient(clientId: string): boolean;
161
185
  authenticateClient(args: {
162
186
  clientId?: string;
163
187
  clientSecret?: string;
@@ -171,22 +195,33 @@ declare class RegisteredClientPolicy implements ClientRedirectPolicy {
171
195
  interface CompositeClientPolicyOptions {
172
196
  /** Used when the request carries NO `client_id` (Tier-1 public/loopback CLI). */
173
197
  loopback: ClientRedirectPolicy;
174
- /** Used when the request carries a `client_id` (Tier-2 registered service). */
175
- registered: ClientRedirectPolicy;
198
+ /** Used when the request carries a `client_id` it recognizes (Tier-2 registered service). */
199
+ registered?: ClientRedirectPolicy;
200
+ /**
201
+ * Used when the request carries a `client_id` the `registered` policy does
202
+ * NOT recognize (RFC 7591 dynamically-registered connector clients). When
203
+ * both `registered` and `dynamic` are set, `registered` MUST implement
204
+ * `hasClient` — it is the dispatch probe.
205
+ */
206
+ dynamic?: ClientRedirectPolicy;
176
207
  }
177
208
  /**
178
- * Runs Tier-1 and Tier-2 side by side, dispatching on the **presence of
179
- * `client_id`** (AUTH-SERVER.md §10): a request with a `client_id` is a
180
- * registered client, one without is a loopback CLI. The split is the safety
181
- * boundary a registered client is routed only to {@link RegisteredClientPolicy}
182
- * (which enforces ITS redirect allowlist, so it cannot smuggle a loopback
183
- * redirect), and a no-`client_id` request is routed only to the loopback policy
184
- * (so it cannot claim to be a registered client). Each sub-policy still owns its
185
- * own redirect validation; this only picks which one runs.
209
+ * Runs the tiers side by side, dispatching on the **presence and ownership of
210
+ * `client_id`** (AUTH-SERVER.md §10, OAUTH.md R2): a request without a
211
+ * `client_id` is a loopback CLI; one with a `client_id` belongs to the static
212
+ * registry when it knows the id (`hasClient`), else to the dynamic policy. The
213
+ * split is the safety boundary each sub-policy still enforces ITS OWN
214
+ * redirect allowlist (so a registered client cannot smuggle a loopback
215
+ * redirect and a no-`client_id` request cannot claim to be registered), and
216
+ * static-first dispatch means a dynamic registration can never shadow a static
217
+ * client id. `authenticateClient` routes through the SAME picker: a dynamic
218
+ * `client_id` must be authenticated by the dynamic policy (existence check,
219
+ * PKCE binds), never rejected by the static registry that doesn't know it.
186
220
  */
187
221
  declare class CompositeClientPolicy implements ClientRedirectPolicy {
188
222
  private readonly loopback;
189
- private readonly registered;
223
+ private readonly registered?;
224
+ private readonly dynamic?;
190
225
  constructor(opts: CompositeClientPolicyOptions);
191
226
  resolveClient(args: {
192
227
  clientId?: string;
@@ -197,6 +232,165 @@ declare class CompositeClientPolicy implements ClientRedirectPolicy {
197
232
  clientId?: string;
198
233
  clientSecret?: string;
199
234
  }): void | Promise<void>;
235
+ /**
236
+ * Ownership dispatch for a presented `client_id` — static registry first
237
+ * (when it knows the id), dynamic otherwise. Sync when the probe is sync
238
+ * (the static registry's is), preserving the callers' sync fast path.
239
+ */
240
+ private pickForClientId;
241
+ }
242
+ //#endregion
243
+ //#region src/authz/client-registration.d.ts
244
+ /**
245
+ * RFC 7591 §3.2.2 error vocabulary — deliberately separate from
246
+ * `AuthorizeErrorCode`: registration errors are a different wire contract
247
+ * (`400 { error, error_description }`) than the authorize/token taxonomy.
248
+ */
249
+ type ClientRegistrationErrorCode = "invalid_redirect_uri" | "invalid_client_metadata";
250
+ /** A typed RFC 7591 registration failure; `message` becomes `error_description`. */
251
+ declare class ClientRegistrationError extends Error {
252
+ readonly code: ClientRegistrationErrorCode;
253
+ constructor(code: ClientRegistrationErrorCode, message: string);
254
+ }
255
+ interface ClientRegistrationValidationOptions {
256
+ /** Max `redirect_uris` entries (anonymous endpoint — rows must stay small). Default 5. */
257
+ maxRedirectUris?: number;
258
+ /** Max length of a single redirect URI. Default 512. */
259
+ maxRedirectUriLength?: number;
260
+ /** Max `client_name` length (after sanitization). Default 128. */
261
+ maxClientNameLength?: number;
262
+ /** Max `scope` string length. Default 256. */
263
+ maxScopeLength?: number;
264
+ /**
265
+ * Registration-time scope filter: the stored `scope` is the intersection of
266
+ * the requested tokens with this list. Omit to record the requested scope
267
+ * as-is. NOTE this is an upper bound on what the client may LATER request —
268
+ * the authorize-time grant is additionally bounded by
269
+ * `DynamicClientPolicyOptions.allowedScopes` (the server allow-list).
270
+ */
271
+ allowedScopes?: string[];
272
+ }
273
+ /**
274
+ * Validate + normalize an RFC 7591 registration request body into a
275
+ * {@link NewDynamicClient}, or THROW a {@link ClientRegistrationError}.
276
+ *
277
+ * Normalization is allowed by RFC 7591 §2 (the server MAY replace requested
278
+ * metadata with its own values) and is load-bearing for real connectors:
279
+ * `grant_types` / `response_types` are INTERSECTED with what the server
280
+ * supports (a connector requesting `["authorization_code", "refresh_token"]`
281
+ * registers with `["authorization_code"]` — the 201 echo of the narrowed set
282
+ * is the contract) rather than rejected. `token_endpoint_auth_method` defaults
283
+ * to `"none"` when absent (v1 is public-clients-only, so the RFC's
284
+ * `client_secret_basic` default is unsupported), but an EXPLICIT non-`"none"`
285
+ * ask is rejected — never silently downgrade a client that asked for a secret.
286
+ * Unknown fields are ignored and never echoed.
287
+ */
288
+ declare function validateClientRegistration(body: unknown, opts?: ClientRegistrationValidationOptions): NewDynamicClient;
289
+ interface DynamicClientRegistrationOptions {
290
+ store: DynamicClientStore;
291
+ /**
292
+ * Hard cap on stored registrations — `/register` is anonymous, so this
293
+ * bounds storage. REJECT-when-full (never evict): evicting a used row
294
+ * strands a connector that cached its `client_id`. Default 1000.
295
+ */
296
+ maxClients?: number;
297
+ /**
298
+ * Age after which a NEVER-USED registration (no `lastUsedAt`) is
299
+ * garbage-collected — lazily, on the next `register()` call, before the cap
300
+ * check. Omit to disable GC.
301
+ */
302
+ unusedClientTtlMs?: number;
303
+ /**
304
+ * Pluggable abuse guard, called with the validated metadata BEFORE anything
305
+ * is stored. Throw a {@link ClientRegistrationError} to reject with a 7591
306
+ * error; any other throw is a server fault (the endpoint answers 500).
307
+ * Rate limiting belongs at the consumer's ingress, not here.
308
+ */
309
+ guard?: (args: {
310
+ metadata: NewDynamicClient;
311
+ }) => void | Promise<void>;
312
+ validation?: ClientRegistrationValidationOptions;
313
+ /** Injectable clock for deterministic GC tests. Defaults to {@link defaultClock}. */
314
+ clock?: Clock;
315
+ }
316
+ /**
317
+ * The RFC 7591 registration operation behind `POST {issuer}/register`
318
+ * (OAUTH.md R2): validate → guard → lazy GC of never-used rows → hard cap →
319
+ * persist. Framework-free — `@aooth/auth-moost`'s controller endpoint is a
320
+ * thin HTTP adapter over `register()`, and non-moost servers can call it
321
+ * directly.
322
+ */
323
+ declare class DynamicClientRegistration {
324
+ private readonly store;
325
+ private readonly maxClients;
326
+ private readonly unusedClientTtlMs?;
327
+ private readonly guard?;
328
+ private readonly validation?;
329
+ private readonly clock;
330
+ constructor(opts: DynamicClientRegistrationOptions);
331
+ /** Validate and persist a registration request body; returns the minted client. */
332
+ register(body: unknown): Promise<DynamicClient>;
333
+ }
334
+ //#endregion
335
+ //#region src/authz/dynamic-client-policy.d.ts
336
+ interface DynamicClientPolicyOptions {
337
+ store: DynamicClientStore;
338
+ /**
339
+ * What a dynamic-client grant mints. Default: a `dynamic-session` access
340
+ * token with a 30-day TTL. Override to re-label or scope connector tokens
341
+ * (e.g. `{ kind: "mcp-session", ttl: 30 * 24 * 60 * 60_000 }`).
342
+ */
343
+ tokenPolicy?: TokenPolicy;
344
+ /**
345
+ * Server-side scope allow-list: the granted scope is
346
+ * `requested ∩ allowedScopes` (∩ the registration's `scope` when set).
347
+ * Omit to fall back to `requested ∩ registration scope` alone. NEVER treat
348
+ * the registration's self-declared `scope` as the allow-set by itself — it
349
+ * is attacker-supplied and also feeds the consent copy.
350
+ */
351
+ allowedScopes?: string[];
352
+ /** Injectable clock for deterministic `lastUsedAt` stamps. Defaults to wall-clock. */
353
+ clock?: Clock;
354
+ }
355
+ /**
356
+ * Policy for RFC 7591 dynamically-registered public clients (OAUTH.md R2) on
357
+ * the `ClientRedirectPolicy` seam. `resolveClient` authorizes the client +
358
+ * `redirect_uri` against ITS registered allowlist and resolves the granted
359
+ * scope + token policy; `authenticateClient` re-checks existence at `/token`
360
+ * (`token_endpoint_auth_method: "none"` ⇒ no secret — PKCE is the binding, and
361
+ * a registration garbage-collected mid-flight fails closed). Dynamic clients
362
+ * receive an access token only — NO `id_token` in v1 (OAUTH.md R6).
363
+ *
364
+ * INVARIANT: the returned {@link ResolvedClient} always carries `clientId`, so
365
+ * the minted code records it and the token endpoint's symmetric client binding
366
+ * applies — a dynamic code redeemed without (or with another) `client_id` is
367
+ * rejected exactly like a Tier-2 code.
368
+ */
369
+ declare class DynamicClientPolicy implements ClientRedirectPolicy {
370
+ private readonly store;
371
+ private readonly tokenPolicy;
372
+ private readonly allowedScopes?;
373
+ private readonly clock;
374
+ constructor(opts: DynamicClientPolicyOptions);
375
+ resolveClient(args: {
376
+ clientId?: string;
377
+ redirectUri: string;
378
+ scope?: string;
379
+ }): Promise<ResolvedClient>;
380
+ /**
381
+ * `/token`-side check: the client must still exist (fail closed when the
382
+ * registration was deleted/GC'd between authorize and redemption). No secret
383
+ * is checked — public client, PKCE is the binding; a spurious
384
+ * `client_secret` is ignored.
385
+ */
386
+ authenticateClient(args: {
387
+ clientId?: string;
388
+ clientSecret?: string;
389
+ }): Promise<void>;
390
+ /** Known-ness probe for `CompositeClientPolicy` dispatch. */
391
+ hasClient(clientId: string): Promise<boolean>;
392
+ private requireClient;
393
+ private grantScope;
200
394
  }
201
395
  //#endregion
202
396
  //#region src/authz/id-token-signer.d.ts
@@ -304,4 +498,133 @@ declare class NoopOidcClaimsResolver extends OidcClaimsResolver {
304
498
  /** `true` when `scope` (space-joined) grants `claim` — `"email"`/`"profile"` etc. */
305
499
  declare function scopeGrants(scope: string | undefined, claim: string): boolean;
306
500
  //#endregion
307
- export { type AuthCode, AuthCodeStore, AuthCodeStoreMemory, type AuthCodeStoreMemoryOptions, AuthorizeError, type AuthorizeErrorCode, type ClientRedirectPolicy, CompositeClientPolicy, type CompositeClientPolicyOptions, DEFAULT_PENDING_TTL_MS, type IdTokenAlg, type IdTokenClaims, IdTokenSigner, type IdTokenSignerOptions, LoopbackClientPolicy, type LoopbackClientPolicyOptions, type NewAuthCode, type NewPendingAuthorization, NoopOidcClaimsResolver, OidcClaimsResolver, type PendingAuthorization, PendingAuthorizationStore, PendingAuthorizationStoreMemory, type PendingAuthorizationStoreMemoryOptions, type RegisteredClient, RegisteredClientPolicy, type RegisteredClientPolicyOptions, type ResolvedClient, type TokenPolicy, isLoopbackRedirectUri, scopeGrants };
501
+ //#region src/authz/server-metadata.d.ts
502
+ /**
503
+ * Strip a trailing slash from an issuer identifier. RFC 8414 mandates EXACT
504
+ * (byte-identical) string comparison of issuer values, so every document that
505
+ * spells the issuer must canonicalize it the same way — same rule as
506
+ * `IdTokenSigner`. Shared by the AS metadata and protected-resource builders.
507
+ */
508
+ declare function canonicalizeIssuer(issuer: string): string;
509
+ /**
510
+ * RFC 8414 Authorization Server Metadata — the discovery document MCP connector
511
+ * clients fetch from `/.well-known/oauth-authorization-server` to locate the
512
+ * authorize / token / registration endpoints. Field names are the wire-format
513
+ * snake_case the RFC mandates; this is the JSON body, serialized as-is.
514
+ */
515
+ interface AuthorizationServerMetadata {
516
+ issuer: string;
517
+ authorization_endpoint: string;
518
+ token_endpoint: string;
519
+ registration_endpoint?: string;
520
+ jwks_uri?: string;
521
+ response_types_supported: string[];
522
+ grant_types_supported: string[];
523
+ code_challenge_methods_supported: string[];
524
+ token_endpoint_auth_methods_supported: string[];
525
+ scopes_supported?: string[];
526
+ }
527
+ interface BuildAuthorizationServerMetadataOptions {
528
+ /**
529
+ * The server's issuer identifier. A trailing slash is stripped so the
530
+ * document's `issuer` (and the endpoints derived from it) stay byte-identical
531
+ * to the `iss` a client compares for EXACT string equality (RFC 8414) — same
532
+ * canonicalization rule as `IdTokenSigner`.
533
+ */
534
+ issuer: string;
535
+ /** Override the authorization endpoint. Default: `${issuer}/authorize`. */
536
+ authorizationEndpoint?: string;
537
+ /** Override the token endpoint. Default: `${issuer}/token`. */
538
+ tokenEndpoint?: string;
539
+ /** RFC 7591 dynamic-registration endpoint. Omitted ⇒ field absent. */
540
+ registrationEndpoint?: string;
541
+ /** JWKS document URL (OIDC id_token verification). Omitted ⇒ field absent. */
542
+ jwksUri?: string;
543
+ /** Advertised scopes. Omitted ⇒ field absent (clients may request any). */
544
+ scopesSupported?: string[];
545
+ /**
546
+ * Token-endpoint client-auth methods. Default `["none", "client_secret_post"]`.
547
+ * `"none"` is ALWAYS included even if a custom list omits it — MCP connector
548
+ * clients are public clients (PKCE-bound, no secret) and refuse a server that
549
+ * doesn't advertise it; see {@link buildAuthorizationServerMetadata}.
550
+ */
551
+ tokenEndpointAuthMethodsSupported?: string[];
552
+ }
553
+ /**
554
+ * Build the RFC 8414 discovery document for this authorization server. Pure —
555
+ * no I/O, no framework: mount the result on any HTTP stack (including the
556
+ * RFC 8414 path-insertion form rooted at the HTTP origin,
557
+ * `/.well-known/oauth-authorization-server/<issuer-path>`).
558
+ *
559
+ * Capability fields are fixed to what the server actually implements:
560
+ * `response_types_supported` `["code"]`, `grant_types_supported`
561
+ * `["authorization_code"]`, `code_challenge_methods_supported` `["S256"]`
562
+ * (PKCE is mandatory — it is the binding for public clients). Optional fields
563
+ * are omitted entirely (no `undefined` keys) so the serialized JSON carries
564
+ * only what was configured.
565
+ */
566
+ declare function buildAuthorizationServerMetadata(opts: BuildAuthorizationServerMetadataOptions): AuthorizationServerMetadata;
567
+ //#endregion
568
+ //#region src/authz/resource-metadata.d.ts
569
+ /**
570
+ * RFC 9728 Protected Resource Metadata — the document a resource server (an
571
+ * MCP server, an API) publishes at `/.well-known/oauth-protected-resource` so
572
+ * clients can discover WHICH authorization server(s) guard it. Field names are
573
+ * the wire-format snake_case; this is the JSON body, serialized as-is.
574
+ */
575
+ interface ProtectedResourceMetadata {
576
+ resource: string;
577
+ authorization_servers: string[];
578
+ bearer_methods_supported: string[];
579
+ scopes_supported?: string[];
580
+ resource_name?: string;
581
+ }
582
+ interface BuildProtectedResourceMetadataOptions {
583
+ /** The resource identifier — the canonical URL of the protected resource. */
584
+ resource: string;
585
+ /**
586
+ * Issuer identifiers of the authorization server(s). Trailing slashes are
587
+ * stripped: clients match these against the AS metadata `issuer` for EXACT
588
+ * string equality (RFC 8414), so both documents must spell it byte-identically.
589
+ */
590
+ authorizationServers: string[];
591
+ /** Scopes the resource understands. Omitted ⇒ field absent. */
592
+ scopesSupported?: string[];
593
+ /** How the bearer token may be presented. Default `["header"]` (RFC 6750 §2.1). */
594
+ bearerMethodsSupported?: string[];
595
+ /** Human-readable display name. Omitted ⇒ field absent. */
596
+ resourceName?: string;
597
+ }
598
+ /**
599
+ * Build the RFC 9728 protected-resource document. Pure — no I/O, no framework;
600
+ * mount it on any HTTP stack. Optional fields are omitted entirely (no
601
+ * `undefined` keys) so the serialized JSON carries only what was configured.
602
+ */
603
+ declare function buildProtectedResourceMetadata(opts: BuildProtectedResourceMetadataOptions): ProtectedResourceMetadata;
604
+ interface WwwAuthenticateBearerChallengeOptions {
605
+ /**
606
+ * URL of the protected-resource document (RFC 9728 §5.1) — emitted as
607
+ * `resource_metadata="..."` so a client receiving a 401 can discover the
608
+ * authorization server without out-of-band configuration.
609
+ */
610
+ resourceMetadataUrl?: string;
611
+ /** RFC 6750 §3.1 error code (`invalid_token`, `insufficient_scope`, ...). */
612
+ error?: string;
613
+ /** Human-readable detail — emitted as `error_description="..."`. */
614
+ errorDescription?: string;
615
+ /** Scope(s) required to access the resource (space-separated). */
616
+ scope?: string;
617
+ /** Protection-space identifier (RFC 7235). */
618
+ realm?: string;
619
+ }
620
+ /**
621
+ * Build the VALUE of an RFC 6750 `WWW-Authenticate` Bearer challenge, e.g.
622
+ * `Bearer resource_metadata="https://...", error="invalid_token"`. With no
623
+ * options (or all absent) it degrades to the bare scheme `Bearer`. Every
624
+ * parameter is emitted as a quoted-string (RFC 6750 style) in a fixed order:
625
+ * `realm`, `error`, `error_description`, `resource_metadata`, `scope`. All
626
+ * values pass through {@link sanitizeQuotedStringValue} — see its security note.
627
+ */
628
+ declare function buildWwwAuthenticateBearerChallenge(opts?: WwwAuthenticateBearerChallengeOptions): string;
629
+ //#endregion
630
+ export { type AuthCode, AuthCodeStore, AuthCodeStoreMemory, type AuthCodeStoreMemoryOptions, type AuthorizationServerMetadata, AuthorizeError, type AuthorizeErrorCode, type BuildAuthorizationServerMetadataOptions, type BuildProtectedResourceMetadataOptions, type ClientRedirectPolicy, ClientRegistrationError, type ClientRegistrationErrorCode, type ClientRegistrationValidationOptions, CompositeClientPolicy, type CompositeClientPolicyOptions, DEFAULT_PENDING_TTL_MS, type DynamicClient, DynamicClientPolicy, type DynamicClientPolicyOptions, DynamicClientRegistration, type DynamicClientRegistrationOptions, DynamicClientStore, DynamicClientStoreMemory, type DynamicClientStoreMemoryOptions, type IdTokenAlg, type IdTokenClaims, IdTokenSigner, type IdTokenSignerOptions, LoopbackClientPolicy, type LoopbackClientPolicyOptions, type NewAuthCode, type NewDynamicClient, type NewPendingAuthorization, NoopOidcClaimsResolver, OidcClaimsResolver, type PendingAuthorization, PendingAuthorizationStore, PendingAuthorizationStoreMemory, type PendingAuthorizationStoreMemoryOptions, type ProtectedResourceMetadata, type RegisteredClient, RegisteredClientPolicy, type RegisteredClientPolicyOptions, type ResolvedClient, type TokenPolicy, type WwwAuthenticateBearerChallengeOptions, buildAuthorizationServerMetadata, buildProtectedResourceMetadata, buildWwwAuthenticateBearerChallenge, canonicalizeIssuer, isLoopbackRedirectUri, scopeGrants, validateClientRegistration };