Skip to content

Conversation

@srstrickland
Copy link

@srstrickland srstrickland commented Nov 17, 2025

In proxy mode, the id_token from the upstream identity provider is now mapped to access_token in the response. This ensures that the token is verifiable, whereas the upstream access token might be from a different issuer.

Without this fix, authentication to MCP-Trino appears to work, but upon making any requests, there are token verification failures.

TBH I'm not 100% confident in this fix (not an expert in oauth or authentication in general)... but I needed it in order to get authentication for Cursor via azure working (in proxy mode). Below is the config I was using (with aggressive redaction 😛):

MCP_URL=http://localhost:8080
MCP_TRANSPORT=http
OAUTH_ENABLED=true
OAUTH_MODE=proxy
OAUTH_PROVIDER=azure
OAUTH_REDIRECT_URI=http://localhost:8080,cursor://anysphere.cursor-mcp/oauth/user-Trino/callback
OIDC_AUDIENCE=XXXXXXXXXXXX
OIDC_CLIENT_ID=XXXXXXXXXXXX
OIDC_CLIENT_SECRET=XXXXXXXXXXXX
OIDC_ISSUER=https://login.microsoftonline.com/XXXXXXXXXXXX/v2.0
TRINO_HOST=trino.somedomain.com
TRINO_PASSWORD=XXXXXXXXXXXX
TRINO_PORT=443
TRINO_SCHEMA=https
TRINO_USER=mcp
JWT_SECRET=XXXXXXXXXXXX

I'd be happy to learn that I'm just using it incorrectly, but figured this was a good place to start the conversation. Thanks!

Summary by CodeRabbit

  • Bug Fixes
    • Updated OAuth2 token response behavior in proxy mode to place ID token values in the access_token field instead of the id_token field
    • Native mode token handling remains unchanged

In proxy mode, the `id_token` from the upstream identity provider is
now mapped to `access_token` in the response. This ensures that the
token is verifiable, whereas the upstream access token might be from a
different issuer.

Without this fix, authentication to MCP-Trino appears to work, but upon
making any requests, there are token verification failures.
@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Walkthrough

OAuth2 token response handling is modified to place the ID token value into the access_token field during proxy mode operation, while native mode retains the standard id_token field placement. This restructures token response payloads for proxy mode clients.

Changes

Cohort / File(s) Summary
OAuth2 Token Response Restructuring
handlers.go
Modified token response construction to conditionally place ID token in access_token field for proxy mode, keeping id_token field for native mode

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler
    participant OAuth2

    Client->>Handler: Token exchange request
    Handler->>OAuth2: Generate ID token
    OAuth2-->>Handler: ID token value
    
    alt Proxy Mode
        Handler->>Handler: Place ID token in access_token field
    else Native Mode
        Handler->>Handler: Place ID token in id_token field
    end
    
    Handler-->>Client: Token response (restructured)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Critical path modification: OAuth2 token responses are security-sensitive; conditional logic must be verified for correctness
  • Token field placement: Confirm proxy vs. native mode detection logic is reliable and won't inadvertently expose tokens in unexpected fields
  • Downstream compatibility: Validate that proxy mode clients correctly interpret tokens from the access_token field

Poem

🐰 A token, a token, two fields collide,
In proxy mode, the ID token must hide,
From id_token to access_token it flows,
While native stays put—that's how it goes!
Hops away with cryptographic glee

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Adjust ID token handling for proxy mode' directly and clearly summarizes the main change: modifying how ID tokens are handled when in proxy mode.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14f9009 and 7adffb1.

📒 Files selected for processing (1)
  • handlers.go (1 hunks)

Comment on lines +589 to +596
if h.config.Mode == "proxy" {
// In proxy mode, trino-mcp is going to expect to receive id tokens
// that can be validated, not access tokens which can be opaque or
// from another issuer (e.g. Microsoft Graph when using Azure).
response["access_token"] = idToken
} else {
response["id_token"] = idToken
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Critical: Overwriting access_token with id_token violates OAuth2/OIDC semantics and discards the original access token.

In proxy mode, this change overwrites the access_token field (originally set on line 577) with the id_token value, which:

  1. Discards the original access token from the upstream provider, making it unavailable to clients
  2. Violates OAuth2/OIDC separation of concerns: access_token is for API authorization while id_token is for authentication/identity verification
  3. Breaks standard OAuth2 client expectations that may rely on both tokens being present in their respective fields
  4. Omits the id_token field entirely from the proxy mode response, which some clients may expect

While this may resolve the immediate MCP-Trino verification issue, it creates a non-standard token response that could break other OAuth2 clients or integrations.

Alternative approaches to consider:

  1. Return both tokens in separate fields and document that clients should use id_token for verification in proxy mode
  2. Add a new custom field (e.g., verifiable_token) to avoid overwriting standard fields
  3. Configure MCP-Trino to accept and verify tokens from the id_token field rather than access_token
  4. Document the behavior and validate that all downstream consumers can handle this non-standard response

Please verify the OAuth2/OIDC implications of this approach:

Additionally, run the following script to identify other potential consumers of the token endpoint:


🏁 Script executed:

#!/bin/bash
# Description: Find references to token endpoint and token response handling to assess impact

# Search for token response handling
rg -n -C3 -P '(access_token|id_token).*token\.(AccessToken|Extra)' --type=go

# Search for HandleToken callers or references
rg -n -C3 'HandleToken|/token' --type=go

Length of output: 7346


🌐 Web query:

OAuth2 and OIDC spec: Can id_token be used as access_token? What are the semantic differences between access_token and id_token?

💡 Result:

Short answer

  • No — the OIDC id_token is not a substitute for an OAuth2 access_token. The specs treat them as different tokens with different audiences and purposes. [1][2]

Key semantic differences

  • Purpose

    • access_token: credential the client presents to a resource server to authorize API/resource access (represents scopes/permissions). [2]
    • id_token: proof to the client that an end-user was authenticated; carries identity claims (iss, sub, aud, nonce, etc.). It is intended for the client (relying party), not for resource servers. [1]
  • Audience / who consumes it

    • access_token: audience = resource server(s). The resource server must understand/validate it. [2]
    • id_token: audience = client_id (the relying party). The client validates it and consumes the identity claims. [1]
  • Format and semantics

    • access_token: can be opaque or structured (JWT); contains authorization data (scope, expiry) or an identifier for server-side lookup. [2]
    • id_token: defined by OIDC as a signed (optionally encrypted) JWT containing authentication claims. Includes at_hash/c_hash when issued with access_token or code. [1]
  • Validation, revocation, and lifecycle

    • access_tokens are designed for resource access, have scopes, lifetimes, and may be introspected or revoked by the authorization server. [2]
    • id_tokens assert authentication and are not meant for resource authorization; they are not intended to be used for calling APIs and are not a replacement for an access token. [1][2]

Security note

  • Using an id_token as an access token breaks the intended audience/validation model and can create security problems (token misuse, wrong audience, inability for resource servers to enforce scopes/introspection). OIDC explicitly provides at_hash to bind an access_token to an id_token when both are issued. [1][2]

Sources

  • OpenID Connect Core 1.0 (ID Token semantics, at_hash, audience/intended use). [1]
  • RFC 6749 — OAuth 2.0 Authorization Framework (access_token definition, scope, usage). [2]
  • RFC 6750 — Bearer Token Usage (how access tokens are used to call resources). [3]

Critical: Code violates OAuth2/OIDC specification by substituting id_token for access_token.

Lines 589-596 in proxy mode overwrite the access_token field (set on line 577) with the id_token value. Per the OAuth2 and OIDC specifications, these tokens serve fundamentally different purposes and audiences:

  • access_token: credential for API/resource server authorization; contains scope and permission information
  • id_token: authentication assertion for the client (relying party) containing identity claims

Using an id_token as an access_token breaks the intended audience/validation model and can create security problems. This change:

  1. Discards the original access token from the upstream provider (making it unavailable)
  2. Sends an authentication token (id_token) to clients expecting an authorization token
  3. Breaks standard OAuth2 clients that validate token audience or scope claims
  4. Violates RFC 6749 (OAuth 2.0) and OpenID Connect Core 1.0

Recommended fixes:

  • Return both tokens in their proper fields (accept id_token verification in MCP-Trino instead of changing token semantics)
  • Add conditional token field mapping only if MCP-Trino cannot be configured to use id_token directly
  • Document and validate that all downstream clients can handle non-standard token responses
🤖 Prompt for AI Agents
In handlers.go around lines 589 to 596, the code currently overwrites the
access_token with the id_token in proxy mode which violates OAuth2/OIDC
semantics; instead preserve the original access_token set earlier and add the
id_token as a separate field (e.g., response["id_token"] = idToken) so both
tokens are returned, and only perform any mapping if an explicit configuration
flag requires replacing the access token; also ensure you document this behavior
and validate downstream clients can accept id_token if you must change
semantics.

response["access_token"] = idToken
} else {
response["id_token"] = idToken
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix indentation inconsistency.

Line 596 has excessive indentation (appears to use more spaces than the surrounding code).

Apply this diff:

-        }
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}
}
🤖 Prompt for AI Agents
In handlers.go around line 596, the closing brace has excessive indentation
compared to the surrounding block; reduce the leading spaces so the brace aligns
with the other block braces (match the repository's indentation style—tabs or
the same number of spaces used nearby), save the file, and run the
formatter/linter to ensure consistent indentation across the file.

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.

1 participant