Skip to main content

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:

SettingValue
Client typeOpenID Connect
Client authenticationOn: a confidential client
Standard flowOn: the authorization code flow, and nothing else
Implicit flowOff
Direct access grantsOff
Service accountsOff
OAuth 2.0 Device Authorization Grant, OIDC CIBA GrantOff
Consent requiredOff
Valid redirect URIsexactly https://gateway.example.test/api/v1/auth/oidc/callback/keycloak
Web originsnone
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 as clientSecretFile.
  • 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 as clientKeyFile, and the key id Keycloak knows it by as clientKeyId.

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 id member of GET 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.