Get a merchant access token
The MCP/UCP service uses two server-side calls to obtain the merchant access token associated with the user's authorization.
Why two tokens are involved
The agent gives your server the AIGW access token representing the authorized user/agent session. Your server separately proves its own identity to AIGW with a project-level Proxy client credential. AIGW requires both contexts before it returns the merchant access token.
This prevents the agent from directly redeeming merchant credentials and prevents an MCP/UCP server from redeeming a user token from a different AIGW project.
Step 1 — Get a proxy token
Use OAuth client_credentials against the AIGW core token endpoint:
POST https://core.api.aigw.loginid.ai/oauth2/token
Authorization: Basic base64(<PROXY_CLIENT_ID>:<PROXY_CLIENT_SECRET>)
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
A successful response contains access_token, token_type, and expires_in. Cache the Proxy token until near the returned expiry; do not assume a fixed lifetime.
Step 2 — Redeem for the merchant access token
The user's AIGW access token must include ext_token:read and come from authorization for this Resource. Call the AIGW credential endpoint:
POST https://api.aigw.loginid.ai/api/credentials/token
Authorization: Bearer <AIGW_ACCESS_TOKEN>
X-Proxy-Token: <PROXY_TOKEN>
Content-Type: application/json
{
"resource": "https://mcp.example.com/mcp"
}
The resource value must exactly match the Resource indicator / audience configured on the AIGW Token exchange Resource. A different URL or trailing slash is not interchangeable.
A successful response contains the merchant access token, and can include its expiry and the scopes the merchant authorization server granted:
{
"access_token": "<MERCHANT_ACCESS_TOKEN>",
"expires_at": "2030-01-01T18:30:00Z",
"scopes": ["search:read"]
}
The merchant refresh token is never returned; AIGW keeps it and refreshes on your behalf. See Credential redemption for response fields and error bodies.
Try retrieval from your server terminal
Prerequisites: Bash or Zsh, curl, and jq; a completed user authorization for this Resource; and a same-Project Proxy client. Set AIGW_ACCESS_TOKEN, AIGW_PROXY_CLIENT_ID, and AIGW_PROXY_CLIENT_SECRET in your server environment. The user token needs ext_token:read. These are runtime credentials, separate from the DevCLI (aigw command-line tool) management API key.
set -o pipefail
AIGW_RESOURCE_INDICATOR='https://mcp.example.com/mcp'
PROXY_TOKEN=$(curl --fail-with-body --silent --show-error \
--user "$AIGW_PROXY_CLIENT_ID:$AIGW_PROXY_CLIENT_SECRET" \
--data-urlencode 'grant_type=client_credentials' \
'https://core.api.aigw.loginid.ai/oauth2/token' | jq -er '.access_token')
# Run only after the command above succeeds and PROXY_TOKEN is nonempty.
if [ -n "$PROXY_TOKEN" ]; then
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $AIGW_ACCESS_TOKEN" \
-H "X-Proxy-Token: $PROXY_TOKEN" \
-H 'Content-Type: application/json' \
--data "$(jq -nc --arg resource "$AIGW_RESOURCE_INDICATOR" '{resource:$resource}')" \
'https://api.aigw.loginid.ai/api/credentials/token' \
| jq '{hasAccessToken: (.access_token != null), expires_at, scopes}'
fi
Replace the indicator with the exact value registered in your Resource. Expected result: hasAccessToken: true, with expiry/scopes when returned. This example displays a summary; your backend uses the returned access_token in the merchant API call. If retrieval fails, use the status and error response with Handle retrieval errors.
Project matching rule
The AIGW access token and proxy token must belong to the same Project. Redemption fails when the Proxy client belongs to a different Project than the user's token. Cache lookup also binds the user token to its specific login ID; a token from another sign-in cannot retrieve this login's merchant credential. X-Proxy-Token contains the token value without a Bearer prefix.
When the merchant access token is actually provisioned
AIGW requests the merchant access token when the user completes authorization. Sign-in can succeed even if the merchant authorization server rejects that request. The stored token is bound to the user’s login, Project, and Resource; check the Resource’s Events if no token is available.
The credential redemption call then reads that cached token and refreshes it when needed. If a refresh token exists, AIGW uses it. If it is missing or rejected, AIGW removes the unrenewable cache entry and returns 410 with msgCode: reauthentication_required. Start a new user authorization; AIGW does not mint a fresh user assertion from an expired login.
Merchant-token provisioning is best effort. A user can complete sign-in even if it fails, and the failure may only surface when the MCP/UCP server later tries to redeem the credential.
How AIGW talks to the merchant authorization server
For the private_key_jwt configuration, AIGW sends:
- an RFC 7523 JWT bearer grant containing a user assertion; and
- JWT client authentication (
client_assertion) for AIGW's merchant-AS client.
The user assertion is ES256-signed using the Resource signing key. The relevant claims are:
| Claim | Value |
|---|---|
iss | AIGW per-resource issuer https://api.aigw.loginid.ai/<RESOURCE_ID> |
sub | User identifier presented to the merchant: an email from the user's sign-in provider or the AIGW pairwise subject |
aud | Merchant authorization-server issuer configured as Issuer URL |
iat, exp | Current time and configured assertion lifetime |
jti | Unique UUID |
The client assertion uses the merchant-AS client ID as iss and sub and the same merchant issuer as aud.
User matching fallback
AIGW first tries each email returned by the user's sign-in provider, in order, then the AIGW pairwise subject. Each assertion contains one of these identifiers as sub. AIGW only tries the next identifier when the merchant AS returns invalid_grant with an error description containing user not found (case-insensitive). Other errors stop the attempt.
This means the merchant IAM integration needs an explicit strategy for user lookup/provisioning. See Merchant Authorization Server.
Handle retrieval errors
| Status | Meaning / next action |
|---|---|
400 | Missing/malformed input or an unsupported exchange configuration; check msgCode and msg |
401 | User/proxy authentication, user ext_token:read scope, revoked proxy client, or Project mismatch |
404 | Wrong indicator/Project or no cached credential for this login; inspect provisioning events |
410 | reauthentication_required: start user authorization again |
500 | Service/downstream failure; inspect Resource Events and retry transient failures with bounded backoff |
The ID token is not an acceptable substitute for the AIGW user access token. On an exchange failure, inspect the Events tab even when the user sign-in succeeded.