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 1 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 👍 / 👎.
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