@cloudflare/workers-oauth-provider 0.10.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -50
- package/dist/oauth-provider.d.ts +334 -89
- package/dist/oauth-provider.js +1393 -309
- package/docs/advanced-configuration.md +370 -0
- package/docs/migration-1.0.md +97 -0
- package/docs/resource-servers.md +153 -0
- package/package.json +4 -2
- package/skills/migrate-to-1.0/SKILL.md +35 -0
package/dist/oauth-provider.d.ts
CHANGED
|
@@ -324,6 +324,134 @@ declare class AuthorizationError extends Error {
|
|
|
324
324
|
}
|
|
325
325
|
declare function isValidOAuthScopeToken(scopeToken: string): boolean;
|
|
326
326
|
//#endregion
|
|
327
|
+
//#region src/oauth-resource-server.d.ts
|
|
328
|
+
/** RFC 9728 metadata published by a standalone OAuth resource server. */
|
|
329
|
+
interface OAuthResourceMetadata {
|
|
330
|
+
/** The one canonical HTTPS identifier for this protected resource. */
|
|
331
|
+
resource: string;
|
|
332
|
+
/** Authorization server issuers that can issue tokens for this resource. */
|
|
333
|
+
authorization_servers: string[];
|
|
334
|
+
/** Minimal scopes used to access the protected resource. */
|
|
335
|
+
scopes_supported?: string[];
|
|
336
|
+
/** Bearer-token presentation methods. This implementation supports only `header`. */
|
|
337
|
+
bearer_methods_supported?: string[];
|
|
338
|
+
/** Human-readable protected-resource name. */
|
|
339
|
+
resource_name?: string;
|
|
340
|
+
}
|
|
341
|
+
/** Successful result returned by the application's token validator. */
|
|
342
|
+
interface OAuthResourceTokenValidation<Props> {
|
|
343
|
+
/** Application data exposed to the protected handler as `ctx.props`. */
|
|
344
|
+
props: Props;
|
|
345
|
+
/** Canonical audience to which the token is bound. */
|
|
346
|
+
audience: string;
|
|
347
|
+
/** Optional absolute expiry as seconds since the Unix epoch. */
|
|
348
|
+
expiresAt?: number;
|
|
349
|
+
/**
|
|
350
|
+
* Scopes the token carries. `OAuthAuthorizationServer.validateToken()` always reports them;
|
|
351
|
+
* a handler needs them to answer with {@link insufficientScope}.
|
|
352
|
+
*/
|
|
353
|
+
scope?: string[];
|
|
354
|
+
/** Subject the token was issued for. */
|
|
355
|
+
userId?: string;
|
|
356
|
+
/** Client the token was issued to. */
|
|
357
|
+
clientId?: string;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* What the host verified about the bearer token before calling the handler, exposed as
|
|
361
|
+
* `ctx.auth` beside the application's `ctx.props`. Both hosts set it: `OAuthResourceServer`
|
|
362
|
+
* from the validator's result, `OAuthProvider` from its own token record.
|
|
363
|
+
*/
|
|
364
|
+
interface OAuthResourceAuth {
|
|
365
|
+
/** The token as presented. It is already in the request's `Authorization` header; never log it. */
|
|
366
|
+
token: string;
|
|
367
|
+
/** Canonical resource the token was accepted for. */
|
|
368
|
+
audience: string;
|
|
369
|
+
/** Absolute expiry as seconds since the Unix epoch, when known. */
|
|
370
|
+
expiresAt?: number;
|
|
371
|
+
/** Scopes the token carries; empty when the validator reported none. */
|
|
372
|
+
scope: string[];
|
|
373
|
+
/** Subject the token was issued for, when known. */
|
|
374
|
+
userId?: string;
|
|
375
|
+
/** Client the token was issued to, when known. */
|
|
376
|
+
clientId?: string;
|
|
377
|
+
}
|
|
378
|
+
/** The execution context a protected handler receives: `props` from the validator, `auth` from the host. */
|
|
379
|
+
type OAuthResourceContext<Props> = ExecutionContext<Props> & {
|
|
380
|
+
readonly auth: OAuthResourceAuth;
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Validates a bearer token presented to one resource. `null` for a token that is not
|
|
384
|
+
* valid for that resource; a thrown error fails closed as `503`.
|
|
385
|
+
*/
|
|
386
|
+
type OAuthResourceTokenValidator<Props> = (resource: string, token: string) => Promise<OAuthResourceTokenValidation<Props> | null>;
|
|
387
|
+
/**
|
|
388
|
+
* Protected application handler called after successful token validation: an object with
|
|
389
|
+
* `fetch`, or a `WorkerEntrypoint` subclass instantiated per request with `(ctx, env)`. Either
|
|
390
|
+
* way `ctx.props` carries what the validator returned and `ctx.auth` what the host verified.
|
|
391
|
+
*/
|
|
392
|
+
type OAuthResourceHandler<Env, Props> = {
|
|
393
|
+
fetch(request: Request, env: Env, ctx: OAuthResourceContext<Props>): Response | Promise<Response>;
|
|
394
|
+
} | (new (ctx: OAuthResourceContext<Props>, env: Env) => {
|
|
395
|
+
fetch(request: Request): Response | Promise<Response>;
|
|
396
|
+
});
|
|
397
|
+
/** Configuration for {@link OAuthResourceServer}. */
|
|
398
|
+
interface OAuthResourceServerOptions<Env = Cloudflare.Env, Props = unknown> {
|
|
399
|
+
/** RFC 9728 metadata, including this server's one canonical resource. */
|
|
400
|
+
resourceMetadata: OAuthResourceMetadata;
|
|
401
|
+
/** Application handler for the canonical resource URL and its path descendants. */
|
|
402
|
+
handler: OAuthResourceHandler<Env, Props>;
|
|
403
|
+
/**
|
|
404
|
+
* The validator for a request. The host calls what you return with this server's
|
|
405
|
+
* canonical resource and the bearer token, so neither is repeated here.
|
|
406
|
+
*
|
|
407
|
+
* - The authorization server in another Worker, over a Service Binding to a
|
|
408
|
+
* `WorkerEntrypoint` that exposes `OAuthAuthorizationServer.validateToken()`:
|
|
409
|
+
* `(env) => env.AUTH_SERVER.validateToken`
|
|
410
|
+
* - The authorization server in this Worker:
|
|
411
|
+
* `(env) => (resource, token) => authorizationServer.validateToken(resource, token, env)`
|
|
412
|
+
* - Anything else, at your own risk: a function that validates the token for `resource`.
|
|
413
|
+
*/
|
|
414
|
+
validateToken(env: Env, request: Request): OAuthResourceTokenValidator<Props>;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* The response for a valid token that lacks the scopes an operation needs (RFC 6750 §3.1,
|
|
418
|
+
* MCP scope challenge handling): `403` with `WWW-Authenticate: Bearer error="insufficient_scope"`,
|
|
419
|
+
* the `scope` the operation requires and the `resource_metadata` URL the client already knows.
|
|
420
|
+
* Name every scope the operation needs in one response; clients treat the list as complete.
|
|
421
|
+
*
|
|
422
|
+
* ```ts
|
|
423
|
+
* if (!ctx.auth.scope.includes('calendar:write')) return insufficientScope(ctx.auth, ['calendar:write']);
|
|
424
|
+
* ```
|
|
425
|
+
*/
|
|
426
|
+
declare function insufficientScope(auth: OAuthResourceAuth, scope: string[], description?: string): Response;
|
|
427
|
+
/**
|
|
428
|
+
* One OAuth protected resource, in the authorization server's Worker or its own. Publishes
|
|
429
|
+
* RFC 9728 metadata, challenges unauthenticated requests, validates the bearer token through
|
|
430
|
+
* the configured `validateToken`, enforces audience and expiry on what it returns, and routes
|
|
431
|
+
* only the canonical resource and its descendants to the application handler.
|
|
432
|
+
*
|
|
433
|
+
* `export default new OAuthResourceServer({ … })`, or dispatch to `fetch()` from your own
|
|
434
|
+
* router, exactly as with `OAuthAuthorizationServer`.
|
|
435
|
+
*/
|
|
436
|
+
declare class OAuthResourceServer<Env = Cloudflare.Env, Props = unknown> {
|
|
437
|
+
#private;
|
|
438
|
+
constructor(options: OAuthResourceServerOptions<Env, Props>);
|
|
439
|
+
fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response>;
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/oauth-resource.d.ts
|
|
443
|
+
/** Validate an RFC 3986-safe HTTP(S) resource identifier for RFC 8707. */
|
|
444
|
+
declare function validateResourceUri(uri: string): boolean;
|
|
445
|
+
/**
|
|
446
|
+
* Whether a requested resource identifies a granted or configured resource.
|
|
447
|
+
* Only ASCII case in the URI scheme and host is ignored, and an empty path is
|
|
448
|
+
* equivalent to `/` (RFC 3986 §6.2.3), so a client that round-trips
|
|
449
|
+
* `https://example.com` through a URL parser as `https://example.com/` still
|
|
450
|
+
* names it. Port, path, query, a trailing slash after a path segment, user
|
|
451
|
+
* information, and every other byte remain significant.
|
|
452
|
+
*/
|
|
453
|
+
declare function resourceMatches(requested: string, granted: string): boolean;
|
|
454
|
+
//#endregion
|
|
327
455
|
//#region src/oauth-provider.d.ts
|
|
328
456
|
/**
|
|
329
457
|
* Enum representing OAuth grant types
|
|
@@ -337,8 +465,8 @@ declare enum GrantType {
|
|
|
337
465
|
/**
|
|
338
466
|
* Aliases for either type of Handler that makes .fetch required
|
|
339
467
|
*/
|
|
340
|
-
type ExportedHandlerWithFetch<Env = Cloudflare.Env> = ExportedHandler<Env> & Pick<Required<ExportedHandler<Env>>, 'fetch'>;
|
|
341
|
-
type WorkerEntrypointWithFetch<Env = Cloudflare.Env> = WorkerEntrypoint<Env> & {
|
|
468
|
+
type ExportedHandlerWithFetch<Env = Cloudflare.Env, Props = unknown> = ExportedHandler<Env, unknown, unknown, Props> & Pick<Required<ExportedHandler<Env, unknown, unknown, Props>>, 'fetch'>;
|
|
469
|
+
type WorkerEntrypointWithFetch<Env = Cloudflare.Env, Props = {}> = WorkerEntrypoint<Env, Props> & {
|
|
342
470
|
fetch: NonNullable<WorkerEntrypoint['fetch']>;
|
|
343
471
|
};
|
|
344
472
|
/**
|
|
@@ -382,16 +510,36 @@ interface TokenExchangeCallbackResult {
|
|
|
382
510
|
/**
|
|
383
511
|
* Override the default refresh token TTL (time-to-live) for this specific grant.
|
|
384
512
|
* Value should be in seconds.
|
|
385
|
-
* Note: This is only honored during authorization code exchange.
|
|
386
|
-
* refresh token exchange
|
|
513
|
+
* Note: This is only honored during authorization code exchange. Returning it during
|
|
514
|
+
* refresh token exchange is rejected with `invalid_request`; to extend a grant on
|
|
515
|
+
* refresh, return `refreshTokenIdleTTL`.
|
|
387
516
|
*/
|
|
388
517
|
refreshTokenTTL?: number;
|
|
518
|
+
/**
|
|
519
|
+
* Idle lifetime for this grant's refresh token, in seconds from this refresh. The
|
|
520
|
+
* grant then expires that long after this refresh unless it is refreshed again. Only
|
|
521
|
+
* honored during refresh token exchange; returning it for another grant type is
|
|
522
|
+
* rejected with `invalid_request`. Overrides the provider's `refreshTokenIdleTTL`
|
|
523
|
+
* for this refresh.
|
|
524
|
+
*
|
|
525
|
+
* Useful when the Worker is itself an OAuth client and has just rotated an upstream
|
|
526
|
+
* refresh token: return the upstream lifetime and the grant lives exactly as long as
|
|
527
|
+
* the credentials it proxies. Must be an integer of at least 60 seconds.
|
|
528
|
+
*/
|
|
529
|
+
refreshTokenIdleTTL?: number;
|
|
389
530
|
/**
|
|
390
531
|
* Optional scopes for the new access token. Values outside the scope ceiling
|
|
391
532
|
* for the current grant flow are ignored. If omitted, the effective requested
|
|
392
533
|
* scopes are used.
|
|
393
534
|
*/
|
|
394
535
|
accessTokenScope?: string[];
|
|
536
|
+
/**
|
|
537
|
+
* Permit a token exchange whose authenticated client differs from the client the
|
|
538
|
+
* subject token's grant was issued to. Cross-client exchange is rejected with
|
|
539
|
+
* `invalid_request` unless the callback returns `true` here, so impersonation
|
|
540
|
+
* across clients is always a deliberate policy decision.
|
|
541
|
+
*/
|
|
542
|
+
allowCrossClientExchange?: boolean;
|
|
395
543
|
}
|
|
396
544
|
/**
|
|
397
545
|
* Options for token exchange callback functions
|
|
@@ -402,9 +550,17 @@ interface TokenExchangeCallbackOptions {
|
|
|
402
550
|
*/
|
|
403
551
|
grantType: GrantType;
|
|
404
552
|
/**
|
|
405
|
-
* Client
|
|
553
|
+
* Client authenticated on this token request. For `authorization_code` and
|
|
554
|
+
* `refresh_token` it is always the grant's client. For token exchange it is the
|
|
555
|
+
* exchanging client, which may differ from {@link subjectClientId}.
|
|
406
556
|
*/
|
|
407
557
|
clientId: string;
|
|
558
|
+
/**
|
|
559
|
+
* Client the underlying grant was issued to. Equal to `clientId` except during a
|
|
560
|
+
* cross-client token exchange, which the callback must explicitly allow with
|
|
561
|
+
* {@link TokenExchangeCallbackResult.allowCrossClientExchange}.
|
|
562
|
+
*/
|
|
563
|
+
subjectClientId: string;
|
|
408
564
|
/**
|
|
409
565
|
* User who authorized this grant
|
|
410
566
|
*/
|
|
@@ -425,6 +581,8 @@ interface TokenExchangeCallbackOptions {
|
|
|
425
581
|
* Effective scopes selected for this token before applying the callback result.
|
|
426
582
|
*/
|
|
427
583
|
requestedScope: string[];
|
|
584
|
+
/** Canonical protected resource selected for this grant and token. */
|
|
585
|
+
resource: string;
|
|
428
586
|
/**
|
|
429
587
|
* Application-specific properties currently associated with this grant
|
|
430
588
|
*/
|
|
@@ -504,11 +662,40 @@ interface ResolveExternalTokenResult {
|
|
|
504
662
|
*
|
|
505
663
|
* A JWT may carry this value as an `aud` claim. For an opaque API token or
|
|
506
664
|
* PAT, the callback can supply the local resource URI as policy after
|
|
507
|
-
* successful validation.
|
|
508
|
-
*
|
|
665
|
+
* successful validation. This value is required and must identify the
|
|
666
|
+
* configured canonical `resourceMetadata.resource`: ASCII case in the scheme
|
|
667
|
+
* and host is folded and an empty path equals `/`, while port, path, query,
|
|
668
|
+
* and trailing slash are compared exactly.
|
|
509
669
|
*/
|
|
510
|
-
audience
|
|
670
|
+
audience: string;
|
|
671
|
+
}
|
|
672
|
+
/** RFC 9728 metadata owned by one protected resource server. */
|
|
673
|
+
interface OAuthProtectedResourceMetadata {
|
|
674
|
+
/**
|
|
675
|
+
* The protected resource identifier HTTPS URL (RFC 9728 `resource` field).
|
|
676
|
+
* Configure an RFC 3986-safe HTTPS producer URL with lowercase scheme/host
|
|
677
|
+
* and no userinfo, default port, fragment, or dot segments.
|
|
678
|
+
*/
|
|
679
|
+
resource: string;
|
|
680
|
+
/**
|
|
681
|
+
* Authorization server issuers that can issue tokens for this resource.
|
|
682
|
+
* In the legacy combined configuration this defaults to the token endpoint
|
|
683
|
+
* origin. In the role-based configuration it defaults to the configured AS
|
|
684
|
+
* issuer.
|
|
685
|
+
*/
|
|
686
|
+
authorization_servers?: string[];
|
|
687
|
+
/** Minimal scopes required for basic protected-resource functionality. */
|
|
688
|
+
scopes_supported?: string[];
|
|
689
|
+
/** Methods by which bearer tokens can be presented. Defaults to `["header"]`. */
|
|
690
|
+
bearer_methods_supported?: string[];
|
|
691
|
+
/** Human-readable name for this resource. */
|
|
692
|
+
resource_name?: string;
|
|
511
693
|
}
|
|
694
|
+
/**
|
|
695
|
+
* Existing combined authorization-server and protected-resource configuration.
|
|
696
|
+
* This shape remains supported in 1.0 and is normalized to a one-resource
|
|
697
|
+
* role-based provider internally.
|
|
698
|
+
*/
|
|
512
699
|
interface OAuthProviderOptions<Env = Cloudflare.Env> {
|
|
513
700
|
/**
|
|
514
701
|
* URL(s) for API routes. Requests with URLs starting with any of these prefixes
|
|
@@ -572,6 +759,18 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
|
|
|
572
759
|
* For example: 3600 = 1 hour, 2592000 = 30 days
|
|
573
760
|
*/
|
|
574
761
|
refreshTokenTTL?: number;
|
|
762
|
+
/**
|
|
763
|
+
* Idle lifetime for refresh tokens in seconds. When set, every successful refresh
|
|
764
|
+
* moves the grant's expiry to this many seconds after the refresh, so a grant lives
|
|
765
|
+
* for as long as the client keeps using it and expires once it has been idle for
|
|
766
|
+
* this long. `refreshTokenTTL` still sets the lifetime of a new grant, and
|
|
767
|
+
* `tokenExchangeCallback` can override the idle lifetime per refresh by returning
|
|
768
|
+
* `refreshTokenIdleTTL`. Must be an integer of at least 60 seconds.
|
|
769
|
+
*
|
|
770
|
+
* Leave unset for a fixed lifetime, where a grant expires `refreshTokenTTL` seconds
|
|
771
|
+
* after the code exchange however often it is refreshed.
|
|
772
|
+
*/
|
|
773
|
+
refreshTokenIdleTTL?: number;
|
|
575
774
|
/**
|
|
576
775
|
* Time-to-live for dynamically registered clients in seconds.
|
|
577
776
|
* Defaults to 90 days (7,776,000 seconds).
|
|
@@ -655,10 +854,10 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
|
|
|
655
854
|
*
|
|
656
855
|
* If the function returns a Response, that will be used in place of the OAuthProvider's default one.
|
|
657
856
|
*
|
|
658
|
-
* `internal`
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
857
|
+
* `internal` carries the stable, server-side-only reason for the error — often more
|
|
858
|
+
* specific than what the wire deliberately reveals (RFC 6749 §5.2), e.g. which refresh-token
|
|
859
|
+
* check failed or which EMA validation fired. It is never placed on the wire. See
|
|
860
|
+
* {@link OAuthErrorInternal}.
|
|
662
861
|
*
|
|
663
862
|
* `request` (when present) is the HTTP request that produced the error response, so the
|
|
664
863
|
* callback can correlate the error with per-request state such as request-keyed telemetry.
|
|
@@ -670,11 +869,7 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
|
|
|
670
869
|
description: string;
|
|
671
870
|
status: number;
|
|
672
871
|
headers: Record<string, string>;
|
|
673
|
-
internal
|
|
674
|
-
category: string;
|
|
675
|
-
reason: string;
|
|
676
|
-
detail?: unknown;
|
|
677
|
-
};
|
|
872
|
+
internal: OAuthErrorInternal;
|
|
678
873
|
request?: Request;
|
|
679
874
|
}) => Response | void;
|
|
680
875
|
/**
|
|
@@ -685,59 +880,52 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
|
|
|
685
880
|
*/
|
|
686
881
|
clientIdMetadataDocumentEnabled?: boolean;
|
|
687
882
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
* grant such as `https://server.com` to accept `https://server.com/mcp`, but
|
|
691
|
-
* also ignores path and query differences. Configured canonical resources
|
|
692
|
-
* always use exact matching.
|
|
693
|
-
*
|
|
694
|
-
* Defaults to false (strict exact matching per RFC 8707).
|
|
695
|
-
*
|
|
696
|
-
* @deprecated This comparison is unsafe for shared-origin multi-path or
|
|
697
|
-
* multi-tenant deployments. Prefer configuring `resourceMetadata.resource`.
|
|
883
|
+
* Metadata for RFC 9728 OAuth 2.0 Protected Resource Metadata.
|
|
884
|
+
* Controls the response served at /.well-known/oauth-protected-resource.
|
|
698
885
|
*/
|
|
699
|
-
|
|
886
|
+
resourceMetadata: OAuthProtectedResourceMetadata;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Functional authorization-server surface for the role-based API.
|
|
890
|
+
* The application owns the interactive authorization route and uses
|
|
891
|
+
* `getOAuthApi()` to parse and complete it; `fetch()` serves protocol-owned AS
|
|
892
|
+
* endpoints such as metadata, token, revocation, and optional registration.
|
|
893
|
+
*/
|
|
894
|
+
type OAuthAuthorizationServerOptions<Env = Cloudflare.Env> = Omit<OAuthProviderOptions<Env>, 'apiRoute' | 'apiHandler' | 'apiHandlers' | 'defaultHandler' | 'resourceMetadata' | 'resolveExternalToken'> & {
|
|
895
|
+
/** Canonical RFC 8414 issuer. */
|
|
896
|
+
issuer: string;
|
|
700
897
|
/**
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
898
|
+
* Canonical identifiers of every protected resource this authorization server issues
|
|
899
|
+
* tokens for, whether hosted in this Worker through `OAuthResourceServer` or by another
|
|
900
|
+
* Worker or service. At least one is required. The registry is fixed at construction,
|
|
901
|
+
* so `defaultResource` and `legacyGrantResource` are checked before the first request,
|
|
902
|
+
* and `validateToken()` rejects for a resource that is not listed.
|
|
706
903
|
*/
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
* Methods by which bearer tokens can be presented to this resource.
|
|
733
|
-
* Defaults to ["header"].
|
|
734
|
-
*/
|
|
735
|
-
bearer_methods_supported?: string[];
|
|
736
|
-
/**
|
|
737
|
-
* Human-readable name for this resource.
|
|
738
|
-
*/
|
|
739
|
-
resource_name?: string;
|
|
740
|
-
};
|
|
904
|
+
resources: readonly string[];
|
|
905
|
+
/** Resource selected for a new authorization request that omits `resource`. */
|
|
906
|
+
defaultResource?: string;
|
|
907
|
+
/**
|
|
908
|
+
* Migration destination for pre-resource grants and access tokens. Changing it
|
|
909
|
+
* re-targets every surviving unbound record, so keep it fixed for the migration window.
|
|
910
|
+
*/
|
|
911
|
+
legacyGrantResource?: string;
|
|
912
|
+
};
|
|
913
|
+
/** Audience-checked token context suitable for a private Service Binding. */
|
|
914
|
+
interface ValidatedAccessToken<T = any> {
|
|
915
|
+
props: T;
|
|
916
|
+
audience: string;
|
|
917
|
+
expiresAt: number;
|
|
918
|
+
scope: string[];
|
|
919
|
+
userId: string;
|
|
920
|
+
clientId: string;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* The RPC surface a `WorkerEntrypoint` exposes around {@link OAuthAuthorizationServer.validateToken},
|
|
924
|
+
* as a resource Worker sees it through a Service Binding. Hand a resource server the bound
|
|
925
|
+
* method as its validator: `validateToken: (env) => env.AUTH_SERVER.validateToken`.
|
|
926
|
+
*/
|
|
927
|
+
interface AuthorizationServerBinding<Props = any> {
|
|
928
|
+
validateToken(resource: string, token: string): Promise<ValidatedAccessToken<Props> | null>;
|
|
741
929
|
}
|
|
742
930
|
/**
|
|
743
931
|
* Helper methods for OAuth operations provided to handler functions
|
|
@@ -852,9 +1040,11 @@ interface ExchangeTokenOptions {
|
|
|
852
1040
|
*/
|
|
853
1041
|
scope?: string[];
|
|
854
1042
|
/**
|
|
855
|
-
* Optional target audience/resource for the new token (maps to
|
|
1043
|
+
* Optional canonical target audience/resource for the new token (maps to the
|
|
1044
|
+
* resource parameter per RFC 8707). When present, it must match the
|
|
1045
|
+
* provider's configured resource.
|
|
856
1046
|
*/
|
|
857
|
-
aud?: string
|
|
1047
|
+
aud?: string;
|
|
858
1048
|
/**
|
|
859
1049
|
* Optional TTL override for the new token in seconds (must not exceed subject token's remaining lifetime)
|
|
860
1050
|
*/
|
|
@@ -893,9 +1083,10 @@ interface AuthRequest {
|
|
|
893
1083
|
*/
|
|
894
1084
|
codeChallengeMethod?: string;
|
|
895
1085
|
/**
|
|
896
|
-
*
|
|
1086
|
+
* Canonical target resource (RFC 8707). Parsed authorization requests
|
|
1087
|
+
* always contain the provider's configured resource.
|
|
897
1088
|
*/
|
|
898
|
-
resource?: string
|
|
1089
|
+
resource?: string;
|
|
899
1090
|
/**
|
|
900
1091
|
* Authorization server issuer recorded while parsing this request.
|
|
901
1092
|
* Include it as `iss` in successful and error authorization responses.
|
|
@@ -1015,10 +1206,12 @@ interface CompleteAuthorizationOptions {
|
|
|
1015
1206
|
*/
|
|
1016
1207
|
revokeExistingGrants?: boolean;
|
|
1017
1208
|
/**
|
|
1018
|
-
*
|
|
1019
|
-
* grants.
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1209
|
+
* How many grants written by a version before 1.0 are read at once while
|
|
1210
|
+
* revoking existing grants. Grants written by 1.0 or later carry their client,
|
|
1211
|
+
* resource and redirect URI as KV key metadata and are matched from `list()`
|
|
1212
|
+
* without being read, so this only bounds the fan-out for older grants. Only
|
|
1213
|
+
* used when revokeExistingGrants is not false. Must be a positive integer;
|
|
1214
|
+
* values above 1000 are clamped. Defaults to 50.
|
|
1022
1215
|
*/
|
|
1023
1216
|
revokeExistingGrantsBatchSize?: number;
|
|
1024
1217
|
}
|
|
@@ -1125,7 +1318,7 @@ interface TokenResponse {
|
|
|
1125
1318
|
* Resource indicator(s) for the issued access token (RFC 8707 Section 2.2)
|
|
1126
1319
|
* SHOULD be included to indicate the resource server(s) for which the token is valid
|
|
1127
1320
|
*/
|
|
1128
|
-
resource
|
|
1321
|
+
resource: string;
|
|
1129
1322
|
}
|
|
1130
1323
|
/**
|
|
1131
1324
|
* Shared fields for Token and TokenSummary
|
|
@@ -1328,6 +1521,8 @@ interface GrantSummary {
|
|
|
1328
1521
|
* grants created before this field was introduced.
|
|
1329
1522
|
*/
|
|
1330
1523
|
redirectUri?: string;
|
|
1524
|
+
/** Canonical protected resource, absent only on a pre-resource legacy grant. */
|
|
1525
|
+
resource?: string | string[];
|
|
1331
1526
|
}
|
|
1332
1527
|
/**
|
|
1333
1528
|
* OAuth 2.0 Provider implementation for Cloudflare Workers
|
|
@@ -1360,6 +1555,43 @@ declare class OAuthProvider<Env = Cloudflare.Env> {
|
|
|
1360
1555
|
*/
|
|
1361
1556
|
purgeExpiredData(env: Env, options?: PurgeOptions): Promise<PurgeResult>;
|
|
1362
1557
|
}
|
|
1558
|
+
/**
|
|
1559
|
+
* The authorization-server role on its own. It serves discovery, token, revocation and
|
|
1560
|
+
* registration endpoints from `fetch()`, exposes the interactive authorization flow through
|
|
1561
|
+
* `getOAuthApi()`, and validates its access tokens for any declared resource through
|
|
1562
|
+
* `validateToken()`. Resources are hosted, in this Worker or another, by
|
|
1563
|
+
* `OAuthResourceServer`, whose `validateToken` points back here.
|
|
1564
|
+
*/
|
|
1565
|
+
declare class OAuthAuthorizationServer<Env = Cloudflare.Env> {
|
|
1566
|
+
#private;
|
|
1567
|
+
constructor(options: OAuthAuthorizationServerOptions<Env>);
|
|
1568
|
+
/**
|
|
1569
|
+
* Validate an access token for one declared resource: the token's KV record is loaded, its
|
|
1570
|
+
* audience checked against `resource`, and its props decrypted. Resolves `null` for a token
|
|
1571
|
+
* that is not valid for that resource, and rejects for a resource this server does not
|
|
1572
|
+
* declare.
|
|
1573
|
+
*
|
|
1574
|
+
* This is what a resource server calls, whether it shares this Worker or reaches it over a
|
|
1575
|
+
* Service Binding. For the latter, expose it from a `WorkerEntrypoint`:
|
|
1576
|
+
*
|
|
1577
|
+
* ```ts
|
|
1578
|
+
* export default class AuthServer extends WorkerEntrypoint<Env> {
|
|
1579
|
+
* fetch(request: Request) {
|
|
1580
|
+
* return authorizationServer.fetch(request, this.env, this.ctx);
|
|
1581
|
+
* }
|
|
1582
|
+
* validateToken(resource: string, token: string) {
|
|
1583
|
+
* return authorizationServer.validateToken(resource, token, this.env);
|
|
1584
|
+
* }
|
|
1585
|
+
* }
|
|
1586
|
+
* ```
|
|
1587
|
+
*/
|
|
1588
|
+
validateToken<T = any>(resource: string, token: string, env: Env): Promise<ValidatedAccessToken<T> | null>;
|
|
1589
|
+
/** Serve protocol-owned authorization-server endpoints, excluding the application authorization UI. */
|
|
1590
|
+
fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response>;
|
|
1591
|
+
/** Obtain helpers for scheduled jobs or custom code outside a fetch dispatch. */
|
|
1592
|
+
getOAuthApi(env: Env): OAuthHelpers;
|
|
1593
|
+
purgeExpiredData(env: Env, options?: PurgeOptions): Promise<PurgeResult>;
|
|
1594
|
+
}
|
|
1363
1595
|
/**
|
|
1364
1596
|
* Gets OAuthHelpers for the given environment
|
|
1365
1597
|
* @param options - Configuration options for the OAuth provider
|
|
@@ -1371,6 +1603,24 @@ declare function getOAuthApi<Env = Cloudflare.Env>(options: OAuthProviderOptions
|
|
|
1371
1603
|
* Error class for OAuth operations
|
|
1372
1604
|
* Carries OAuth error code and description for proper error responses
|
|
1373
1605
|
*/
|
|
1606
|
+
/**
|
|
1607
|
+
* The internal reason behind an error response, forwarded to the `onError` hook and never
|
|
1608
|
+
* placed on the wire. `category` is a stable kebab-case subsystem (`client-authentication`,
|
|
1609
|
+
* `authorization-code-grant`, `refresh-token-grant`, `token-exchange-grant`,
|
|
1610
|
+
* `token-endpoint-request`, `token-revocation`, `token-issuance`, `resource-indicator`,
|
|
1611
|
+
* `client-registration`, `client-id-metadata-document`, `protected-resource`,
|
|
1612
|
+
* `enterprise-managed-authorization`, `token-exchange-callback`); `reason` is a stable
|
|
1613
|
+
* snake_case slug naming the exact check that failed. Treat both like enum members in semver.
|
|
1614
|
+
* `detail` may carry structured context such as a caught error; it is never a secret.
|
|
1615
|
+
*/
|
|
1616
|
+
interface OAuthErrorInternal {
|
|
1617
|
+
/** Stable kebab-case subsystem that produced the error. */
|
|
1618
|
+
category: string;
|
|
1619
|
+
/** Stable snake_case slug naming the failed check, often more specific than the wire description. */
|
|
1620
|
+
reason: string;
|
|
1621
|
+
/** Optional structured context, e.g. the caught error or the offending parameter. */
|
|
1622
|
+
detail?: unknown;
|
|
1623
|
+
}
|
|
1374
1624
|
/**
|
|
1375
1625
|
* Options accepted by the {@link OAuthError} constructor.
|
|
1376
1626
|
*/
|
|
@@ -1392,6 +1642,13 @@ interface OAuthErrorOptions {
|
|
|
1392
1642
|
* number of seconds or an HTTP-date.
|
|
1393
1643
|
*/
|
|
1394
1644
|
headers?: Record<string, string>;
|
|
1645
|
+
/**
|
|
1646
|
+
* Internal reason forwarded to the `onError` hook and never sent on the wire. The library
|
|
1647
|
+
* sets it on every error it originates; a `tokenExchangeCallback` may set its own. An
|
|
1648
|
+
* `OAuthError` thrown without one reaches `onError` as
|
|
1649
|
+
* `{ category: 'token-exchange-callback', reason: 'callback_error', detail: error }`.
|
|
1650
|
+
*/
|
|
1651
|
+
internal?: OAuthErrorInternal;
|
|
1395
1652
|
}
|
|
1396
1653
|
/**
|
|
1397
1654
|
* Structured OAuth 2.0 token-endpoint error.
|
|
@@ -1523,18 +1780,6 @@ declare class CimdFetchError extends Error {
|
|
|
1523
1780
|
*/
|
|
1524
1781
|
constructor(metadataUrl: string, cause: unknown);
|
|
1525
1782
|
}
|
|
1526
|
-
/**
|
|
1527
|
-
* Validates a resource URI per RFC 8707 Section 2
|
|
1528
|
-
* @param uri - The URI string to validate
|
|
1529
|
-
* @returns true if valid, false otherwise
|
|
1530
|
-
*/
|
|
1531
|
-
declare function validateResourceUri(uri: string): boolean;
|
|
1532
|
-
/**
|
|
1533
|
-
* Checks if a requested resource matches a granted resource.
|
|
1534
|
-
* When originOnly is true, compares only the origin (scheme + host + port),
|
|
1535
|
-
* allowing path-aware resources to match origin-only grants.
|
|
1536
|
-
*/
|
|
1537
|
-
declare function resourceMatches(requested: string, granted: string, originOnly: boolean): boolean;
|
|
1538
1783
|
/**
|
|
1539
1784
|
* Decodes a base64url-encoded string to bytes.
|
|
1540
1785
|
*/
|
|
@@ -1551,4 +1796,4 @@ declare function getJwtCryptoAlgorithms(alg: string): {
|
|
|
1551
1796
|
verifyAlgorithm: Parameters<SubtleCrypto['verify']>[0];
|
|
1552
1797
|
};
|
|
1553
1798
|
//#endregion
|
|
1554
|
-
export { AuthRequest, AuthorizationError, type AuthorizationErrorCode, type AuthorizationErrorOptions, CimdFetchError, ClientInfo, ClientRegistrationCallbackOptions, ClientRegistrationCallbackResult, CompleteAuthorizationOptions, type EmaClaimsMapper, type EmaClaimsMapperInput, type EmaClaimsMapperResult, type EmaIdJagClaims, type EmaOptions, type EmaTrustedIssuer, type EmaTrustedIssuerResolver, type EmaTrustedIssuerResolverInput, type EmaValidationError, ExchangeTokenOptions, ExternalTokenError, ExternalTokenErrorOptions, Grant, GrantSummary, GrantType, ListOptions, ListResult, OAuthError, OAuthErrorOptions, OAuthHelpers, OAuthProvider, OAuthProvider as default, OAuthProviderOptions, OAuthTokenErrorCode, PurgeOptions, PurgeResult, ResolveExternalTokenInput, ResolveExternalTokenResult, Token, TokenBase, TokenExchangeCallbackOptions, TokenExchangeCallbackResult, TokenSummary, base64UrlToBytes, getJwtCryptoAlgorithms, getOAuthApi, isValidOAuthScopeToken, parseJwtJsonPart, resourceMatches, validateResourceUri };
|
|
1799
|
+
export { AuthRequest, AuthorizationError, type AuthorizationErrorCode, type AuthorizationErrorOptions, AuthorizationServerBinding, CimdFetchError, ClientInfo, ClientRegistrationCallbackOptions, ClientRegistrationCallbackResult, CompleteAuthorizationOptions, type EmaClaimsMapper, type EmaClaimsMapperInput, type EmaClaimsMapperResult, type EmaIdJagClaims, type EmaOptions, type EmaTrustedIssuer, type EmaTrustedIssuerResolver, type EmaTrustedIssuerResolverInput, type EmaValidationError, ExchangeTokenOptions, ExternalTokenError, ExternalTokenErrorOptions, Grant, GrantSummary, GrantType, ListOptions, ListResult, OAuthAuthorizationServer, OAuthAuthorizationServerOptions, OAuthError, OAuthErrorInternal, OAuthErrorOptions, OAuthHelpers, OAuthProtectedResourceMetadata, OAuthProvider, OAuthProvider as default, OAuthProviderOptions, OAuthResourceAuth, OAuthResourceContext, OAuthResourceHandler, OAuthResourceMetadata, OAuthResourceServer, OAuthResourceServerOptions, OAuthResourceTokenValidation, OAuthResourceTokenValidator, OAuthTokenErrorCode, PurgeOptions, PurgeResult, ResolveExternalTokenInput, ResolveExternalTokenResult, Token, TokenBase, TokenExchangeCallbackOptions, TokenExchangeCallbackResult, TokenSummary, ValidatedAccessToken, base64UrlToBytes, getJwtCryptoAlgorithms, getOAuthApi, insufficientScope, isValidOAuthScopeToken, parseJwtJsonPart, resourceMatches, validateResourceUri };
|