Skip to content

fix: preserve MCP account authorization denials - #359

Merged
dodeja merged 2 commits into
mainfrom
codex/mcp-oauth-forbidden-semantics
Sep 10, 2026
Merged

fix: preserve MCP account authorization denials#359
dodeja merged 2 commits into
mainfrom
codex/mcp-oauth-forbidden-semantics

Conversation

@alosec

@alosec alosec commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve valid OAuth grants when Terminal49 denies account-level access
  • return 403 without an OAuth challenge instead of converting resolver 403 responses into invalid-token 401s
  • record the resolver status as non-sensitive structured telemetry

Why

The MCP gateway currently collapses both resolver 401 and 403 responses into an OAuth invalid-token response. Account rollout or access denials therefore make clients discard valid WorkOS grants and repeat authorization. Only resolver 401 means the credential is invalid; resolver 403 is an authenticated account authorization denial.

This change improves protocol correctness and makes the present AskT49/Eve rejection diagnosable. It does not grant account access or turn a genuinely invalid credential into a valid one.

Verification

  • Node 24
  • @terminal49/mcp: 15 files, 214 tests passed
  • root TypeScript check passed
  • API and MCP lint/format checks passed with existing warnings
  • six protocol/auth response matrices passed against the Vercel preview

The preview deployment is healthy, but it intentionally does not contain the production OAuth resolver secret; therefore full end-to-end OAuth cannot be proven on that preview. Production QA completed WorkOS authorization for both the demo account and a real Terminal49 account, then reproduced the resolver rejection. After this diagnostic change reaches an appropriately configured environment, repeat that QA to classify and correct the underlying resolver result.

Draft only; no production deployment is included.

Related: Terminal49/t49#3543 and Linear DEV-12439.


View with [code]smith

Greptile Summary

This PR corrects MCP connected-client resolver semantics so authenticated account-level denials remain HTTP 403 responses without prompting clients to discard valid OAuth grants.

  • Distinguishes resolver 401 invalid credentials from resolver 403 account authorization denials.
  • Adds non-sensitive upstream status telemetry for resolver failures.
  • Adds coverage confirming that 403 responses omit the OAuth challenge.
  • Greptile automatically discovered a related ticket that helped explain the purpose of this PR: preserve resolver 401/403 semantics while keeping MCP tools fail-closed.

Confidence Score: 5/5

The PR appears safe to merge, with the resolver authorization distinction implemented consistently and covered by a focused test.

No new changes exist since the previous review SHA, no previous Greptile findings were supplied as outstanding, and the full PR currently preserves fail-closed authorization while correctly distinguishing invalid credentials from account access denials.

Important Files Changed

Filename Overview
api/mcp.ts Separates invalid-token and account-forbidden resolver outcomes, records upstream status, and returns a generic 403 without an OAuth challenge.
packages/mcp/tests/api-handler.test.ts Adds focused coverage for preserving resolver 403 responses and valid OAuth grants.

Sequence Diagram

sequenceDiagram
    participant Client
    participant MCP as MCP Gateway
    participant Resolver as Connected Client Resolver
    Client->>MCP: Bearer OAuth token
    MCP->>Resolver: Resolve access token
    alt Resolver returns 401
        Resolver-->>MCP: Invalid credential
        MCP-->>Client: 401 + OAuth challenge
    else Resolver returns 403
        Resolver-->>MCP: Account access denied
        MCP-->>Client: 403 without OAuth challenge
    else Resolver returns another error
        Resolver-->>MCP: Upstream failure
        MCP-->>Client: Retryable gateway error
    end
Loading

Reviews (2): Last reviewed commit: "chore: clean up MCP anti-slop warnings" | Re-trigger Greptile

Context used:

@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
api Ready Ready Preview Sep 10, 2026 6:09pm UTC

Request Review

@alosec

alosec commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Live OAuth evidence for the follow-up QA: on 2026-09-10 at 03:44:54Z, alex.garcia@terminal49.com completed the existing WorkOS Connected Clients flow from the AskT49 preview, after which production MCP rejected the grant. Agent error ID: 8aef91a1-5fff-44e4-8f7a-228a73fcc9b7; MCP request ID: 8af957b0-cde4-49eb-af79-4a7b7834d695. The same behavior occurred with the demo account. Because current production collapses upstream 401 and 403, this is evidence of the unresolved resolver path—not evidence that account rollout is definitely the cause. Track in DEV-12439 and repeat after the new status telemetry is available.

@dodeja
dodeja marked this pull request as ready for review September 10, 2026 18:23
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T18:26:53.615388Z ea15837 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dodeja

dodeja commented Sep 10, 2026

Copy link
Copy Markdown
Member

@greptile review

@dodeja
dodeja merged commit 39bf758 into main Sep 10, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants