Harden hosted MCP OAuth docs, challenge semantics, and e2e smoke tooling - #179
Harden hosted MCP OAuth docs, challenge semantics, and e2e smoke tooling#179dodeja wants to merge 0 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d740f6c30f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let authSource: 'authorization' | 'environment' | 'oauth_local' | 'oauth_remote' = | ||
| resolvedAuth.source ?? 'authorization'; | ||
|
|
||
| const jwtLikeBearerToken = callerScheme === 'bearer' && looksLikeJwt(callerToken); |
There was a problem hiding this comment.
Accept opaque Bearer tokens in OAuth validation path
The OAuth branch is gated on looksLikeJwt, so only Bearer tokens with exactly three dot-separated segments are treated as OAuth tokens. Any valid opaque OAuth access token will skip this path and be handled as legacy auth, which means it is rejected as Invalid client credentials whenever T49_API_TOKEN/T49_MCP_CLIENT_SECRET is configured (or forwarded upstream as a Token scheme when not configured). This breaks hosted OAuth clients that receive opaque access tokens.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
|
|
||
| const response = await fetch(jwksUrl, { |
There was a problem hiding this comment.
Handle JWKS fetch failures as auth failures
fetchJwks performs network I/O without error handling, so a transient JWKS network error (DNS, timeout, connection reset) throws out of verifyWorkosJwt instead of returning null. In api/mcp.ts, that propagates to the top-level catch and returns HTTP 500, which both skips internal token verification fallback and turns a token-validation issue into a server error for clients.
Useful? React with 👍 / 👎.
d740f6c to
8f6e499
Compare
Rebase result: this PR is fully superseded by #240 (already merged to main)I rebased this branch onto current
Failing Review comments: both Greptile findings (opaque-token rejection in the JWT branch, and unhandled JWKS fetch failures) are on What I did: rebased onto Recommendation: close this PR as superseded by #240. Happy to reopen/redo it scoped to anything from here that's still genuinely missing (e.g., dedicated OAuth e2e smoke tooling for the current WorkOS-based flow) if that's still wanted — but it should be written against the shipped architecture, not the original Rails-AS design. |
Greptile Summary
This PR implements OAuth 2.1 authentication for the hosted MCP server while maintaining backward compatibility with legacy API token authentication. The implementation adds JWT verification with WorkOS, proper WWW-Authenticate challenge semantics per RFC standards, and comprehensive e2e smoke testing tooling.
Key Changes:
workos-jwt.tsmodule with RS256 signature verification, JWKS caching with key rotation support, and comprehensive claim validation (issuer, audience, scope, exp, nbf)WWW-Authenticate: Bearer resource_metadata=...headers on 401sSecurity & Quality:
The implementation is solid, well-tested, and follows OAuth 2.1 and RFC standards for MCP authentication.
Confidence Score: 5/5
Important Files Changed
Sequence Diagram
sequenceDiagram participant Client as MCP Client participant MCP as MCP Handler participant WorkOS as WorkOS JWT participant Internal as Internal Verify participant Upstream as Terminal49 API Note over Client,Upstream: OAuth Flow (JWT Bearer Token) Client->>MCP: POST /mcp (no auth) MCP->>Client: 401 + WWW-Authenticate header Note right of Client: Discovery + OAuth flow happens<br/>(external to this PR) Client->>MCP: POST /mcp (Bearer JWT) MCP->>MCP: Detect JWT-like Bearer token MCP->>WorkOS: Verify JWT locally alt Local JWT valid WorkOS->>MCP: Valid (user_id, account_id) MCP->>Upstream: Call API with Bearer token Upstream->>MCP: Response MCP->>Client: 200 OK else Local JWT invalid WorkOS->>MCP: Invalid MCP->>Internal: Verify via internal endpoint alt Internal verify succeeds Internal->>MCP: Valid (active, user_id, account_id) MCP->>Upstream: Call API with Bearer token Upstream->>MCP: Response MCP->>Client: 200 OK else Both verifications fail Internal->>MCP: Invalid MCP->>Client: 401 + WWW-Authenticate end end Note over Client,Upstream: Legacy Flow (API Token) Client->>MCP: POST /mcp (Token <api_token>) MCP->>MCP: Not JWT-like, check client secret MCP->>Upstream: Call API with configured token Upstream->>MCP: Response MCP->>Client: 200 OKLast reviewed commit: d740f6c