Skip to main content

Identity provider fields

Reference

Fields for configuring an OIDC identity provider whose tokens AIControls accepts, and the rules that map a validated token's claims onto the identity used for audit attribution, budgets, and policy evaluation.

Where these are configured

Three surfaces configure identity here. The first and last set the provider fields on this page; the middle one holds client registrations AIControls issues itself:

SurfaceHoldsUse for
Settings → SSO / OIDCOne providerAlmost always all you need: one directory serving the developer portal and AI clients at the gateway. Add the AI client's Client ID under AI client sign-in. This same tab can also enable admin console sign-in — see Admin Console SSO.
Settings → SSO/OIDC → AI AppsClient registrations issued by AIControlsClients that sign in through the gateway rather than against your provider — see Registering a client for this path.
identity.oidcProviders (YAML)Any number of providersSelf-managed deployments that configure by file, and the only way to trust more than one issuer — needed during an acquisition or a migration between identity providers.

All apply immediately; none needs a restart. Tokens are checked against each configured provider in turn, and the first provider that fully verifies the token wins. Providers configured in the console take precedence over YAML ones for the same issuer.

Provider fields

FieldTypeDefaultDescription
namestringIdentifier for this provider, used in logs and error messages.
issuerstringThe token's expected iss value. Signing keys are discovered from <issuer>/.well-known/openid-configuration and cached. Use the issuer value itself, not the metadata URL.
jwksURLstringdiscoveredExplicit signing-key URL, bypassing discovery. Needed when the provider serves keys at a non-standard path, or when the token's iss differs from what the discovery document advertises.
audiencestringA single accepted aud value.
audienceslist of stringsAdditional accepted aud values. A token is accepted if its audience matches any entry, unioned with audience.
skipAudienceCheckbooleanfalseDisables audience validation. See the warning below before setting this.
userIDClaimstringsubClaim holding the stable user identifier. Set to oid for Microsoft Entra ID.
emailClaimstringClaim holding the email address. When unset, the default chain applies (see Claim mapping).
groupsClaimstringgroupsClaim holding group memberships.
hostedDomainstringRequires the token's hd claim to equal this value. Google Workspace only.
clockSkewSecondsinteger60Tolerance applied when checking token expiry. Set to -1 to disable tolerance.
autoProvisionbooleanfalseCreate a portal user the first time a token from this provider authenticates at the gateway. See Auto-provisioning.
autoProvisionFallbackdefault | none | denydefaultWhat happens when an auto-provisioned user's groups match no team group binding. See When no group binding matches.
usagelist of strings(all surfaces)Restricts which surfaces accept tokens from this provider. See Surface scoping.

Surface scoping

A validated token is accepted by whichever surface the provider permits. Surfaces are named by the usage values below:

ValueSurface
llmThe LLM gateway — /v1/messages and the other model endpoints
xaaCross-App Access / ID-JAG grant validation

A provider with no usage is accepted everywhere. That is the right default for an identity provider used for interactive sign-in, and it is what every existing configuration does.

Scoping matters because token validation is shared by many surfaces: the LLM gateway, the developer session API, MCP tool-call user tokens, and the external-authorization hooks. Without a restriction, adding an audience so one client can reach the gateway also makes that audience acceptable at all the others — which is not what "gateway audiences" is asking for, and matters especially when the client is a public OAuth application whose client ID is not secret.

Providers you configure under Gateway Issuers, and the gateway audiences you add under SSO / OIDC, are automatically scoped to llm for this reason. You only need to set usage by hand in YAML.

note

An unrecognized usage value never matches any surface, so a typo makes the provider inert rather than over-permissive.

Audience validation

Every provider should specify an audience. A provider with no audience, no audiences, and no explicit skipAudienceCheck rejects every token at request time, and logs a warning naming itself at startup.

This is deliberate rather than pedantic. A valid signature proves only that your identity provider minted the token — not that it was minted for AIControls. Without an audience check, a token issued to any other application in the same tenant is accepted here, and that application can present its users' tokens to your gateway.

The failure is scoped to the offending provider rather than aborting startup: a configuration that was valid before this requirement existed would otherwise stop the whole deployment from booting — including the admin console you would use to fix it. Other providers keep working, and the warning tells you which entry to correct.

An audience set is genuinely needed in some cases, not just a convenience:

  • A client that signs in under its own app registration presents its own client ID as the audience. Claude Desktop and Cowork gateway SSO work this way — see Claude Desktop & Cowork.
  • Microsoft Entra ID sends the client ID on ID tokens, but the API app ID URI or the bare client GUID on access tokens issued against a custom API scope. List both.
danger

skipAudienceCheck: true accepts any token signed by the configured issuer. Only set it when something else in the request path performs an equivalent, context-aware audience check.

Claim mapping

A validated token is mapped onto an identity as follows.

Principal

The principal is what audit records, budget windows, and model-scope resolution key on. It is resolved in this order:

  1. The email address (see Email below).
  2. The value of userIDClaimsub unless configured otherwise.

A token carrying neither is rejected, rather than being attributed to an empty principal.

Email is preferred because it is what every other identity source keys on — personal access tokens, portal accounts, developer sessions — so a budget or model scope assigned to an email address applies to the same person however they connect.

warning

On Microsoft Entra ID, set userIDClaim to oid. Entra's sub claim is pairwise: it is scoped to the app registration that requested the token, so the same person authenticating through a different application appears as a different principal — silently splitting their budget and attribution history. oid is the tenant-wide immutable user object ID.

Email

When emailClaim is set, that claim is read first. If it is unset or absent from the token, the default chain applies:

emailupnpreferred_username

The fallbacks cover Microsoft Entra ID, which omits email from ID tokens unless it is added as an optional claim. Access tokens frequently carry no email at all; that is not an error — the principal falls back to userIDClaim.

Groups

Read from groupsClaim (default groups) and passed through unchanged, so group-scoped model policies and budgets apply without further configuration. For GitHub issuers the claim defaults to teams instead, since GitHub emits no groups claim.

Groups are also available to policies as object.user.groups — see Policy context.

Roles

Read from roles, falling back to nirmata_approved_actions.

Session ID

Read from the first present of sid, session_id, jti.

Auto-provisioning

autoProvision creates a portal user record the first time a principal from this provider authenticates at the gateway, mirroring LiteLLM's user_id_upsert. It is off by default.

Whether you need it depends on one thing: do you use team-scoped model access?

Team membership lives on the portal user record. A gateway-authenticated user who has no record resolves to the global model ceiling rather than their team's — so team-scoped model restrictions do not apply to them at all. They are not blocked; they are simply governed by the wrong ceiling. Enable autoProvision when users authenticate directly at the gateway (Claude Desktop SSO, for instance) without first visiting the portal.

Provisioned users get:

  • The builder role, and authSource: oidc.
  • Teams derived from the token's groups via your team group bindings (Settings → Teams). See below for what happens when no binding matches.
  • The same principal key the gateway attributes requests to, so budgets, model scope and audit records all line up on one value.

Existing records are never modified. If an admin adjusts a user's teams or role after their first sign-in, a later request does not revert it.

note

Users appear on the Identities page (Inventory → Identities) whether or not auto-provisioning is enabled — that listing is populated from request traffic. Auto-provisioning is specifically about the portal user record that carries team membership.

When no group binding matches

autoProvisionFallback decides what happens to a user whose token groups match none of your team group bindings:

ValueBehavior
default (default)The user is placed in the default team.
noneThe user is provisioned with no team. The global model ceiling applies; no team grant is inferred.
denyThe request is refused, with an error naming this setting.

If you rely on team-scoped model ceilings, set this to none or deny. The default value is the widest of the three: if your default team is permissive, a user whose groups did not map is silently governed by a more generous ceiling than their real team allows. Nothing is blocked and nothing looks wrong — the access is simply larger than intended.

This is easy to trigger by accident across an entire deployment. Microsoft Entra ID emits group GUIDs, not display names, so bindings written against names match nothing for any user, and everyone falls through to the fallback. Check the group values your IdP actually emits — an audit event's identity detail shows the groups on the token.

deny applies only to the no-binding case: a user whose groups do map is unaffected, and a user who already has a portal record is never re-evaluated. A transient failure to write the user record does not deny the request under any setting — a valid token is not refused over a bookkeeping error.

An unrecognized value is treated as default and logged as a warning at startup, rather than failing to boot. Because that resolves to the widest mode, check your logs after setting this.

Time validation

ClaimBehavior
expRejected once expired, with clockSkewSeconds of tolerance (default 60).
nbfRejected if more than roughly five minutes in the future. This leeway matches what the Microsoft identity libraries apply and is not affected by clockSkewSeconds.
iatNot validated.

Raise clockSkewSeconds if you see intermittent expiry rejections caused by clock drift between your identity provider and the proxy.

Tokens minted by AIControls

A client can authenticate without any external-IdP configuration of its own, by treating AIControls as its Authorization Server: it discovers the gateway's metadata, signs in through AIControls, and presents a token AIControls minted.

The same document is served at both discovery paths — /.well-known/oauth-authorization-server (RFC 8414) and /.well-known/openid-configuration (OpenID Connect Discovery 1.0, with the two extra fields that specification requires) — so clients that expect an OAuth authorization server and clients that expect an OpenID Provider both discover the same endpoints. Clients of the second kind never probe the RFC 8414 path, and a client whose issuer field names the gateway is one of them.

The token endpoint returns an access token only, no id_token, and a client offering a choice must be set to send the access token as its bearer credential.

That is a deliberate design choice rather than a missing feature. An ID token is an authentication assertion audienced to a client; AIControls is a resource server, and accepting client-audienced tokens as API credentials invites audience confusion, carries no scope with which to enforce least privilege, and has no revocation semantics — an ID token stays valid until it expires. The access token issued here is opaque and its revocation status is checked on every request.

Prefer access tokens generally, including with external providers, whenever the provider can audience them to the gateway. Where it cannot — Microsoft Entra ID requires exposing an API and a scope, which some tenants will not permit, leaving an access token audienced to Microsoft Graph — an ID token audienced to the app is the better of the two, and providers are configured for id_token mode for that reason. It is a compatibility trade-off, not a security improvement.

AIControls performs the identity-provider exchange itself, server-side, so this path works even where the provider will not permit a public client — Google requires a client secret on the code exchange for every client type, which a client sending none cannot satisfy.

Registering a client for this path

A client on this path needs a client ID issued by AIControls, not by your identity provider. Register one under Settings → SSO/OIDC → AI Apps: give it a name and the loopback port the client listens on for its sign-in callback, and copy the client ID it returns. The table there lists what is registered, when each was last used, and lets you revoke one.

The common loopback callback paths are registered for you on both 127.0.0.1 and localhost, so a client that uses a path other than the one you expected still completes sign-in. Every registration is a public client: the client runs on the end user's machine and cannot hold a secret, which is what PKCE is for.

Revoking a registration stops new sign-ins with that client ID. It does not invalidate access tokens already issued through it — those are per-user tokens listed under Access Tokens, and revoking one there is how you cut off a single person while leaving the application usable.

note

This is a different field from SSO / OIDC → AI client sign-in. That one lists the audiences of tokens your identity provider issues, for clients that sign in against the provider directly. Registrations here are for clients that sign in through AIControls instead. Which applies depends on whether your provider will issue tokens to a public client.

Such a token is accepted on both the MCP and LLM planes. It is verified against the Authorization Server's own signing key, its standard claims validated, and its revocation status checked on every request, so it resolves to a verified identity (source: xaa) and satisfies requireVerifiedIdentity.

note

The Authorization Server advertises scopes_supported: ["mcp"]. That scope is recorded on the identity and available to policy, but it is not enforced as an inference-versus-tools split: requiring a separate inference scope would reject every token obtained through the advertised flow. Restrict what these callers may reach using model scope, budgets or a CEL policy on object.identity.scopes.

Precedence against other credentials

The LLM endpoints accept several credential types. A bearer token is resolved in this order, and the first matching branch decides:

  1. Virtual keys — tokens prefixed virt_.
  2. Developer session tokens — tokens prefixed sess_.
  3. Personal access tokens — tokens prefixed aic_.
  4. Raw API keys — a token containing no . cannot be a JWT, and is handled as a provider key (forwarded in passthrough mode, otherwise rejected with instructions).
  5. Tokens minted by AIControls' own Authorization Server — see above. Tried before the identity providers, since such a token is signed by AIControls rather than an IdP and would fail all of them. A verification failure falls through to the next step rather than rejecting.
  6. User OIDC tokens — validated against the providers described on this page. This is where Claude Desktop gateway SSO tokens land.
  7. Service-account tokens — Kubernetes projected tokens, for machine-to-machine calls.

Because the prefixes in steps 1–3 are proxy-issued and a JWT always contains dots, these branches cannot shadow one another.

Diagnosing rejections

When no provider accepts a token, the 401 response names the token's own presented iss and aud. That is usually enough to spot the common mistakes — pointed at the wrong tenant, or signed in under the wrong application — and clients that authenticate directly against the gateway show it to the end user.

The expected values are deliberately not in the response. Publishing them would let any unauthenticated caller enumerate every configured issuer and accepted audience, which in a shared deployment discloses other tenants' configuration and hands over precisely the target list that audience validation exists to defend against.

The full comparison — the presented token against every configured provider — is written to the proxy logs at warning level. Look there for the expected values when a token is being rejected. See Troubleshooting for the common causes.

Model discovery fields

GET /v1/models returns the standard OpenAI-compatible fields (id, object, created, owned_by) plus two AIControls extensions, for clients that auto-discover models:

FieldTypeDescription
anthropic_family_tierstringopus, sonnet, or haiku. Lets a client tell which Claude tier an opaque model ID maps to — including a gateway alias, a Bedrock model ID, or a Vertex model ID.
supports1mbooleantrue when the model has the 1M-token context window.

Both are omitted when unknown, so a non-Anthropic model carries neither and clients that ignore unrecognized fields are unaffected. When a model ID is an alias configured on the upstream, the alias is resolved before the tier and window are determined.