refactor(auth): delegate API-token validation to the OAuth provider - #443
Merged
Merged
Conversation
Upgrade workers-oauth-provider to 0.10.1 and replace the pre-provider bearer-token fork with the provider's resolveExternalToken hook. Expected verification failures now become structured 401/403/429 protected-resource responses with WWW-Authenticate challenges instead of escaping as Worker exceptions. Verified identities are cached against a credential digest so repeat MCP requests skip the Cloudflare API identity probes, and authorize-endpoint validation failures redirect to validated client redirect URIs or render locally per OAuth 2.1.
…ration devApiTokenModeEnabled inspects only the environment, so it no longer reads as a per-request predicate. The OAuth callback's createClient call never registered the requesting client: the provider generates a random client ID, so the call only wrote an orphan KV record per authorization. Clients register through the provider's /register endpoint, and the authorize flow validates them before the callback can run.
…paths getUserAndAccounts now reports when an ok-status probe returned an unparseable payload; such identities may serve the current request but are never written to the identity cache, so a transient Cloudflare API payload problem self-heals on the next probe instead of pinning reduced account data for the cache TTL. Local development mode converts expected verification failures into structured responses again, and the OAuth callback returns a client error when completeAuthorization rejects a grant reconstructed from an older provider version.
mattzcarey
marked this pull request as ready for review
August 7, 2026 15:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the hand-rolled bearer-token fork in front of
OAuthProviderwith the provider'sresolveExternalTokenhook, on@cloudflare/workers-oauth-provider0.10.3 (was 0.8.2). This is the same architecture cloudflare/mcp adopted in cloudflare/mcp#185 and cloudflare/mcp#193.packages/mcp-common/src/api-token-mode.ts: direct Cloudflare API/OAuth credentials are now resolved inside the provider viaresolveExternalToken. Expected verification failures throwExternalTokenError, which the provider converts to structured401 invalid_token/403 insufficient_scope(withWWW-Authenticatechallenges and step-up scope guidance) /429 temporarily_unavailable(preservingRetry-After) /5xx server_errorresponses. Nothing in this path can escape as an uncaught Worker exception (HTTP 500 / error 1101) anymore.OAUTH_KVfor 30 days, keyed by a SHA-256 digest of the credential, so repeat MCP requests skip the/user+/accountsidentity probes (~500ms and 2 subrequests per request today). Failed verifications are never cached, and identities degraded by an ok-status-but-unparseable API payload are served but never cached, so transient API payload problems self-heal on the next probe. Revoking a credential still revokes access — tool calls use the token itself, not the cached identity.DEV_DISABLE_OAUTHshort-circuit, which now returns structured errors for rejected dev tokens instead of throwing./oauth/authorizeclassifies the provider 0.10AuthorizationError(redirect to the validated client redirect URI witherror/state/iss, or render locally when no redirect URI was validated) andCimdFetchError(retryable 503) instead of collapsing them into a 500./oauth/callbackreturns a client error whencompleteAuthorizationrejects a grant reconstructed from an older provider version (relevant only during the deploy window).createClientcall in/oauth/callbackthat never registered the requesting client (the provider generates a random client ID) and only wrote an orphan KV record per authorization. Clients register through/registerand are validated before the callback can run.Why
A well-formed bearer token that fails verification (invalid credential, revoked token, malformed value) previously threw an
MCPErrorthat escaped the Worker as an exception, returning HTTP 500 / Cloudflare error 1101 to MCP clients instead of a spec-correct401withWWW-Authenticate. #393 converted most of these inside the fork; this PR removes the fork entirely so the provider owns the protected-resource error contract, and upgrades the fleet to the current provider.Notes for review
/useridentity) remain supported:cfat_-prefixed tokens use only the/accountsprobe and must resolve to exactly one account; legacy unprefixed tokens keep response-based inference.invalid_token— same trade-off cloudflare/mcp accepted.plainPKCE, are rejected by provider 0.10.x per OAuth 2.1; MCP-spec clients are unaffected. In-flight authorizations started under 0.8.2 without a code challenge fail as client errors during the deploy window.grantTypes: ['implicit']/responseTypes: ['token']because provider 0.10 validates registered client capabilities atcompleteAuthorization().Testing
pnpm test: 318 tests green across 35 files, including new coverage forresolveExternalToken(identity caching, failed-verification and degraded-identity cache exclusion, KV-outage fallback, prefixed-owner probes, 400/401/403/429 mapping),/ssewith a direct API token through the router, dev-mode structured errors, and the authorize-route error classification.pnpm types,pnpm check:turbo,pnpm check:format,pnpm check:depsall green.