Add OAuth 2.1 /internal endpoint (Sentry SSO via Cloudflare Access)#2
Open
sergical wants to merge 6 commits into
Open
Add OAuth 2.1 /internal endpoint (Sentry SSO via Cloudflare Access)#2sergical wants to merge 6 commits into
sergical wants to merge 6 commits into
Conversation
532c54f to
59f8e3f
Compare
59f8e3f to
c32fa9e
Compare
c32fa9e to
da1ebe0
Compare
da1ebe0 to
d05ca83
Compare
d05ca83 to
bf7b33e
Compare
bf7b33e to
391f7af
Compare
391f7af to
86b69f3
Compare
8b9b7f9 to
b22eb5b
Compare
b22eb5b to
d53c4d2
Compare
d53c4d2 to
2c5b563
Compare
3178cb9 to
5a58292
Compare
5a58292 to
4a2cf5c
Compare
Adds a second Worker endpoint, /internal, an OAuth 2.1 server for managed connectors (Cowork, Claude.ai) that can't send a custom Authorization header. /mcp (bring-your-own-key) is unchanged. - /internal runs @cloudflare/workers-oauth-provider and federates login to Cloudflare Access (Sentry Google SSO). Only @sentry.io identities are admitted; queries use one shared server-side Plausible key. - Access id_token verified via JWKS (cf-access.ts): RS256-only, case-insensitive @sentry.io check. - OAuth 2.1 hardening: S256-only PKCE; access 1h / refresh 24h TTLs (offboarding); no plaintext PII in the unencrypted grant store (hashed userId, no email metadata — email lives only in encrypted props for Sentry attribution); CIMD enabled with DCR /register fallback; global_fetch_strictly_public flag. - Sentry: sendDefaultPii false; Authorization/cookie/Cf-Access-Jwt-Assertion scrubbed from spans. Per-IP rate limiting + security headers on all responses. - Worker files now type-checked via tsconfig.worker.json (`pnpm typecheck`). - @modelcontextprotocol/sdk pinned to 1.28.0 so it resolves to a single copy (matches agents@0.8.7), required for the worker type-check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4a2cf5c to
21ca53c
Compare
- Validate redirect_uri against the registered client in POST /authorize so a tampered round-tripped oauthReqInfo can't exfiltrate the auth code - Pin accepted Host(s) before deriving redirect_uri from request.url - Fail closed in verifyCloudflareAccessJwt: malformed tokens/JWKS errors now return null (403) instead of throwing (500) - Require a well-formed `Bearer <key>` Authorization header on /mcp - Add tests for the JWT verifier fail-closed path and the access-handler redirect_uri validation + host guard Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reconstructing the Response in corsResponse can collapse repeated Set-Cookie headers on some runtimes, which would drop one of the two cookies the POST /authorize redirect sets (approved-client + CSRF-clear). Re-apply them individually from the original response. Flagged by Sentry review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 057ee0c. Configure here.
The hardcoded host allowlist blocked self-hosted deploys (README registers Access against <your-worker-host>/callback). Gate the authorize/callback host check on an optional SERVICE_HOSTNAME env var instead: enforced when set (prod pins plausible-mcp.sentry.dev via wrangler.toml [vars]), skipped when unset so custom hosts work. localhost always allowed for wrangler dev. Flagged by Cursor Bugbot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y.io (SEC-1266) The @sentry.io check in cf-access.ts was hardcoded, so a self-hosted deploy couldn't admit its own users even with a matching Cloudflare Access policy — and that wasn't documented. Gate on ALLOWED_EMAIL_DOMAIN instead (comma- separated, defaults to sentry.io so the hosted deploy stays Sentry-only and self-hosters fail closed until configured). Also supports multiple domains (e.g. contractors). Document that the hosted /internal is Sentry-only and how to self-host for another org. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hash (SEC-1266) - GET /authorize: wrap renderApprovalDialog so a non-ASCII client-supplied state (btoa DOMException) returns 400 instead of an uncaught 500 - /callback: hash the lowercased email for the OAuth userId so mixed-case emails from the IdP map to one stable grant record - Test for the non-ASCII /authorize path Both flagged by Sentry review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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 this introduces
Today the Worker exposes a single endpoint,
/mcp— bring-your-own-key, each caller passes their own Plausible API key as a Bearer token.This adds a second endpoint,
/internal, an OAuth 2.1 server for managed connectors (Cowork, Claude.ai) that can't send a custom header./mcpis unchanged.How
/internaldiffers from the existing/mcpmodel@cloudflare/workers-oauth-provider) and federates login to Cloudflare Access as the upstream OIDC provider (Sentry Google SSO).@sentry.ioidentities are admitted; queries run against one shared, server-side Plausible key, so connector users never handle a key (vs./mcpwhere the user's own key is the credential).cf-access.ts).Security posture of the new auth surface
@sentry.iogateuserIdis a hash and no email is stored in metadata; the email lives only in the encryptedprops, used for Sentry attribution/registerkept as fallback;global_fetch_strictly_publicset (CIMD requirement + SSRF guard)sendDefaultPii: false, andAuthorization/ cookie /Cf-Access-Jwt-Assertionscrubbed from spansX-Frame-Options, etc.) on all responses/.well-known/*,/authorize,/token,/register) are served publicly by the Worker — Access is the upstream IdP, not a proxy in front, so connector OAuth discovery isn't blockedOther effects on the codebase
worker.ts,env.ts,access-handler.ts,workers-oauth-utils.ts) are now type-checked viapnpm typecheck(tsconfig.worker.json); previously transpiled without type-checking.@modelcontextprotocol/sdkpinned to1.28.0so it resolves to a single copy (matchingagents@0.8.7), required for the worker type-check.Requires before deploy (Cloudflare dashboard, not code)
Register the Access OIDC app (app-specific
CF_ACCESS_AUD,@sentry.ioSSO policy), createOAUTH_KV, set the secrets listed in the README, then run the end-to-end Cowork connect test.Verification
Worker type-check +
pnpm buildclean · 68 tests pass · CI green.