Token endpoint
Authorization code exchange and Proxy client credentials, with claims and errors.
Checked against the Evaluation environment, Console 0.10.1 / 0.14.1, on October 19, 2026. Release status
Endpoint
POST https://core.api.aigw.eval.loginid.ai/oauth2/tokenContent-Type: application/x-www-form-urlencodedThe token endpoint lives on the core host, not on the per-Resource gateway path. Read it from the token_endpoint field of the Resource's discovery document instead of hard-coding it.
| Grant | Used by | Status |
|---|---|---|
authorization_code | Agents, after the user approves | Supported |
client_credentials | MCP and UCP services (Proxy clients) | Supported |
refresh_token | Agents, to renew an expired access token | Unverified |
Client authentication
| Client | Method | Status |
|---|---|---|
| Agent (registered with DCR) | client_secret_post: client_id and client_secret in the form body | Supported |
| Agent (registered with DCR) | client_secret_basic: HTTP Basic header | Unsupported |
| Proxy client (created in Console) | HTTP Basic header with the Proxy client ID and secret | Supported |
Register agents with "token_endpoint_auth_method": "client_secret_post". Registering with client_secret_basic is not implemented for agents in this release.
Authorization code grant
Exchange the code from the authorization endpoint for the user's AIGW tokens. PKCE is required.
Form parameters
grant_typestringRequiredauthorization_codecodestringRequired- The code returned to your redirect URI. Single use.
redirect_uristringRequired- Exactly the redirect URI sent on the authorization request.
client_idstringRequired- From Dynamic Client Registration.
client_secretstringRequired- From Dynamic Client Registration.
code_verifierstringRequired- The PKCE verifier whose S256 hash you sent as code_challenge.
curl -s -X POST "https://core.api.aigw.eval.loginid.ai/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$CODE" \ --data-urlencode "redirect_uri=$REDIRECT_URI" \ --data-urlencode "client_id=$CLIENT_ID" \ --data-urlencode "client_secret=$CLIENT_SECRET" \ --data-urlencode "code_verifier=$CODE_VERIFIER"{ "access_token": "<AIGW access token, a JWT>", "id_token": "<ID token>", "refresh_token": "<refresh token>", "scope": "openid search:read"}Response fields
access_tokenJWT- The AIGW access token. The agent sends it as
Authorization: Bearerto the MCP or UCP service. Claims are listed below. id_tokenJWT- Identifies the user to your agent. Contains personal data: do not log it or forward it to services.
refresh_tokenstring- Renews the access token. See the refresh grant, Unverified.
scopestring- Space-separated scopes granted.
expires_in and token_type are not documented for this grant. Read exp from the access token instead.
Client credentials grant
MCP and UCP services use this grant to get a Proxy token. You need it, together with the user's AIGW token, to redeem the merchant token. Create the Proxy client in the Console under your Project.
Request
AuthorizationheaderRequiredBasic base64(client_id:client_secret)of the Proxy client.grant_typestringRequiredclient_credentials
curl -s -X POST "https://core.api.aigw.eval.loginid.ai/oauth2/token" \ -u "$PROXY_CLIENT_ID:$PROXY_CLIENT_SECRET" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=client_credentials"{ "access_token": "<Proxy access token>", "expires_in": 3599}Cache the Proxy token and request a new one shortly before expires_in runs out. It is not tied to any user, so one token serves every request from your service.
Refresh token grant
Unverified A refresh_token is issued, but renewing with it has not been confirmed end to end in this release. The request below follows RFC 6749 section 6. If it fails, send the user through the authorization flow again.
curl -s -X POST "https://core.api.aigw.eval.loginid.ai/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=$REFRESH_TOKEN" \ --data-urlencode "client_id=$CLIENT_ID" \ --data-urlencode "client_secret=$CLIENT_SECRET"Access token claims
| Claim | Meaning |
|---|---|
iss | The core issuer. Must equal the issuer in the Resource's discovery document. |
aud | The resource indicator the token is bound to. Services must check it is theirs. |
sub | An opaque user id, for example sub_…. Stable per user; contains no personal data. |
exp | Expiry as a Unix timestamp. |
ext.acr | Authentication assurance level reached by the user, for example a value ending in :al2. |
ext.amr | Methods the user authenticated with, for example ["mfa", "kba"]. |
ext.agent | The agent the token was issued to. |
ext.act | The actor acting on the user's behalf. |
ext.project_id | The Console Project that issued the token. |
The full structure of ext.agent and ext.act is not documented for this release. Treat unknown fields as optional.
Validate tokens
If you run an MCP or UCP service, validate every AIGW token before you act on it:
- Verify the signature with keys from the
jwks_uriin discovery. Cache keys bykidand refetch when you see an unknownkid, so key rotation never breaks you. - Check
issequals the discovery issuer andaudcontains your resource indicator. - Reject tokens past
exp. Allow at most a small clock skew. - Use
subas the user key. Never ask the agent for the user's email or name.
Errors
Unverified Error responses from this endpoint have not been documented for this release. The core token service is an OAuth 2.0 server, so expect the standard RFC 6749 section 5.2 format: HTTP 400 (or 401 for client authentication failures) with a JSON body.
{ "error": "invalid_grant", "error_description": "<human-readable detail>"}| error | Common cause | Recovery |
|---|---|---|
invalid_request | A required parameter is missing or repeated | Check the parameter table. Send the body as form data, not JSON. |
invalid_client | Wrong client ID or secret, or an unsupported authentication method | Agents: send credentials in the body (client_secret_post). Proxy clients: use HTTP Basic. Rotate the secret in the Console if it may be wrong. |
invalid_grant | Code expired or already used, redirect URI mismatch, or wrong code_verifier | Start a fresh authorization request with a new PKCE pair. Do not retry the same code. |
unauthorized_client | The client is not allowed this grant type | Register the agent with the grant types it uses, or use a Proxy client for client_credentials. |
unsupported_grant_type | Misspelled or unsupported grant_type | Use one of the grants listed on this page. |
invalid_scope | A scope not configured on the Resource | Request only scopes configured on the Resource in the Console. |
Limits
Rate limits, token lifetimes other than the Proxy token's 3599 seconds, and retry guidance are not documented for this release. Until they are, retry only on network errors and 5xx responses, with exponential backoff, and never retry invalid_grant. See the production checklist.

