Summary
_extract_upstream_claims (added in #2997) allows subclasses to embed custom claims in the FastMCP JWT under the upstream_claims key. These claims are correctly signed into the JWT at issuance. However, load_access_token doesn't surface them during validation — it swaps the JWT for the upstream provider token and returns that, losing the upstream_claims.
Expected Behavior
Claims returned by _extract_upstream_claims should be accessible on the AccessToken returned by load_access_token, since they were signed into the JWT and verified during validation.
Actual Behavior
load_access_token:
- Verifies the FastMCP JWT signature (payload includes
upstream_claims) ✅
- Extracts
jti from the payload
- Looks up the upstream token via JTI mapping
- Validates the upstream token → returns
AccessToken with upstream provider claims only
upstream_claims from step 1 is discarded ❌
Reproduction
- Subclass
OIDCProxy and override _extract_upstream_claims to return custom claims
- Authenticate via OAuth — the FastMCP JWT correctly contains
upstream_claims
- Make an MCP tool call —
load_access_token validates the JWT but the returned AccessToken.claims only has the upstream provider's claims, not the upstream_claims from the FastMCP JWT
Workaround
Override load_access_token to re-decode the already-verified JWT and merge upstream_claims into the AccessToken:
async def load_access_token(self, token: str) -> AccessToken | None:
access_token = await super().load_access_token(token)
if access_token is None:
return None
payload = pyjwt.decode(token, options={"verify_signature": False})
upstream_claims = payload.get("upstream_claims")
if upstream_claims:
access_token.claims["upstream_claims"] = upstream_claims
return access_token
Suggested Fix
In OAuthProxy.load_access_token, after verifying the JWT and looking up the upstream token, merge upstream_claims from the verified JWT payload into the returned AccessToken.claims. This makes _extract_upstream_claims useful for both external JWT consumers (API gateways) and internal FastMCP tool dependencies.
Summary
_extract_upstream_claims(added in #2997) allows subclasses to embed custom claims in the FastMCP JWT under theupstream_claimskey. These claims are correctly signed into the JWT at issuance. However,load_access_tokendoesn't surface them during validation — it swaps the JWT for the upstream provider token and returns that, losing theupstream_claims.Expected Behavior
Claims returned by
_extract_upstream_claimsshould be accessible on theAccessTokenreturned byload_access_token, since they were signed into the JWT and verified during validation.Actual Behavior
load_access_token:upstream_claims) ✅jtifrom the payloadAccessTokenwith upstream provider claims onlyupstream_claimsfrom step 1 is discarded ❌Reproduction
OIDCProxyand override_extract_upstream_claimsto return custom claimsupstream_claimsload_access_tokenvalidates the JWT but the returnedAccessToken.claimsonly has the upstream provider's claims, not theupstream_claimsfrom the FastMCP JWTWorkaround
Override
load_access_tokento re-decode the already-verified JWT and mergeupstream_claimsinto theAccessToken:Suggested Fix
In
OAuthProxy.load_access_token, after verifying the JWT and looking up the upstream token, mergeupstream_claimsfrom the verified JWT payload into the returnedAccessToken.claims. This makes_extract_upstream_claimsuseful for both external JWT consumers (API gateways) and internal FastMCP tool dependencies.