With the OpenID Connect AI Auth Strategy, AI Gateway accepts the JWT bearer tokens or OAuth 2.0 grants that your AI Consumers already get from an OIDC-compliant identity provider (IdP).
You don’t issue or rotate static API keys, as you would with key auth.
Use OpenID Connect when your AI Consumers already authenticate through an enterprise IdP.
Use key auth when your AI Consumers are internal tools or scripts that you control.
An AI Model can use both at the same time, for example key auth for internal automation and OpenID Connect for user-facing applications.
A request is authenticated if it satisfies either strategy.
AI Agents and AI MCP Servers accept one AI Auth Strategy each.
The strategy’s config object passes through to Kong Gateway’s OpenID Connect plugin.
AI Gateway documents a subset of the plugin’s fields in the schema and accepts the rest for advanced use cases.
OpenID Connect works by forming a federation with an IdP.
The IdP stores account credentials and authenticates the caller.
AI Gateway trusts that authentication instead of managing credentials itself.
Konnect provides Kong Identity, a managed IdP you can use for this, or you can point the AI Auth Strategy at any OIDC-compliant provider you already run.
You create an OpenID Connect AI Auth Strategy once, then attach it by name or id to one or more AI Models, AI Agents, or AI MCP Servers through their access.auth_strategies array.
AI MCP Servers only accept an AI Auth Strategy in conversion-listener, listener, or passthrough-listener mode, the three modes that accept incoming MCP traffic directly.
See the AI MCP Server entity for the full mode reference.
When you attach an AI Auth Strategy to an AI Model, AI Agent, or AI MCP Server, AI Gateway auto-provisions a shared anonymous AI Consumer with a Request Termination policy under the hood, and points the auth strategy’s failure path at it.
By default, this means that any request that fails authentication (for example, no credential, an invalid token, or a valid token that doesn’t map to a known AI Consumer or Kong Identity principal) is routed to that anonymous AI Consumer and rejected with 401 Unauthorized before it reaches the AI Model, AI Agent, or AI MCP Server.
Two settings opt specific requests out of that default termination, without disabling it entirely:
config.consumer_optional: true (openid-connect only): A valid token that doesn’t map to any AI Consumer proceeds instead of terminating. The request still carries no AI Consumer identity.
config.principals.error_on_miss: false (key-auth and openid-connect): A request that doesn’t match any Kong Identity principal proceeds instead of terminating.
Neither setting affects a request without credentials or an invalid token, those always terminate through the anonymous AI Consumer regardless of consumer_optional or principals.error_on_miss.
The following diagram shows the three outcomes for a request against an AI Model, AI Agent, or AI MCP Server with an OpenID Connect AI Auth Strategy attached:
sequenceDiagram
participant Client as AI Consumer
participant Auth as OpenID Connect AI Auth Strategy
participant Anon as Anonymous AI Consumer (Request Termination)
participant Entity as AI Model, AI Agent, or AI MCP Server
Client->>Auth: Request with bearer token
alt Valid token, maps to a known AI Consumer
Auth->>Entity: Proxies request as that AI Consumer
Entity-->>Client: Response
else No token, or invalid token
Auth->>Anon: Routes to anonymous AI Consumer
Anon-->>Client: 401 Unauthorized
else Valid token, no Consumer/principal match, consumer_optional or principals.error_on_miss set
Auth->>Entity: Proxies request without an AI Consumer identity
Entity-->>Client: Response
end
Authentication and authorization are separate steps.
The AI Auth Strategy only confirms who’s calling.
It doesn’t decide which AI Model, AI Agent, or AI MCP Server that caller may reach.
Use access.acls on the entity itself to restrict that by AI Consumer, AI Consumer Group, or Authenticated Group (a dynamic group derived from an OAuth 2.0 scope or claim in the token).
This matters even when every caller shares one IdP.
If several AI Models reference the same OpenID Connect AI Auth Strategy, every caller with a valid token from that IdP passes authentication on all of them.
access.acls is what actually segments access to a specific AI Model, AI Agent, or AI MCP Server.
When you configure config.issuer in the OIDC Auth Strategy, AI Gateway automatically retrieves the provider’s discovery metadata.
The OIDC Auth Strategy stores the metadata as a discovery cache object and uses the cache to avoid repeated fetches. This cache includes the discovery document endpoints, JWKS keys, and the token endpoint.
AI Gateway uses the discovery cache whenever validation needs issuer metadata. The cache behaves in the following way:
The cache TTL (time-to-live) is managed by config.cache_ttl, which is set to 3600 seconds by default.
If a request requires discovery information that isn’t in the cache, the plugin attempts to “rediscover” it using the value in config.issuer. After a rediscovery occurs, no further rediscovery attempts are made until the time period defined in config.rediscovery_lifetime has elapsed, which helps avoid excessive requests to the identity provider.
If a JWT can’t be validated due to missing discovery data, and a rediscovery request returns a non‑2xx status code, the plugin falls back to using any sufficient discovery information that remains in the cache.
Configure which flows are active with config.auth_methods.
AI Gateway enables bearer and client_credentials by default.
AI Gateway searches for credentials in the following order of precedence:
AI Gateway stops at the first match in that order.
This precedence order is the same one AI Gateway’s OpenID Connect plugin uses, since the same plugin engine runs underneath the AI Auth Strategy, and it’s fixed: it can’t be reconfigured.
AI Gateway can issue a session cookie after a caller first authenticates through one of the other flows.
The caller then presents that cookie on subsequent requests instead of re-authenticating.
For example, the authorization code flow demonstrates session authentication when it uses the redirect login action.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with session cookie
deactivate client
kong->>kong: load session cookie
kong->>kong: verify session
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
For legacy reasons, stateless JWT access token authentication is named bearer in config.auth_methods.
AI Gateway verifies the token’s signature against the IdP’s published keys and validates standard claims, such as exp.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with access token
deactivate client
kong->>kong: load access token
kong->>kong: verify signature
kong->>kong: verify claims
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
Like bearer, introspection authentication relies on a token the AI Consumer already obtained.
The difference is that AI Gateway calls the IdP’s introspection endpoint to check whether the token is valid and active, which allows the IdP to issue opaque tokens instead of JWTs.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant idp as IdP (e.g. Keycloak)
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with access token
deactivate client
kong->>kong: load access token
activate idp
kong->>idp: IdP/introspect with client credentials and access token
deactivate kong
idp->>idp: authenticate client and introspect access token
activate kong
idp->>kong: return introspection response
deactivate idp
kong->>kong: verify introspection response
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
User info authentication verifies the access token against the IdP’s standard user info endpoint, instead of the introspection endpoint.
Use introspection instead in most cases, as it’s meant for validating the token itself, where user info is meant for retrieving information about the user the token was issued to.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant idp as IdP (e.g. Keycloak)
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with access token
deactivate client
kong->>kong: load access token
activate idp
kong->>idp: IdP/userinfo with access token
deactivate kong
idp->>idp: verify access token
activate kong
idp->>kong: return user info response
deactivate idp
kong->>kong: verify response status code (200)
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
The refresh token grant can be used when the AI Consumer already has a refresh token.
IdPs generally only allow the refresh token grant to run with the same client that originally obtained the token.
A mismatch can cause it to fail.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant idp as IdP (e.g. Keycloak)
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with refresh token
deactivate client
kong->>kong: load refresh token
activate idp
kong->>idp: IdP/token with client credentials and refresh token
deactivate kong
idp->>idp: authenticate client and verify refresh token
activate kong
idp->>kong: return tokens
deactivate idp
kong->>kong: verify tokens
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
Password grant is a legacy authentication grant.
This is a less secure way of authenticating end users than the authorization code flow, because, for example, the passwords are shared with third parties.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant idp as IdP (e.g. Keycloak)
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: Service with basic authentication
deactivate client
kong->>kong: load basic authentication credentials
activate idp
kong->>idp: IdP/token with client credentials and password grant
deactivate kong
idp->>idp: authenticate client and verify password grant
activate kong
idp->>kong: return tokens
deactivate idp
kong->>kong: verify tokens
activate httpbin
kong->>httpbin: request with access token
httpbin->>kong: response
deactivate httpbin
activate client
kong->>client: response
deactivate kong
deactivate client
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
The authorization code flow is a three-legged OAuth/OpenID Connect flow, including session cookie issuance.
The sequence diagram below describes the participants and their interactions for this usage scenario, including the use of session cookies.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant idp as IdP (e.g. Keycloak)
participant httpbin as Upstream (upstream service, e.g. httpbin)
activate client
activate kong
client->>kong: HTTP request
kong->>client: Redirect mobile app to IDP
deactivate kong
activate idp
client->>idp: Request access and authentication with client parameter
Note left of idp: /auth response_type=code, scope=openid
idp->>client: Login (ask for consent)
client->>idp: /auth with user credentials (grant consent)
idp->>client: Return authorization code and redirect
Note left of idp: short-lived authcode
activate kong
client->>kong: HTTP redirect with authorization code
deactivate client
kong->>kong: Verify authorization code flow
kong->>idp: Request ID token, access token, and refresh token
Note left of idp: /token client_id:client_secret authcode
idp->>idp: Authenticate client (Kong) and validate authcode
idp->>kong: Returns tokens
Note left of idp: ID token, access token, and refresh token
deactivate idp
kong->>kong: Validate tokens
Note right of kong: Cryptographic signature validation, expiry check (OIDC Standard JWT validation)
activate client
kong->>client: Redirect with session cookie having session ID (SID)
Note left of kong: sid: cryptorandom bytes (128 bits) & HMAC protected
client->>kong: Authenticated request with session cookie
deactivate client
kong->>kong: Verify session cookie
Note right of kong: Retrieve encrypted tokens from session store (redis)
activate httpbin
kong->>httpbin: Backend service request with tokens
Note right of idp: Access token and ID token
httpbin->>kong: Backend service response
deactivate httpbin
activate client
kong->>client: HTTP response
deactivate kong
deactivate client
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
If using PKCE, the IdP must include code_challenge_methods_supported in its /.well-known/openid-configuration discovery response, per RFC 8414.
Otherwise, AI Gateway won’t send the PKCE code_challenge parameter.
Beyond access.acls (see How it works), the OpenID Connect AI Auth Strategy supports claims-based authorization.
Claims-based authorization uses a pair of options to manage claims verification during authorization.
The pair can be any of:
config.scopes_claim and config.scopes_required
config.audience_claim and config.audience_required
config.groups_claim and config.groups_required
config.roles_claim and config.roles_required
In each parameter pair, the *_claim parameter points to a source, and the *_required parameter defines a set of claims values to check against.
Claims-based auth adheres to the following rules:
You can validate a maximum of 4 claims at the same time
You can traverse an array or object for the claim name
You can validate multiple values of the same claim using OR and AND logic
Both the claim type and the required claim content take an array of string elements.
For the claim type (for example, config.groups_claim), the array is a list of JSON objects listed in nested order.
AI Gateway uses the order of the items in the array to look up data in a JSON payload.
The value of a claim can be:
A space-separated string (common for scope claims)
A JSON array of strings (common for groups claims)
A simple value, such as a string
For example, look at the following sample payload, where groups is nested inside user:
config.client_auth selects how AI Gateway authenticates itself to the IdP, including through mutual TLS (mTLS) client authentication.
Note: Setting config.client_auth to client_secret_post lets you test the connection to your IdP, but we recommend using a more secure auth method in production.
You can use any of the supported client auth methods.
The OpenID Connect auth strategy supports mutual TLS (mTLS) client authentication with the IdP.
When mTLS authentication is enabled, AI Gateway establishes mTLS connections with the IdP using the configured client certificate.
You can use mTLS client authentication with the following IdP endpoints and corresponding flows:
For all these endpoints and for the flows supported, the auth strategy uses mTLS client authentication as the authentication method when communicating with the IdP, for example, to fetch the token from the token endpoint.
The following capabilities exist on Kong Gateway’s OpenID Connect plugin, which the OpenID Connect AI Auth Strategy’s config object passes through to for advanced use cases.
AI Gateway supports various features of the FAPI standard, aimed at protecting APIs that expose high-value and sensitive data.
Specification
Description
Pushed authorization requests (PAR)
With PAR enabled, AI Gateway (as the OAuth client) sends the payload of an authorization request to the IdP. As a result, it obtains a request_uri value. The client uses this value in a call to the authorization endpoint as a reference to obtain the authorization request payload data.
With JAR enabled, when sending requests to the authorization endpoint, AI Gateway provides request parameters in a JSON Web Token (JWT) instead of using a query string. This allows for request data to be signed with JSON Web Signature (JWS).
With JARM enabled, AI Gateway requests the authorization server to return the authorization response parameters encoded in a JWT, which allows the response data to be signed with JSON Web Signature (JWS).
Set config.response_mode to any of the following values: query.jwt, form_post.jwt, fragment.jwt, jwt to enable JARM.
Certificate-bound access tokens
Certificate-bound access tokens allow binding tokens to clients.
This guarantees the authenticity of the token by verifying whether the sender is authorized to use the token for accessing protected resources.
Mutual TLS (mTLS) client authentication with certificate-bound access tokens
When mTLS client authentication is enabled, AI Gateway establishes mTLS connections with the IdP using the configured X.509 certificate as client credentials.
If the authorization server is configured to bind the client certificate with the issued access token, AI Gateway can validate the access token using mTLS proof of possession.
Demonstrating Proof of Possession (DPoP) is an application-level mechanism for proving the sender’s ownership of OAuth access and refresh tokens.
With DPoP, a client can prove possession of a public/private key pair associated with a token by using a header.
The header contains a signed JWT that includes a reference to the associated access token.
When DPoP is enabled, AI Gateway validates the DPoP header in the request to ensure that the sender is authorized to use the access token.
One of the main vulnerabilities of OAuth is bearer tokens.
With OAuth, presenting a valid bearer token is enough proof to access a resource.
This can create problems, since the client presenting the token isn’t validated as the legitimate user the token was issued to.
Certificate-bound access tokens solve this problem by binding tokens to clients.
This ensures the legitimacy of the token, because it requires proof that the sender is authorized to use a particular token to access protected resources.
Certificate-bound access tokens are supported by the following auth methods:
Session authentication is only compatible with certificate-bound access tokens when used along with one of the other supported authentication methods:
When config.proof_of_possession_auth_methods_validation is set to false and other non-compatible methods are enabled, and a valid session is found, AI Gateway only performs the proof of possession validation if the session was originally created using one of the compatible methods.
If you configure multiple OpenID Connect auth strategy instances with the session auth method, configure a different config.session_secret value on each for additional security. This avoids sessions being shared across auth strategy instances and possibly bypassing the proof of possession validation.
To enable certificate-bound access tokens:
Ensure that the IdP you’re using is set up to generate OAuth 2.0 mutual TLS certificate-bound access tokens.
Use config.proof_of_possession_mtls to verify that the supplied access token belongs to the client, by checking its binding with the client certificate provided in the request.
The following is an example cert-bound access token config:
Demonstrating Proof-of-Possession (DPoP) is an alternative technique to mutual TLS client authentication with certificate-bound access tokens. Unlike mTLS, which binds the token to the mTLS client certificate, DPoP binds the token to a JSON Web Key (JWK) provided by the client.
sequenceDiagram
autonumber
participant client as Client (e.g. mobile app)
participant kong as Gateway
participant upstream as Upstream (backend service, e.g. httpbin)
participant idp as Authentication Server (e.g. Keycloak)
activate client
client->>client: generate key pair
client->>idp: POST /oauth2/token DPoP:$PROOF
deactivate client
activate idp
idp-->>client: DPoP bound access token ($AT)
activate client
deactivate idp
client->>kong: GET https://example.com/resource Authorization: DPoP $AT DPoP: $PROOF
activate kong
deactivate client
kong->>kong: validate $AT and $PROOF
kong->>upstream: proxied request GET https://example.com/resource Authorization: Bearer $AT
deactivate kong
activate upstream
upstream-->>kong: upstream response
deactivate upstream
activate kong
kong-->>client: response
deactivate kong
You can use DPoP without mTLS, and even with plain HTTP, although HTTPS is recommended for enhanced security.
When verification of the DPoP proof is enabled, AI Gateway removes the DPoP header and changes the token type from dpop to bearer.
This effectively downgrades the request to use a conventional bearer token, and lets an upstream without DPoP support work with the DPoP token without losing the protection of the key binding mechanism.
DPoP is compatible with the following authentication methods:
Session authentication is only compatible with DPoP when used along with one of the other supported authentication methods. If you configure multiple OpenID Connect auth strategy instances with the session authentication method, configure a different config.session_secret value on each for additional security. This avoids sessions being shared across auth strategy instances and possibly bypassing the proof of possession validation.
To enable DPoP:
Ensure that the IdP you’re using has DPoP enabled.
Use config.proof_of_possession_dpop to verify that the supplied access token is bound to the client, by checking its association with the JWT provided in the request.
If your APIs serve clients that authenticate with different identity providers, the OIDC auth strategy can validate tokens from multiple issuers at the gateway layer, so backends don’t need per-IdP logic.
You can implement this in one of the following ways:
Trusted issuers registry: Configure the OIDC auth strategy with a list of trusted issuers and their JWKS endpoints using config.issuers_allowed and config.extra_jwks_uris.
AI Gateway validates incoming tokens against the appropriate public keys and forwards them to the backend as-is.
This works best when token formats are consistent across IdPs.
Token exchange: Configure the OIDC auth strategy to swap incoming tokens for a canonical token from one trusted issuer using config.token_exchange.
The backend always receives tokens from a single issuer regardless of which IdP the client used.
This works best when backends must trust one issuer, or when you need to normalize scopes and claims across IdPs.
The OAuth 2.0 Token Exchange (RFC 8693) is an extension to the OAuth 2.0 framework that allows exchanging an existing security token for a new one.
The RFC defines a protocol approach to support scenarios where a client can exchange a token for a new token by interacting with the authorization server.
This is particularly useful in complex environments like microservices or cross-domain federations.
Note: Only access tokens can be exchanged with the OIDC auth strategy. Token exchange isn’t supported on AI MCP Servers that set access.metadata.
Token exchange can be used in several critical use cases:
Downscoping: A service receives a powerful token but only needs a subset of those permissions to call an upstream service.
It exchanges the powerful token for one with fewer scopes to maintain the Principle of Least Privilege.
Internal vs. external tokens: Converting an external opaque token or a third-party token (like a SAML assertion) into an internal JWT that the microservices understand.
Impersonation and delegation: Allowing a service to act on behalf of a user.
For example, a frontend service needs to trade its token for a new token with specific scopes to call a backend service.
Privacy: Removing sensitive user information from a token before passing it to an upstream service.
Because token exchange allows for the creation of new tokens, trust models are vital.
The trust model must strictly define which clients are allowed to exchange tokens and which scopes they are permitted to elevate or downgrade to prevent security flaws like privilege escalations.
In a typical OAuth flow, a token is obtained to access a resource.
However, in a token exchange, a client already has a token (the “subject token”).
AI Gateway decides which incoming tokens are eligible for exchange and facilitates the token exchange using its own client credentials.
The subject token is presented to the authorization server to get a different token (the “requested token”) that is better suited for accessing the resource.
sequenceDiagram
participant C as Client (e.g. mobile app)
participant K as Gateway
participant A as Authorization server (e.g. Keycloak)
participant U as Upstream (backend service, e.g. httpbin)
C->>K: Request with subject token
activate K
note over K: Validate subject token (iss, exp, nbf)
K->>A: Token exchange request
activate A
A-->>K: Exchanged access token
deactivate A
K->>K: Validate exchanged token
K->>U: Proxy request with exchanged token
activate U
U-->>K: Response
deactivate U
K-->>C: Response
deactivate K
Before triggering the exchange, the OIDC auth strategy performs the following checks on the incoming token:
Checks the incoming subject token meets the following criteria:
The issuer (iss claim) matches a configured trusted issuer (subject_token_issuers).
The token is not expired (exp claim).
The token is not used before its time (nbf claim).
If the subject_token_issuer and target_issuer are different, token exchange is triggered.
If the subject_token_issuer and target_issuer are the same, the configured conditions are evaluated to determine whether to trigger token exchange.
AI Gateway uses its client credentials to trigger the exchange.
Afterwards, the OIDC auth strategy continues processing the exchanged token through the rest of its flow.
Depending on the use case, AI Gateway can exchange the token either with the same authorization server that issued the initial subject token, or exchange tokens between different authorization servers.
Subject token: The input token representing the identity/authorization being exchanged.
Subject token issuer: The authorization server that issued the initial token (subject token).
Target issuer: The authorization server protecting the resources (APIs/services).
Conditions: Conditions under which to trigger token exchange.
Conditions look for the presence or absence of two claims: scopes and audience.
The following is an example token exchange config, which exchanges tokens issued by https://keycloak.example.com for a token from the AI Auth Strategy’s own issuer, https://dev-123456.okta.com, before proxying the request upstream:
If you set config.session_storage to redis to store sessions for the session or authorization code flow, you can authenticate to Redis with a cloud provider. This allows you to rotate credentials without relying on static passwords.
The following providers are supported:
AWS ElastiCache
Azure Managed Redis
Google Cloud Memorystore (with or without Valkey)
Each provider also supports an instance and cluster configuration.
You need:
A running Redis instance on an AWS ElastiCache instance for Valkey 7.2 or later or ElastiCache for Redis OSS version 7.0 or later
config.client_id and config.client_secret are array fields on the OpenID Connect AI Auth Strategy schema, and behave the same way as on the plugin.
You can configure the OIDC auth strategy (config.client_id) and
client secrets (config.client_secret), where the ID and client pairs correspond based on their locations in the array.
When making a request, you can specify which client to target to use by including a client ID argument.
For example, after configuring the auth strategy client secrets, you can target a client by name:
curl -X GET "http://localhost:8000?client_id=my-second-client"
Copied!
Or by its index value (starting with 1):
curl -X GET "http://localhost:8000?client_id=2"
Copied!
AI Gateway will look for the client ID in the following locations, in order of precedence:
If config.client_arg is set, AI Gateway checks for that value in the following order: in the request header, URI argument, and body.
If config.client_arg is not set, AI Gateway checks for a client_id in the following order: in the request header, URI argument, and body.
If no client is found in either of those places, AI Gateway uses the first client ID and client secret pair.
Note: Configuring multiple clients is not possible with the client credentials grant, as the auth strategy always uses the client ID passed directly from the client.