Keycloak
The supplied environment uses Keycloak 26.x. The working reference pattern uses AIGW private_key_jwt, which the supplied backend implements as an RFC 7523 JWT bearer authorization grant for the user assertion plus JWT client authentication for the AIGW client.
This is not the same thing as simply enabling Keycloak Standard Token Exchange. In the supplied notes, Keycloak Standard Token Exchange does not accept the external AIGW-signed subject token in the tested standard mode, so the RFC 7523 path is recommended for this setup.
Before you start
Collect these values from the AIGW ResourceResourceThe downstream system registered in a Project. Its kind (Token exchange or OAuth client) decides the role AIGW plays.:
- AIGW Resource authorization-server/issuer URL, for example
https://api.aigw.eval.loginid.ai/<RESOURCE_ID>; - Resource JWKSJWKSJSON Web Key Set: a published set of public keys used to verify JWT signatures. URL
https://api.aigw.eval.loginid.ai/<RESOURCE_ID>/.well-known/jwks.json; - client ID configured in the AIGW Resource;
- requested scopes;
- your Keycloak realm issuer and token endpoint.
The AIGW Resource Reference page exposes the merchant-facing values.

Step 1 — Create a JWT Authorization Grant Identity Provider
In Keycloak go to Identity providers → Add provider → JWT Authorization Grant.

Configure:
| Field | Value |
|---|---|
| Alias | Unique alias for this AIGW Resource, e.g. aigw-demo-search-api |
| Issuer | AIGW per-resource issuer |
| Use JWKS URL | On |
| JWKS URL | AIGW per-resource JWKS URL |
| Max allowed assertion expiration | 1 hour in the supplied setup |
| Assertion signature algorithm | ES256 |
| Allow assertion reuse | Off in the reference setup |
| Allowed clock skew | 60 seconds in the reference setup |

After saving, verify that the provider exists and still points to the intended Resource issuer/JWKS.

The AIGW issuer is per Resource. A Keycloak JWT Authorization Grant IdP configured for Resource A should not be reused for Resource B with a different issuer.
Step 2 — Create the Keycloak client used by AIGW
Go to Clients → Create client and choose OpenID Connect.
The Client ID must exactly match the Client ID configured on the AIGW Resource.
In Capability config:
- Client authentication: On
- Standard flow: Off for the newly created back-channel client in the supplied walkthrough
- JWT Authorization Grant: On
- Allowed Identity Providers for JWT Authorization Grant: the IdP alias from step 1
- Standard Token Exchange was also enabled in the tested console, but the AIGW Keycloak recommendation still uses the JWT Authorization Grant path

Step 3 — Configure Signed JWT client authentication
Open the client Credentials tab:
- Client Authenticator: Signed JWT
- Signature algorithm: ES256
- Max expiration: 1 minute in the reference setup

When Keycloak asks Change to client-jwt?, confirm the change. Otherwise the new authenticator is not saved.
Next open Keys:
- Use JWKS URL: On
- JWKS URL: the same AIGW Resource JWKS URL

Using a JWKS URL lets Keycloak fetch an updated key when AIGW rotates to a new kid.
Step 4 — Configure the custom audience mapping
The supplied AIGW user/client assertions use the AIGW Resource's configured Issuer URL (the merchant AS issuer) as aud.
In Keycloak open the client Advanced → OpenID Connect Compatibility Modes → Custom audience mapping and map:
<JWT Authorization Grant IdP alias> -> <Keycloak realm issuer>
Example:
aigw-demo-search-api -> https://id.example.com/realms/shop

This mapping is essential in the supplied working reference.
Step 5 — Create and attach merchant scopes
For every AIGW Requested scope, create a Keycloak client scope with the same protocol value.
Example for search:read:
- Name:
search:read - Type: None
- Protocol: OpenID Connect
- Include in token scope: On

Then open the AIGW client and attach the scope as Optional.

Using Type None avoids assigning the scope as a realm-wide default to unrelated clients.
Step 6 — Point the AIGW Resource to Keycloak
Back in AIGW, open the Token exchange ResourceToken exchange ResourceA Resource for agent access: AIGW authorizes the user and obtains a token from the merchant's own authorization server. Configuration and set:
Issuer URL = https://<keycloak>/realms/<realm>
Token endpoint = https://<keycloak>/realms/<realm>/protocol/openid-connect/token
Client ID = <the Keycloak client created above>
Method = private_key_jwt

Reload after saving and verify the values persisted.
Step 7 — Define user matching/provisioning
This is the part most likely to block the first real exchange.
AIGW tries user assertion sub values using login-evidence emails first and the AIGW pairwise subject last. The supplied Keycloak reference user was linked to the JWT Authorization Grant IdP using a federated identity link whose external user ID matched the AIGW subject.
A new JWT Authorization Grant IdP starts with no linked users. Decide whether you will:
- pre-link known users;
- map email to an existing Keycloak user;
- auto-create/link a Keycloak user through a custom flow/extension;
- reject unknown users.
The supplied material does not verify auto-provisioning for the newly created IdP, so do not assume it works without testing.
Step 8 — Validate the setup
Check each layer separately:
- Keycloak can fetch the AIGW Resource JWKS.
- The Keycloak client uses Signed JWT/ES256.
- Audience mapping points the IdP alias to the realm issuer.
- Every requested AIGW scope exists and is attached.
- The target user can be resolved from the assertion
sub. - AIGW Resource issuer/token endpoint/client ID exactly match Keycloak.
- Assertion lifetime is accepted by Keycloak's configured maximum-expiration rules.
Common Keycloak pitfalls
| Problem | Why it happens | Fix |
|---|---|---|
| IdP alias collision | Add-provider form may prefill jwt-authorization-grant | Use a unique alias per AIGW Resource |
| New scope never appears | Include in token scope defaults Off | Turn it On |
| Scope accidentally assigned widely | Client scope Type set to Default/Optional during creation | Use Type=None, then attach explicitly |
invalid_client | Signed JWT/JWKS/algorithm/lifetime mismatch | Recheck client credentials + keys |
invalid_grant / user not found | No user mapping/link for assertion sub | Configure mapping/provisioning |
| JWT rejected for audience | Custom audience mapping missing | Map IdP alias to realm issuer |
| Key rotation fails | Keycloak has static key instead of JWKS URL | Configure the Resource JWKS URL |
All objects shown above were created and saved in the supplied Keycloak environment, and the AIGW Resource was pointed at that configuration. The full AIGW → Keycloak → merchant-token exchange for the new Resource was not completed end to end in the supplied run.