@cloudflare/workers-oauth-provider 0.8.3 → 0.9.1

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.
@@ -39,6 +39,9 @@ type EmaValidationError = {
39
39
  reason: 'aud_mismatch';
40
40
  expected: string;
41
41
  got: string | string[];
42
+ } | {
43
+ reason: 'unsupported_claim';
44
+ claim: 'authorization_details' | 'cnf';
42
45
  } | {
43
46
  reason: 'expired';
44
47
  exp: number;
@@ -295,6 +298,9 @@ interface EmaOptions<Env = Cloudflare.Env> {
295
298
  allowPublicClients?: boolean;
296
299
  }
297
300
  //#endregion
301
+ //#region src/oauth-capabilities.d.ts
302
+ declare function isValidOAuthScopeToken(scopeToken: string): boolean;
303
+ //#endregion
298
304
  //#region src/oauth-provider.d.ts
299
305
  /**
300
306
  * Enum representing OAuth grant types
@@ -446,7 +452,7 @@ interface ClientRegistrationCallbackResult {
446
452
  /**
447
453
  * Input parameters for the resolveExternalToken callback function
448
454
  */
449
- interface ResolveExternalTokenInput {
455
+ interface ResolveExternalTokenInput<Env = Cloudflare.Env> {
450
456
  /**
451
457
  * The token string that was provided in the Authorization header
452
458
  */
@@ -458,21 +464,25 @@ interface ResolveExternalTokenInput {
458
464
  /**
459
465
  * Cloudflare Worker environment variables
460
466
  */
461
- env: any;
467
+ env: Env;
462
468
  }
463
469
  /**
464
470
  * Result returned from the resolveExternalToken callback function
465
471
  */
466
472
  interface ResolveExternalTokenResult {
467
473
  /**
468
- * Application-specific properties that will be passed to the API handlers
469
- * These properties are set in the execution context (ctx.props) when the external token is validated
474
+ * Application-specific properties that will be passed to the API handlers.
475
+ * These properties are set in the execution context (`ctx.props`) after the
476
+ * external bearer credential is validated.
470
477
  */
471
478
  props: any;
472
479
  /**
473
- * Audience claim from the external token (RFC 7519 Section 4.1.3)
474
- * If provided, will be validated against the resource server identity
480
+ * Protected resource audience established by the external validator.
475
481
  *
482
+ * A JWT may carry this value as an `aud` claim. For an opaque API token or
483
+ * PAT, the callback can supply the local resource URI as policy after
484
+ * successful validation. When `resourceMetadata.resource` is configured,
485
+ * this value is required and must match it exactly.
476
486
  */
477
487
  audience?: string | string[];
478
488
  }
@@ -548,8 +558,9 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
548
558
  */
549
559
  clientRegistrationTTL?: number;
550
560
  /**
551
- * List of scopes supported by this OAuth provider.
552
- * If not provided, the 'scopes_supported' field will be omitted from the OAuth metadata.
561
+ * Scopes supported by the authorization server.
562
+ * These are advertised only in authorization server metadata; configure
563
+ * `resourceMetadata.scopes_supported` separately for protected resource requirements.
553
564
  */
554
565
  scopesSupported?: string[];
555
566
  /**
@@ -559,10 +570,9 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
559
570
  */
560
571
  allowImplicitFlow?: boolean;
561
572
  /**
562
- * Controls whether the plain PKCE method is allowed.
563
- * OAuth 2.1 recommends using S256 exclusively as plain offers no cryptographic protection.
564
- * When set to false, only the S256 code_challenge_method will be accepted.
565
- * Defaults to true for backward compatibility.
573
+ * Controls whether the legacy plain PKCE method is allowed.
574
+ * Defaults to false so PKCE challenges use S256 exclusively.
575
+ * Set to true only for compatibility with clients that cannot use S256.
566
576
  */
567
577
  allowPlainPKCE?: boolean;
568
578
  /**
@@ -604,15 +614,18 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
604
614
  */
605
615
  tokenExchangeCallback?: (options: TokenExchangeCallbackOptions) => Promise<TokenExchangeCallbackResult | void> | TokenExchangeCallbackResult | void;
606
616
  /**
607
- * Optional callback function that is called when a provided token was not found in the internal KV.
608
- * This allows authentication through external OAuth servers.
609
- * For example, if a request includes an authenticated token from a different OAuth authentication server,
610
- * the callback can be used to authenticate it and set the context props through it.
617
+ * Optional callback called when a provided bearer credential was not found
618
+ * in the internal KV. It can validate an external OAuth access token, opaque
619
+ * API token, personal access token (PAT), or another bearer credential and
620
+ * set the application props passed to the protected handler.
611
621
  *
612
- * The callback can optionally return props values that will passed-through to the apiHandlers.
613
- * The callback can return `null` to signal resolution failure.
622
+ * Return props to authenticate the request, or `null` for a generic `invalid_token` response.
623
+ * Throw this package's exported {@link ExternalTokenError} to return an intentional
624
+ * structured error response for an upstream validation failure.
625
+ * All other thrown errors, including {@link OAuthError}, remain unexpected
626
+ * failures and are re-thrown for backwards compatibility.
614
627
  */
615
- resolveExternalToken?: (input: ResolveExternalTokenInput) => Promise<ResolveExternalTokenResult | null>;
628
+ resolveExternalToken?: (input: ResolveExternalTokenInput<Env>) => Promise<ResolveExternalTokenResult | null>;
616
629
  /**
617
630
  * Optional callback function that is called whenever the OAuthProvider returns an error response.
618
631
  * This allows the client to emit notifications or perform other actions when an error occurs.
@@ -623,6 +636,11 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
623
636
  * deliberately did NOT put on the wire — used for richer diagnostics where the public
624
637
  * response must stay generic (e.g. JWT validation failures on the EMA path). Backwards
625
638
  * compatible: existing callbacks ignoring this field continue to work unchanged.
639
+ *
640
+ * `request` (when present) is the HTTP request that produced the error response, so the
641
+ * callback can correlate the error with per-request state such as request-keyed telemetry.
642
+ * Currently populated for CIMD metadata fetch failures at the token endpoint. Backwards
643
+ * compatible in the same way as `internal`.
626
644
  */
627
645
  onError?: (error: {
628
646
  code: string;
@@ -634,6 +652,7 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
634
652
  reason: string;
635
653
  detail?: unknown;
636
654
  };
655
+ request?: Request;
637
656
  }) => Response | void;
638
657
  /**
639
658
  * Explicitly enable Client ID Metadata Document (CIMD) support.
@@ -648,6 +667,7 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
648
667
  * with an origin-only resource (e.g. `https://server.com`) to be used with
649
668
  * path-aware resource requests (e.g. `https://server.com/mcp`), enabling seamless
650
669
  * migration from pre-0.4.0 versions that stored origin-only resource URIs.
670
+ * Explicit `resourceMetadata.resource` configuration always uses exact matching.
651
671
  *
652
672
  * Defaults to false (strict exact matching per RFC 8707).
653
673
  */
@@ -662,7 +682,11 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
662
682
  resourceMetadata?: {
663
683
  /**
664
684
  * The protected resource identifier URL (RFC 9728 `resource` field).
665
- * If not set, defaults to the request URL's origin.
685
+ *
686
+ * Configuring this value pins authorization requests, token requests, and
687
+ * access-token audiences to this exact resource. If omitted, the provider
688
+ * accepts valid RFC 8707 resource indicators and uses the authorization
689
+ * request origin as the default when the client does not send one.
666
690
  */
667
691
  resource?: string;
668
692
  /**
@@ -672,8 +696,10 @@ interface OAuthProviderOptions<Env = Cloudflare.Env> {
672
696
  */
673
697
  authorization_servers?: string[];
674
698
  /**
675
- * Scopes supported by this protected resource.
676
- * If not set, falls back to the top-level scopesSupported option.
699
+ * Minimal scopes required for basic protected resource functionality.
700
+ * These scopes are advertised in Protected Resource Metadata and used as
701
+ * baseline bearer challenge guidance. `offline_access` is omitted because
702
+ * refresh-token issuance is an authorization server capability.
677
703
  */
678
704
  scopes_supported?: string[];
679
705
  /**
@@ -695,18 +721,23 @@ interface OAuthHelpers {
695
721
  * Parses an OAuth authorization request from the HTTP request
696
722
  * @param request - The HTTP request containing OAuth parameters
697
723
  * @returns The parsed authorization request parameters
724
+ * @throws Error when the response type is missing, unsupported, or not registered for the client
725
+ * @throws {@link CimdFetchError} when the client ID is a CIMD URL whose document cannot be resolved
698
726
  */
699
727
  parseAuthRequest(request: Request): Promise<AuthRequest>;
700
728
  /**
701
729
  * Looks up a client by its client ID
702
730
  * @param clientId - The client ID to look up
703
- * @returns A Promise resolving to the client info, or null if not found
731
+ * @returns A Promise resolving to the client info, or null if the client does not exist
732
+ * @throws {@link CimdFetchError} when the client ID is a CIMD URL whose document cannot be resolved
704
733
  */
705
734
  lookupClient(clientId: string): Promise<ClientInfo | null>;
706
735
  /**
707
736
  * Completes an authorization request by creating a grant and authorization code
708
737
  * @param options - Options specifying the grant details
709
738
  * @returns A Promise resolving to an object containing the redirect URL
739
+ * @throws Error when the request's response type is not permitted
740
+ * @throws {@link CimdFetchError} when the client ID is a CIMD URL whose document cannot be resolved
710
741
  */
711
742
  completeAuthorization(options: CompleteAuthorizationOptions): Promise<{
712
743
  redirectTo: string;
@@ -762,6 +793,7 @@ interface OAuthHelpers {
762
793
  * Implements OAuth 2.0 Token Exchange (RFC 8693)
763
794
  * @param options - Options for token exchange including subject token and optional modifications
764
795
  * @returns Promise resolving to token response with new access token
796
+ * @throws {@link CimdFetchError} when the grant's client ID is a CIMD URL whose document cannot be resolved
765
797
  */
766
798
  exchangeToken(options: ExchangeTokenOptions): Promise<TokenResponse>;
767
799
  /**
@@ -838,6 +870,11 @@ interface AuthRequest {
838
870
  * Resource parameter indicating target resource(s) (RFC 8707)
839
871
  */
840
872
  resource?: string | string[];
873
+ /**
874
+ * Authorization server issuer recorded while parsing this request.
875
+ * Include it as `iss` in successful and error authorization responses.
876
+ */
877
+ issuer?: string;
841
878
  }
842
879
  /**
843
880
  * OAuth client registration information
@@ -1317,11 +1354,10 @@ interface OAuthErrorOptions {
1317
1354
  headers?: Record<string, string>;
1318
1355
  }
1319
1356
  /**
1320
- * Structured OAuth 2.0 error.
1357
+ * Structured OAuth 2.0 token-endpoint error.
1321
1358
  *
1322
- * Throw from a `tokenExchangeCallback` (or any code it calls — the error
1323
- * propagates naturally up through deep call stacks) to surface a standard
1324
- * `/token` error response (`{ error, error_description }`) instead of a
1359
+ * Throw from a `tokenExchangeCallback` or any code it calls to surface a
1360
+ * standard OAuth token response (`{ error, error_description }`) instead of a
1325
1361
  * generic `500 Internal Server Error`.
1326
1362
  *
1327
1363
  * Anything thrown that is **not** an `OAuthError` continues to surface as
@@ -1371,6 +1407,82 @@ declare class OAuthError extends Error {
1371
1407
  readonly headers?: Record<string, string>;
1372
1408
  constructor(code: string, options: OAuthErrorOptions);
1373
1409
  }
1410
+ /** Options accepted by the {@link ExternalTokenError} constructor. */
1411
+ interface ExternalTokenErrorOptions {
1412
+ /**
1413
+ * Public description returned in the OAuth `error_description` field.
1414
+ * Do not include credentials, upstream response bodies, or private diagnostics.
1415
+ */
1416
+ description: string;
1417
+ /** HTTP status returned to the protected-resource client. */
1418
+ statusCode: number;
1419
+ /** Additional public response headers, such as `Retry-After`. */
1420
+ headers?: Record<string, string>;
1421
+ /**
1422
+ * Minimum scopes needed for the protected-resource operation.
1423
+ *
1424
+ * For `403 insufficient_scope`, these values are validated, deduplicated,
1425
+ * and added to the synthesized `WWW-Authenticate` challenge. Each value must
1426
+ * use the OAuth scope-token grammar from RFC 6749 §3.3.
1427
+ */
1428
+ requiredScopes?: string[];
1429
+ }
1430
+ /**
1431
+ * Intentional public error from an external bearer-token validator.
1432
+ *
1433
+ * Throw only from `resolveExternalToken` when an expected validation outcome
1434
+ * should become a structured protected-resource response. Ordinary errors and
1435
+ * {@link OAuthError} retain their pre-existing behavior and propagate as
1436
+ * unexpected failures.
1437
+ */
1438
+ declare class ExternalTokenError extends Error {
1439
+ /** OAuth error code returned in the response body and, when applicable, challenge. */
1440
+ readonly code: OAuthTokenErrorCode;
1441
+ /** Public description returned as `error_description`. */
1442
+ readonly description: string;
1443
+ /** HTTP status returned to the protected-resource client. */
1444
+ readonly statusCode: number;
1445
+ /** Additional public response headers. */
1446
+ readonly headers?: Record<string, string>;
1447
+ /** Minimum scopes for an `insufficient_scope` challenge. */
1448
+ readonly requiredScopes?: string[];
1449
+ /**
1450
+ * Creates an intentional external-token validation error.
1451
+ * @param code - Standard OAuth error code to return
1452
+ * @param options - Public response details
1453
+ */
1454
+ constructor(code: OAuthTokenErrorCode, options: ExternalTokenErrorOptions);
1455
+ }
1456
+ /**
1457
+ * Thrown when fetching a Client ID Metadata Document (CIMD) fails — the
1458
+ * server-to-server fetch errored, timed out, or returned an invalid document.
1459
+ * Distinct from a client that simply does not exist, which is reported as a
1460
+ * null client lookup result.
1461
+ *
1462
+ * At the token endpoint the provider handles this itself: the wire response
1463
+ * stays a generic `invalid_client` / "Client not found", and the failure is
1464
+ * reported through the `onError` hook's `internal` field (category
1465
+ * `client-id-metadata-document`) together with the originating `request`.
1466
+ *
1467
+ * `OAuthHelpers` methods that look up clients (`lookupClient`, and methods
1468
+ * built on it such as `exchangeToken`) let this error propagate to the
1469
+ * caller. Callers that previously relied on a `null` result for these
1470
+ * failures should catch it to preserve their error contract.
1471
+ */
1472
+ declare class CimdFetchError extends Error {
1473
+ /** Stable reason slug suitable for telemetry and control flow. */
1474
+ readonly reason: "metadata_resolution_failed";
1475
+ /** The CIMD URL whose fetch or validation failed. */
1476
+ readonly metadataUrl: string;
1477
+ /** The underlying failure message (e.g. "Failed to fetch client metadata: HTTP 403"). */
1478
+ readonly detail: string;
1479
+ /**
1480
+ * Creates an error for a failed CIMD fetch or validation.
1481
+ * @param metadataUrl - The CIMD URL that could not be resolved
1482
+ * @param cause - The underlying fetch or validation failure
1483
+ */
1484
+ constructor(metadataUrl: string, cause: unknown);
1485
+ }
1374
1486
  /**
1375
1487
  * Validates a resource URI per RFC 8707 Section 2
1376
1488
  * @param uri - The URI string to validate
@@ -1391,7 +1503,6 @@ declare function base64UrlToBytes(base64Url: string): Uint8Array;
1391
1503
  * Parses a base64url-encoded JWT JSON part into an object.
1392
1504
  */
1393
1505
  declare function parseJwtJsonPart(encoded: string): Record<string, unknown>;
1394
- declare function isValidOAuthScopeToken(scopeToken: string): boolean;
1395
1506
  /**
1396
1507
  * Gets WebCrypto import and verify parameters for supported JOSE algorithms.
1397
1508
  */
@@ -1400,4 +1511,4 @@ declare function getJwtCryptoAlgorithms(alg: string): {
1400
1511
  verifyAlgorithm: Parameters<SubtleCrypto['verify']>[0];
1401
1512
  };
1402
1513
  //#endregion
1403
- export { AuthRequest, ClientInfo, ClientRegistrationCallbackOptions, ClientRegistrationCallbackResult, CompleteAuthorizationOptions, type EmaClaimsMapper, type EmaClaimsMapperInput, type EmaClaimsMapperResult, type EmaIdJagClaims, type EmaOptions, type EmaTrustedIssuer, type EmaTrustedIssuerResolver, type EmaTrustedIssuerResolverInput, type EmaValidationError, ExchangeTokenOptions, 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 };
1514
+ export { AuthRequest, 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 };