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
namestring—Identifier for this provider, used in logs and error messages.
issuerstring—The 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.
audiencestring—A single accepted aud value.
audienceslist of strings—Additional 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.
emailClaimstring—Claim holding the email address. When unset, the default chain applies (see Claim mapping).
groupsClaimstringgroupsClaim holding group memberships.
nameClaimstring—Claim holding a readable name for machine and agent identities. A dotted path reads a nested claim, for example agent.label. When unset, the defaults apply (see Readable name).
hostedDomainstring—Requires 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
agentMicrosoft Entra app-only agent tokens only, on the MCP and LLM paths. Never users, sessions, the portal or XAA.

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. Entra tenants added under Settings → SSO / OIDC → Agent tokens → Agent identity providers are scoped to agent. 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.

Configure only the audiences your integration needs:

  • 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 tokens use the sign-in application's client ID. For access tokens, ver: 2.0 uses the API's client ID (a GUID); ver: 1.0 can use that GUID or the resource URI, depending on the request. Do not automatically list both formats for a v2-only integration. See Microsoft's access-token claim reference.

Entra token validation also requires the issuer and discovery metadata for the token's version. Accepting both audience formats does not make a v2 configuration accept v1 tokens. See Microsoft's token-version validation guidance and the v2-only Entra agent setup.

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 userIDClaim — sub 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:

email → upn → preferred_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.

Readable name​

Machine and agent tokens usually identify the caller with an opaque value: an Entra application ID, an Okta client ID (0oa…), an Auth0 <client-id>@clients subject. To show a readable name in the console instead, AIControls reads a name claim from the verified token, in this order:

  1. nameClaim, if set.
  2. app_displayname, for Microsoft Entra ID issuers.
  3. name.

If none is present, the console shows the ID. People keep showing their email address.

Set Name Claim under Settings → SSO / OIDC, next to Groups Claim. It applies to tokens, including agent tokens, whose issuer matches that provider. The nameClaim field above does the same for a provider defined in the configuration file. With neither set, the defaults apply, so an Entra application's name needs no configuration once the token carries app_displayname.

The name appears wherever the admin console shows who made a call, including the identity list and the activity, audit, security-event and cost views. Hover over a name to see the ID. Filters, links and exports keep using the ID. The Builder Portal keeps showing IDs.

The name is read only after the token's signature, issuer and audience are verified, and never from request headers. It is display text only: budgets, attribution, audit records and policy keep using the immutable ID, so renaming an application, or giving two applications the same name, doesn't merge or split their history.

warning

The name is controlled by whoever manages the application in your identity provider. Don't use it in policies; match on the ID instead.

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.

autoProvision controls the gateway only. A first sign-in through the Builder Portal always creates the account, whether or not autoProvision is on, and resolves its teams from the token's groups the same way, with the same primary-team rule (the matching team whose ID sorts first in byte order (so Zeta beats alpha), or the team the person picked at sign-in when it matches). See Place new builders in teams from identity provider groups.

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.

The setting applies to both first-sign-in paths. At the gateway, deny refuses the request. In the Builder Portal, deny ends the sign-in with a message asking the person to contact an administrator, and no session, token, or access token is issued. Unlike autoProvision, the portal reads this setting whether or not autoProvision is on, and a Settings save that does not include the field keeps the stored value.

Group values are matched exactly as the identity provider emits them, and the Groups Claim setting only selects which token field to read. Microsoft Entra emits group object IDs, so bind object IDs. When Entra replaces the group list with an overage pointer, AIControls does not resolve it through Graph; the token is treated as carrying no groups and follows this setting.

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.

Entra ID app-only agent tokens​

An agent registered as a Microsoft Entra ID application can call MCP tools and the LLM endpoints with an app-only (client credentials) access token. AIControls verifies the token against the Entra providers you already configured for user sign-in, in OIDC and passthrough identity modes. No separate trust configuration is needed.

A token is treated as an app-only token when its issuer is an Entra ID issuer and it carries idtyp: app, or has an app ID (azp or appid) and no delegated-user claims. It is accepted only when:

  • its iss matches a configured provider's issuer exactly, so the tenant must match;
  • its signature verifies against that provider's keys;
  • its aud is one of the provider's accepted audiences. Providers with skipAudienceCheck are never used for agent tokens;
  • the provider's usage allows the surface: a provider scoped to llm verifies app tokens on the LLM endpoints only, and a provider with no usage verifies them everywhere.

To make Entra issue matching tokens:

  1. In the API app registration that the provider's audience names, set accessTokenAcceptedVersion to 2 in the manifest, so tokens use the https://login.microsoftonline.com/&lt;tenant&gt;/v2.0 issuer your provider is configured with.
  2. Define an app role for agents on that API and grant it to the agent's app registration.
  3. Have the agent request a token for the scope <app ID URI>/.default.

The agent ID is the calling application's client ID. The token's signed claims are recorded as trusted workload metadata with source entra-id.

In OIDC mode, a token for a configured tenant that fails verification is rejected. In passthrough mode, any app-only token that does not verify, including one no configured provider is eligible to check, is handled as an unverified passthrough token. It records no workload metadata.

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. Entra ID app-only agent tokens — see above. A token that does not verify falls through to the next step.
  7. User OIDC tokens — validated against the providers described on this page. This is where Claude Desktop gateway SSO tokens land.
  8. 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.