@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/atscript-db.cjs +80 -3
- package/dist/atscript-db.d.cts +70 -2
- package/dist/atscript-db.d.mts +70 -2
- package/dist/atscript-db.mjs +78 -2
- package/dist/authz.cjs +380 -16
- package/dist/authz.d.cts +337 -14
- package/dist/authz.d.mts +337 -14
- package/dist/authz.mjs +366 -12
- package/dist/{auth-code-store-B_m51OSB.cjs → dynamic-client-store-DAStgv2j.cjs} +74 -0
- package/dist/{auth-code-store-Dqc4rEOx.mjs → dynamic-client-store-DLRfntHr.mjs} +63 -1
- package/dist/{auth-code-store-BZ88d_tq.d.cts → dynamic-client-store-DzqdUwnw.d.cts} +112 -1
- package/dist/{auth-code-store-Om5MXNUT.d.mts → dynamic-client-store-oHEsQaJg.d.mts} +112 -1
- package/package.json +14 -5
- package/src/atscript-db/auth-code.as +2 -0
- package/src/atscript-db/auth-code.as.d.ts +3 -0
- package/src/atscript-db/dynamic-client.as +44 -0
- package/src/atscript-db/dynamic-client.as.d.ts +59 -0
- package/src/atscript-db/pending-authorization.as +7 -0
- package/src/atscript-db/pending-authorization.as.d.ts +6 -0
package/dist/authz.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Clock } from "./clock-BjXa0LXb.cjs";
|
|
2
|
-
import { a as
|
|
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"
|
|
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
|
|
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
|
|
179
|
-
* `client_id`** (AUTH-SERVER.md §10): a request
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* redirect
|
|
184
|
-
*
|
|
185
|
-
*
|
|
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
|
-
|
|
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 };
|