Skip to main content

LiteLLM as resource app

Let an application exchange an ID-JAG for a LiteLLM access token, then use it to run inference or call MCP tools on behalf of an existing LiteLLM user.

LiteLLM's POST /token endpoint issues the token. Inference endpoints such as /v1/chat/completions and the MCP gateway accept it.

info

This feature requires an enterprise license, a database, a shared Redis, and JWT authentication. Configure Redis through litellm_settings.cache with Redis cache_params or through general_settings.coordination_redis, so each ID-JAG is redeemed once across all workers and replicas. Set PROXY_BASE_URL and allowlist at least one requesting app to enable the grant.

Get started​

Select your identity provider for the full setup.

This walkthrough assumes LiteLLM is already registered in Okta. It uses Okta Cross App Access and an OIDC requesting app. Replace your-org.okta.com and litellm.example.com with your domains.

1. Register the requesting app in Okta​

In Applications and Resources > Applications, create or select an OIDC Web Application for the app that will call LiteLLM.

Okta application integration dialog with OIDC and Web Application selected
Create an OIDC Web Application for the requesting app.

Configure the requesting app so users can sign in with Okta:

  1. Under Grant type, select Authorization Code.
  2. In Sign-in redirect URIs, enter the callback URL from the requesting app's sign-in configuration. This is where Okta returns the user after sign-in.
  3. Save the app. Open Assignments and assign the Okta user you'll use to test the flow.
Okta sign-in redirect URI set to https://agent.example.com/callback
Set the requesting app's callback URL

From the app's Machine Assignments > Resources tab, select Register AI agent and complete the registration.

In the agent's Client registration, use client-secret authentication. Save its client ID and secret as IDP_CLIENT_ID and IDP_CLIENT_SECRET in the requesting app. These are also its OIDC sign-in credentials. Activate the agent if it is STAGED.

Okta client registration showing masked client secret and client ID fields
Client secret and client ID

2. Connect the requesting app to LiteLLM​

In Applications and Resources > Applications, open your existing LiteLLM app. Under Assignments, make sure the user from step 1 is assigned to it.

Open Machine Assignments > Callers > Cross App Access > Edit and enable XAA. Set Issuer URL to your LiteLLM URL. Define litellm:mcp and litellm:llm as the supported scopes. Leave Audience/tenant ID empty for this single-tenant example.

ValueURL
Issuer URL / ID-JAG audiencehttps://litellm.example.com
Token endpointhttps://litellm.example.com/token
Authorization server metadatahttps://litellm.example.com/.well-known/oauth-authorization-server
Native client discoveryhttps://litellm.example.com/.well-known/litellm-cli-auth
Okta Cross App Access settings with the bare-origin LiteLLM issuer and litellm:mcp and litellm:llm scopes
Example XAA settings for LiteLLM

On the AI agent's Resource connections tab, select your LiteLLM app. Set the client ID registered in LiteLLM to agent-client, the Resource identifier to https://litellm.example.com, and select Allow specific scopes. Choose the scopes to allow:

Okta resource connection with client ID agent-client, resource https://litellm.example.com, and litellm:mcp and litellm:llm scopes
Connect the requesting app to LiteLLM
Granted scopeAccess
litellm:mcpMCP gateway routes only.
litellm:llmLLM API routes only.
litellm:mcp litellm:llmBoth MCP gateway and LLM API routes.
No scopeFull access within the user and team permissions, as with lite login.

3. Configure LiteLLM​

Configure LiteLLM with the requesting app's client ID and existing client secret. The example uses agent-client and reads its secret from AGENT_CLIENT_SECRET.

values.yaml
envVars:
PROXY_BASE_URL: "https://litellm.example.com"
environmentSecrets: [litellm-id-jag]
proxy_config:
general_settings:
enable_jwt_auth: true
litellm_jwtauth:
id_jag_requesting_apps:
- client_id: agent-client
client_secret: os.environ/AGENT_CLIENT_SECRET

Create or select a LiteLLM user whose email matches the IdP account. Give that user access to the models or MCP tools you want to call. After deploying, check that discovery lists urn:ietf:params:oauth:grant-type:jwt-bearer:

curl -s https://litellm.example.com/.well-known/oauth-authorization-server \
| jq '.grant_types_supported'

In your existing JWT configuration, use Okta's org issuer (https://your-org.okta.com) and JWKS URL (https://your-org.okta.com/oauth2/v1/keys).

4. Sign in and request an ID-JAG​

Configure the requesting app's OIDC login with issuer https://your-org.okta.com and scopes openid profile email. Sign in as the assigned test user and save the login response's id_token as ID_TOKEN. Set REQUESTED_SCOPES="litellm:mcp litellm:llm" to try both examples below, or request just one of the scopes allowed on the resource connection.

Use the agent's Okta credentials from step 1 to exchange the ID token:

Request an ID-JAG
curl -s -X POST https://your-org.okta.com/oauth2/v1/token \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d requested_token_type=urn:ietf:params:oauth:token-type:id-jag \
-d subject_token_type=urn:ietf:params:oauth:token-type:id_token \
--data-urlencode "subject_token=$ID_TOKEN" \
-d audience=https://litellm.example.com \
-d resource=https://litellm.example.com \
--data-urlencode "scope=$REQUESTED_SCOPES" \
--data-urlencode "client_id=$IDP_CLIENT_ID" \
--data-urlencode "client_secret=$IDP_CLIENT_SECRET"

Save the response's access_token as ID_JAG.

5. Get a LiteLLM access token​

Send the ID-JAG as assertion with the jwt-bearer grant. Use the requesting app's client ID and secret at LiteLLM, as configured in id_jag_requesting_apps.

Get a LiteLLM credential
curl -s -X POST https://litellm.example.com/token \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d client_id=agent-client \
--data-urlencode "client_secret=$AGENT_CLIENT_SECRET" \
--data-urlencode "assertion=$ID_JAG"

Confidential clients can also use HTTP Basic authentication (-u "agent-client:$AGENT_CLIENT_SECRET") instead of the client ID and secret form fields. Public clients send client_id without a secret.

{
"access_token": "LneZuxEFqvemEwK6lRzgg6BN...",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "litellm:mcp litellm:llm",
"user_id": "user@example.com",
"team_id": "125eca86-fbde-44de-8cb9-cf1432933fcd"
}

Save access_token as LITELLM_ACCESS_TOKEN. scope echoes the granted scope, which limits route access. No refresh token is issued. When the credential expires, request a fresh ID-JAG and redeem it again.

6. Call LiteLLM​

Use the access token for calls allowed by its scope and the user's LiteLLM permissions. Spend is attributed to their user_id and team_id.

Replace <your-model-name> with a model configured on your proxy.

Run inference
curl -s https://litellm.example.com/v1/chat/completions \
-H "Authorization: Bearer $LITELLM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"<your-model-name>","messages":[{"role":"user","content":"Hello"}]}'

Troubleshooting​

Response or logCause and fix
invalid_target or unauthorized_client from OktaUnder Directory > AI Agents > Resource connections, check the LiteLLM issuer URL, allowed scopes, user assignments, and agent activation.
400 unsupported_grant_typeCheck the enterprise license, database, shared Redis, JWT authentication, PROXY_BASE_URL, and nonempty requesting-app allowlist. Restart after configuration changes. Leave virtual_key_claim_field and jwt_client_id_field unset on this gateway.
400 invalid_request: missing client_id or assertionSend client_id in the form or HTTP Basic authentication, and the ID-JAG in the assertion form field.
401 invalid_clientMatch the allowlisted client ID and secret. Configure a secret only for a confidential client.
400 invalid_grant: wrong typ or malformed JWTSend the ID-JAG returned by the IdP exchange, rather than the user's login token or an ordinary bearer token.
400 invalid_grant: assertion already redeemedRequest a new ID-JAG. Each assertion can be redeemed once.
400 invalid_grant; log reports an audience mismatchSet Machine Assignments > Callers > Issuer URL to the bare origin, <PROXY_BASE_URL>. Check the scheme and trailing slashes.
400 invalid_grant; log reports an untrusted issuer or missing signing keyUse the Okta issuer https://your-org.okta.com and JWKS URL https://your-org.okta.com/oauth2/v1/keys.
400 invalid_grant; log reports a client_id mismatchSet AI agent's client ID registered in this app to the ID the agent sends to LiteLLM.
400 invalid_grant; log reports that the user does not existMap the Okta email or sub to an existing LiteLLM user.
400 invalid_request: team selection or membershipSend x-litellm-team-id with the ID or alias of one of the user's teams.
503 temporarily_unavailable: user teams could not be readRetry. The assertion has not been consumed.
401 when using the ID-JAG on /mcpRedeem it at /token and use the returned access_token.
LiteLLM Enterprise
SSO/SAML, audit logs, spend tracking, multi-team management, and guardrails, built for production.
Learn more →