Configuring Keycloak for OpenID Connect Sign-In
This page sets up a Keycloak realm as the OpenID provider for the REST Gateway's OpenID Connect sign-in. It was verified against Keycloak 26.8.1: the Gateway's own login page, driven in a real browser through Keycloak's own login form, signed a user in, made an authorized call and was refused a denied one by the key server.
The examples use the Gateway at https://gateway.example.test and Keycloak at
https://idp.example.test, with a realm named te and a provider id of keycloak.
1. The realm
Use an existing realm or create one. The Gateway needs nothing special from it, but these settings match what the Gateway enforces:
- Require SSL: all requests. The Gateway accepts only
https://issuers. - Leave user self-registration off. The Gateway signs in only users you map, so a user who
registers themselves gets nothing but
identity_not_mapped.
The realm's issuer is https://<host>/realms/<realm>, here
https://idp.example.test/realms/te. It is exactly the issuer that the realm's
/.well-known/openid-configuration document reports. The Gateway compares it byte for byte,
so use the same host name that Keycloak's hostname setting gives it.
2. The client
Create an OpenID Connect client for the Gateway, for example te-gateway:
| Setting | Value |
|---|---|
| Client type | OpenID Connect |
| Client authentication | On: a confidential client |
| Standard flow | On: the authorization code flow, and nothing else |
| Implicit flow | Off |
| Direct access grants | Off |
| Service accounts | Off |
| OAuth 2.0 Device Authorization Grant, OIDC CIBA Grant | Off |
| Consent required | Off |
| Valid redirect URIs | exactly https://gateway.example.test/api/v1/auth/oidc/callback/keycloak |
| Web origins | none |
| Proof Key for Code Exchange Code Challenge Method (Advanced) | S256 |
| Use refresh tokens (Advanced, OpenID Connect Compatibility Modes) | Off |
The redirect URI is <public origin>/api/v1/auth/oidc/callback/<providerId>: the Gateway's
public origin, the fixed callback path, and the providerId you give this provider in the
Gateway's configuration. List it exactly: no wildcard, and no trailing slash. It must be
identical to the provider's callbackUri in the Gateway's configuration.
PKCE S256 is required: the Gateway always sends an S256 code challenge, and refuses a provider whose discovery document does not offer S256.
Refresh tokens off: the Gateway never uses a refresh token. When a session reaches its end the user signs in again. With refresh tokens off, Keycloak issues none for the Gateway to discard.
A realm exported to JSON carries these settings as:
{
"clientId": "te-gateway",
"protocol": "openid-connect",
"publicClient": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": false,
"consentRequired": false,
"redirectUris": ["https://gateway.example.test/api/v1/auth/oidc/callback/keycloak"],
"webOrigins": [],
"attributes": {
"pkce.code.challenge.method": "S256",
"use.refresh.tokens": "false",
"oauth2.device.authorization.grant.enabled": "false",
"oidc.ciba.grant.enabled": "false"
}
}
Keep the client secret out of any export you store: set it in the admin console or through the admin API.
3. Client authentication
The Gateway authenticates to Keycloak's token endpoint in one of two ways:
- Client secret (Keycloak: Client Id and Secret). Copy the secret from the client's
Credentials tab into a file the Gateway's service account alone can read. Configure the
provider with
"clientAuth": "client_secret_basic"and that file's absolute path asclientSecretFile. - Signed JWT (Keycloak: Signed Jwt,
private_key_jwt). Give Keycloak the public key and keep the private key in a file the Gateway's service account alone can read. Configure"clientAuth": "private_key_jwt", the key file's absolute path asclientKeyFile, and the key id Keycloak knows it by asclientKeyId.
The secret or key never goes into the Gateway's configuration file itself; only its path does.
4. Keycloak's TLS certificate
The Gateway verifies Keycloak's TLS certificate. If it is issued by a public CA the system
trust store covers it. Otherwise, put the issuing CA's certificate in a PEM file and name it as
the provider's caFile.
List every host Keycloak's discovery document names in endpointHosts. For a single Keycloak
host that is one entry, idp.example.test, with the port if it is not 443.
5. Mapping users
The Gateway maps a user by the ID token's sub claim. In Keycloak that is the user's
ID, a UUID, and not the user name: renaming a user does not change it. Read it:
- in the admin console, under Users → the user → Details → ID; or
- through the admin API, from the
idmember ofGET https://idp.example.test/admin/realms/te/users?username=<user name>&exact=true.
Then add a mapping to the Gateway's configuration, naming the XAuth principal the user acts as on the key server:
{
"bindingId": "console",
"issuer": "https://idp.example.test/realms/te",
"subject": "<the user's Keycloak ID>",
"xauthName": "alice",
"enabled": true,
"revision": 1
}
The issuer must be the provider's issuer, exactly. The principal must already exist on
the key server: nothing is created at sign-in. A Keycloak user with no enabled mapping is
refused with identity_not_mapped. Two realms are two issuers: the same person in two realms
has two subjects and needs two mappings.
6. The Gateway's provider entry
{
"providerId": "keycloak",
"issuer": "https://idp.example.test/realms/te",
"clientId": "te-gateway",
"clientAuth": "client_secret_basic",
"clientSecretFile": "/var/lib/pisoft/console-gateway/secrets/oidc-keycloak-client-secret",
"allowedAlgs": ["RS256", "ES256"],
"callbackUri": "https://gateway.example.test/api/v1/auth/oidc/callback/keycloak",
"endpointHosts": ["idp.example.test"],
"caFile": "/var/lib/pisoft/console-gateway/secrets/oidc-keycloak-ca.pem"
}
Keycloak signs ID tokens with RS256 by default, which allowedAlgs must include.
When Keycloak rotates its signing key, the Gateway refuses a token signed with a key it has not
seen until jwksRefreshMinSeconds (default 30 seconds) after its last key fetch. It then
fetches Keycloak's keys once and accepts the new key. A login refused in that window succeeds
when the user tries again.
Checking it
Open https://gateway.example.test/oidc/?provider=keycloak&binding=console in a browser, select
Sign in, and sign in at Keycloak's form in the window that opens. The page shows the
principal you are signed in as. If it shows a code instead, see
the refusals you will see. The Gateway's log names
the specific check that failed.