fix(mcp): return 502 not KeyError when a bridge upstream response lacks access_token

The eager access_token = token_response["access_token"] extraction ran before
the dcr_bridge branch, so a missing upstream access_token raised an unhandled
KeyError and _bridge_grant_from_token_response's nil guard (which maps to a clean
502) was dead code. Move the extraction onto the non-bridge result path so the
bridge branch reaches its 502 guard.
This commit is contained in:
Tin Chi Lo 2026-07-11 14:41:44 -07:00
parent 85255c96fb
commit 7df848aa6c
2 changed files with 18 additions and 2 deletions

View file

@ -865,7 +865,6 @@ async def exchange_token_with_server(
)
raise
token_response = response.json()
access_token = token_response["access_token"]
# Validate token response against server-configured rules before any storage.
# This rejects tokens from wrong Slack workspaces, Atlassian orgs, etc.
@ -912,7 +911,7 @@ async def exchange_token_with_server(
return await _mint_bridge_delegate_token_response(request, mcp_server, token_response)
result = {
"access_token": access_token,
"access_token": token_response["access_token"],
"token_type": token_response.get("token_type", "Bearer"),
}

View file

@ -4449,6 +4449,23 @@ async def test_oauth_delegate_bridge_token_exchange_fails_closed_without_litellm
assert exc.value.detail["error"] == "invalid_request"
@pytest.mark.asyncio
async def test_oauth_delegate_bridge_token_exchange_missing_access_token_is_502_not_keyerror():
"""When the upstream token response has no access_token, a dcr_bridge oauth_delegate exchange
returns a clean 502 rather than raising a KeyError. The eager access_token extraction used to run
before the bridge branch, so a missing token raised KeyError and _bridge_grant_from_token_response's
nil guard (which maps to 502) was dead code; the extraction now lives on the non-bridge path only."""
from litellm.types.mcp import MCPAuth
server = _bridge_server(auth_type=MCPAuth.oauth_delegate)
upstream = {"token_type": "Bearer", "expires_in": 3600}
with pytest.raises(HTTPException) as exc:
await _exchange_for_bridge_server(server, upstream, key_hash="hashed-litellm-key-77")
assert exc.value.status_code == 502
@pytest.mark.asyncio
async def test_true_passthrough_bridge_token_exchange_returns_raw_upstream_token():
"""Only oauth_delegate mints. A true_passthrough dcr_bridge server relays the raw upstream token