Skip to main content

SDK Proxy Authentication (OAuth2/JWT Auto-Refresh)

Automatically obtain and refresh OAuth2/JWT tokens when using the LiteLLM Python SDK with a LiteLLM Proxy that requires JWT authentication.

Overview​

When your LiteLLM Proxy is protected by an OAuth2/OIDC provider (Azure AD, Keycloak, Okta, Auth0, etc.), your SDK clients need valid JWT tokens for every request. Instead of manually managing token lifecycle, litellm.proxy_auth handles this automatically:

  • Obtains tokens from your identity provider
  • Caches tokens to avoid unnecessary requests
  • Refreshes tokens before they expire (60-second buffer)
  • Injects Authorization: Bearer <token> headers into every request

Quick Start​

Azure AD​

Uses the DefaultAzureCredential chain (environment variables, managed identity, Azure CLI, etc.):

import litellm
from litellm.proxy_auth import AzureADCredential, ProxyAuthHandler

# One-time setup
litellm.proxy_auth = ProxyAuthHandler(
credential=AzureADCredential(), # uses DefaultAzureCredential
scope="api://my-litellm-proxy/.default"
)
litellm.api_base = "https://my-proxy.example.com"

# All requests now include Authorization headers automatically
response = litellm.completion(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)

Required package: pip install azure-identity

Generic OAuth2 (Okta, Auth0, Keycloak, etc.)​

Works with any OAuth2 provider that supports the client_credentials grant type:

import litellm
from litellm.proxy_auth import GenericOAuth2Credential, ProxyAuthHandler

litellm.proxy_auth = ProxyAuthHandler(
credential=GenericOAuth2Credential(
client_id="your-client-id",
client_secret="your-client-secret",
token_url="https://your-idp.example.com/oauth2/token"
),
scope="litellm_proxy_api"
)
litellm.api_base = "https://my-proxy.example.com"

response = litellm.completion(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)

Custom Credential Provider​

Implement the TokenCredential protocol to use any authentication mechanism:

import time
import litellm
from litellm.proxy_auth import AccessToken, ProxyAuthHandler

class MyCustomCredential:
"""Any class with a get_token(scope) -> AccessToken method works."""

def get_token(self, scope: str) -> AccessToken:
# Your custom logic to obtain a token
token = my_auth_system.get_jwt(scope=scope)
return AccessToken(
token=token,
expires_on=int(time.time()) + 3600
)

litellm.proxy_auth = ProxyAuthHandler(
credential=MyCustomCredential(),
scope="my-scope"
)

Supported Endpoints​

Auth headers are automatically injected for:

EndpointFunction
Chat Completionslitellm.completion() / litellm.acompletion()
Embeddingslitellm.embedding() / litellm.aembedding()

How It Works​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Your β”‚ β”‚ ProxyAuthHandler β”‚ β”‚ Identity β”‚ β”‚ LiteLLM β”‚
β”‚ Code │────▢│ (token cache) │────▢│ Provider β”‚ β”‚ Proxy β”‚
β”‚ β”‚ β”‚ │◀────│ (Azure AD, β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ Okta, etc) β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ Authorization: Bearer <token> β”‚ β”‚
β”‚ │──────────────┼───────────────────────────────────▢│ β”‚
β”‚ │◀─────────────┼────────────────────────────────────│ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. You set litellm.proxy_auth once at startup
  2. On each SDK call (completion(), embedding()), the handler checks its cached token
  3. If the token is missing or expires within 60 seconds, it requests a new one from your identity provider
  4. The Authorization: Bearer <token> header is injected into the request
  5. If token retrieval fails, a warning is logged and the request proceeds without auth headers

API Reference​

ProxyAuthHandler​

The main handler that manages the token lifecycle.

from litellm.proxy_auth import ProxyAuthHandler

handler = ProxyAuthHandler(
credential=<TokenCredential>, # required - credential provider
scope="<oauth2-scope>" # required - OAuth2 scope to request
)
ParameterTypeRequiredDescription
credentialTokenCredentialYesA credential provider (AzureADCredential, GenericOAuth2Credential, or custom)
scopestrYesThe OAuth2 scope to request tokens for

Methods:

MethodReturnsDescription
get_token()AccessTokenGet a valid token, refreshing if needed
get_auth_headers()dictGet {"Authorization": "Bearer <token>"} headers

AzureADCredential​

Wraps any azure-identity credential with lazy initialization.

from litellm.proxy_auth import AzureADCredential

# Uses DefaultAzureCredential (recommended)
cred = AzureADCredential()

# Or wrap a specific azure-identity credential
from azure.identity import ManagedIdentityCredential
cred = AzureADCredential(credential=ManagedIdentityCredential())
ParameterTypeRequiredDescription
credentialAzure TokenCredentialNoAn azure-identity credential. If None, uses DefaultAzureCredential

GenericOAuth2Credential​

Standard OAuth2 client credentials flow for any provider.

from litellm.proxy_auth import GenericOAuth2Credential

cred = GenericOAuth2Credential(
client_id="your-client-id",
client_secret="your-client-secret",
token_url="https://your-idp.com/oauth2/token"
)
ParameterTypeRequiredDescription
client_idstrYesOAuth2 client ID
client_secretstrYesOAuth2 client secret
token_urlstrYesToken endpoint URL

AccessToken​

Dataclass representing an OAuth2 access token.

from litellm.proxy_auth import AccessToken

token = AccessToken(
token="eyJhbG...", # JWT string
expires_on=1234567890 # Unix timestamp
)

TokenCredential Protocol​

Any class implementing this protocol can be used as a credential provider:

from litellm.proxy_auth import AccessToken

class MyCredential:
def get_token(self, scope: str) -> AccessToken:
...

Provider-Specific Examples​

Keycloak​

from litellm.proxy_auth import GenericOAuth2Credential, ProxyAuthHandler

litellm.proxy_auth = ProxyAuthHandler(
credential=GenericOAuth2Credential(
client_id="litellm-client",
client_secret="your-keycloak-client-secret",
token_url="https://keycloak.example.com/realms/your-realm/protocol/openid-connect/token"
),
scope="openid"
)

Okta​

from litellm.proxy_auth import GenericOAuth2Credential, ProxyAuthHandler

litellm.proxy_auth = ProxyAuthHandler(
credential=GenericOAuth2Credential(
client_id="your-okta-client-id",
client_secret="your-okta-client-secret",
token_url="https://your-org.okta.com/oauth2/default/v1/token"
),
scope="litellm_api"
)

Auth0​

from litellm.proxy_auth import GenericOAuth2Credential, ProxyAuthHandler

litellm.proxy_auth = ProxyAuthHandler(
credential=GenericOAuth2Credential(
client_id="your-auth0-client-id",
client_secret="your-auth0-client-secret",
token_url="https://your-tenant.auth0.com/oauth/token"
),
scope="https://my-proxy.example.com/api"
)

Azure AD with Managed Identity​

from azure.identity import ManagedIdentityCredential
from litellm.proxy_auth import AzureADCredential, ProxyAuthHandler

litellm.proxy_auth = ProxyAuthHandler(
credential=AzureADCredential(
credential=ManagedIdentityCredential()
),
scope="api://my-litellm-proxy/.default"
)

Combining with use_litellm_proxy​

You can use proxy_auth together with use_litellm_proxy to route all SDK requests through an authenticated proxy:

import os
import litellm
from litellm.proxy_auth import AzureADCredential, ProxyAuthHandler

# Route all requests through the proxy
os.environ["LITELLM_PROXY_API_BASE"] = "https://my-proxy.example.com"
litellm.use_litellm_proxy = True

# Authenticate with OAuth2/JWT
litellm.proxy_auth = ProxyAuthHandler(
credential=AzureADCredential(),
scope="api://my-litellm-proxy/.default"
)

# This request goes through the proxy with automatic JWT auth
response = litellm.completion(
model="vertex_ai/gemini-2.0-flash-001",
messages=[{"role": "user", "content": "Hello!"}]
)