diff --git a/litellm/proxy/auth_v2/authenticators/oauth.py b/litellm/proxy/auth_v2/authenticators/oauth.py index d2e0ea0035d..275471dd45c 100644 --- a/litellm/proxy/auth_v2/authenticators/oauth.py +++ b/litellm/proxy/auth_v2/authenticators/oauth.py @@ -87,6 +87,7 @@ class OAuth2Authenticator(Authenticator): audience=token_audience, scopes=split_scope(body.get("scope")), claims=claims, + subject_token=token, ) def challenge(self) -> str: diff --git a/litellm/proxy/auth_v2/authenticators/utils.py b/litellm/proxy/auth_v2/authenticators/utils.py index be20515dcc2..eab8311dc35 100644 --- a/litellm/proxy/auth_v2/authenticators/utils.py +++ b/litellm/proxy/auth_v2/authenticators/utils.py @@ -66,6 +66,7 @@ def credential_from_claims( scopes=split_scope(claims.get("scope")), claims=claims, credential_ref=CredentialRef(key_id=header.get("kid"), token_id=claims.get("jti")), + subject_token=token, ) diff --git a/litellm/proxy/auth_v2/models.py b/litellm/proxy/auth_v2/models.py index 7f5df99e836..046442b0ec9 100644 --- a/litellm/proxy/auth_v2/models.py +++ b/litellm/proxy/auth_v2/models.py @@ -97,6 +97,12 @@ class Credential(BaseModel): credential_ref: CredentialRef = Field(default_factory=CredentialRef) client_certificate: Optional[ClientCertificate] = None + # Raw bearer/access token as presented by the caller, retained so it can be + # used as the subject_token for downstream token exchange (RFC 8693) when + # calling LLM providers or MCP servers on the caller's behalf. None for + # schemes without an exchangeable token (API key, HTTP basic, mTLS). + subject_token: Optional[str] = None + class Principal(BaseModel): """Normalized caller identity. Identity only, no policy/budget state."""