curl -i -X GET "$KONNECT_PROXY_URL/anything" \
--no-progress-meter --fail-with-body \
-H "Authorization: Bearer $TOKEN_A"Configure OpenID Connect with multiple IdPs using a trusted issuer registry
Configure the OpenID Connect plugin with extra_jwks_uris listing each IdP’s JWKS endpoint and issuers_allowed listing each IdP’s issuer URL.
Set verify_claims to false so that the iss claim is checked against issuers_allowed rather than requiring it to match config.issuer.
Kong Gateway validates incoming tokens against the matching JWKS and forwards them to the upstream without transformation.
Prerequisites
Kong Konnect
This is a Konnect tutorial and requires a Konnect personal access token.
-
Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
-
Export your token to an environment variable:
export KONNECT_TOKEN='YOUR_KONNECT_PAT' -
Run the quickstart script to automatically provision a Control Plane and Data Plane, and configure your environment:
curl -Ls https://get.konghq.com/quickstart | bash -s -- -k $KONNECT_TOKEN --deck-outputThis sets up a Konnect Control Plane named
quickstart, provisions a local Data Plane, and prints out the following environment variable exports:export DECK_KONNECT_TOKEN=$KONNECT_TOKEN export DECK_KONNECT_CONTROL_PLANE_NAME=quickstart export KONNECT_CONTROL_PLANE_URL=https://us.api.konghq.com export KONNECT_PROXY_URL='http://localhost:8000'Copy and paste these into your terminal to configure your session.
Kong Gateway running
This tutorial requires Kong Gateway Enterprise. If you don’t have Kong Gateway set up yet, you can use the quickstart script with an enterprise license to get an instance of Kong Gateway running almost instantly.
-
Export your license to an environment variable:
export KONG_LICENSE_DATA='LICENSE-CONTENTS-GO-HERE' -
Run the quickstart script:
curl -Ls https://get.konghq.com/quickstart | bash -s -- -e KONG_LICENSE_DATAOnce Kong Gateway is ready, you will see the following message:
Kong Gateway Ready
decK v1.62.1+
To complete this tutorial, install decK. We recommend keeping decK up to date with the latest version (1.62.1).
decK is a CLI tool for managing Kong Gateway declaratively with state files.
This guide uses deck gateway apply, which directly applies entity configuration to your Gateway instance.
You can check your current decK version with deck version.
Required entities
For this tutorial, you’ll need Kong Gateway entities, like Gateway Services and Routes, pre-configured. These entities are essential for Kong Gateway to function but installing them isn’t the focus of this guide. Follow these steps to pre-configure them:
-
Run the following command:
echo ' _format_version: "3.0" services: - name: example-service url: http://httpbin.konghq.com/anything routes: - name: example-route paths: - "/anything" service: name: example-service protocols: - http - https ' | deck gateway apply -
To learn more about entities, you can read our entities documentation.
Set up Keycloak with two realms
This tutorial requires two identity providers (IdPs). If you don’t have them, you can simulate two separate IdPs using two Keycloak realms. The steps will be similar with other standard identity providers.
Install and run Keycloak
-
Install Keycloak (version 26 or later) on your platform.
For example, you can use the Keycloak Docker image. The following command attaches Keycloak to the same network as Kong Gateway so that the OIDC plugin can reach it:
docker run -p 127.0.0.1:8080:8080 \ --name keycloak \ --network kong-quickstart-net \ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ -e KC_HOSTNAME=http://localhost:8080 \ quay.io/keycloak/keycloak start-devThe parameter
KC_HOSTNAME=http://localhost:8080ensures Keycloak always useslocalhost:8080as its token issuer regardless of which URL it’s accessed through. This is required because Kong Gateway runs inside Docker and accesses Keycloak via the container namekeycloak:8080, but theissclaim in issued tokens must uselocalhost:8080for the plugin to recognize them. -
Export your environment variables. For example, using Docker and the
masterandrealm-brealms:export DECK_REALM_A_ISSUER='http://localhost:8080/realms/master' export DECK_REALM_B_ISSUER='http://localhost:8080/realms/realm-b' export DECK_REALM_A_JWKS='http://keycloak:8080/realms/master/protocol/openid-connect/certs' export DECK_REALM_B_JWKS='http://keycloak:8080/realms/realm-b/protocol/openid-connect/certs' export KEYCLOAK_HOST='localhost'Because we’re using Docker for this demo, we must configure a few networking parameters:
-
DECK_REALM_A_ISSUERandDECK_REALM_B_ISSUERuselocalhostbecause that’s how you access Keycloak from your machine. -
DECK_REALM_A_JWKSandDECK_REALM_B_JWKSuse the container namekeycloakbecause Kong Gateway runs inside Docker and reaches Keycloak over the sharedkong-quickstart-netnetwork.
In your own setup, especially running outside of a container, you may not need
DECK_REALM_A_JWKSandDECK_REALM_B_JWKS. -
-
Open the Keycloak admin console.
The default URL is
http://localhost:8080/admin/master/console/. -
Log in with the credentials you defined when you launched Keycloak. For this example, the credentials are username
adminand passwordadmin.
Configure the first IdP
The master realm acts as the first identity provider.
Create a client for realm-a:
- In the sidebar, open Clients, then click Create client.
- Configure the client:
|
Section |
Settings |
|---|---|
| General settings |
|
| Capability config |
|
Find the credentials for client-a:
- In the sidebar, open Clients, and select
client-a. - Click the Credentials tab.
- Set Client Authenticator to Client ID and Secret.
- Copy the Client Secret.
-
Export the client secret to an environment variable:
export DECK_CLIENT_A_SECRET='YOUR-CLIENT-SECRET'
Configure the second IdP
Create a second Keycloak realm to simulate a second identity provider:
- In the sidebar click Manage realms.
- Click Create realm.
- Set Realm name to
realm-b. - Click Create.
Create a client for realm-b:
- Make sure you’re in the
realm-brealm (check the top-left dropdown). - In the sidebar, open Clients, then click Create client.
- Configure the client:
|
Section |
Settings |
|---|---|
| General settings |
|
| Capability config |
|
Find the credentials for client-b:
- In the sidebar, open Clients, and select
client-b. - Open the Credentials tab.
- Set Client Authenticator to Client ID and Secret.
- Copy the Client Secret.
-
Export the client secret to an environment variable:
export DECK_CLIENT_B_SECRET='YOUR-CLIENT-SECRET'
Generate salt token
Starting with decK v1.59+, you need to set cache_tokens_salt to avoid regenerating session credentials during sync. Generate a salt token:
export DECK_TOKEN_SALT="$(openssl rand -base64 16)"export DECK_TOKEN_SALT="$(openssl rand -base64 16)"Enable the OpenID Connect plugin for multiple IdPs
Using the Keycloak configuration from the prerequisites, configure the OpenID Connect plugin on example-route to accept tokens from both realms:
echo '
_format_version: "3.0"
plugins:
- name: openid-connect
route: example-route
config:
issuer: "${{ env "DECK_REALM_A_ISSUER" }}"
using_pseudo_issuer: true
jwks_endpoint: "${{ env "DECK_REALM_A_JWKS" }}"
auth_methods:
- bearer
extra_jwks_uris:
- "${{ env "DECK_REALM_B_JWKS" }}"
issuers_allowed:
- "${{ env "DECK_REALM_A_ISSUER" }}"
- "${{ env "DECK_REALM_B_ISSUER" }}"
verify_signature: true
verify_claims: false
cache_tokens_salt: "${salt-token}"
' | deck gateway apply -Auth configuration:
-
issuer: The primary IdP URL, used for discovery and as the canonical issuer reference. -
using_pseudo_issuer: Disables OIDC discovery from theissuerURL. Required here because Kong Gateway runs inside Docker and can’t reachlocalhost:8080directly. Theissuervalue is still used to match theissclaim in tokens fromrealm-a. -
jwks_endpoint: Explicit JWKS endpoint that Kong Gateway uses to fetch signing keys forrealm-a. Uses thekeycloakcontainer name, which is reachable from Kong Gateway over the shared Docker network. -
extra_jwks_uris: The JWKS endpoint forrealm-b. The plugin checks the primary JWKS first, then falls back to this list. -
issuers_allowed: Explicit allowlist of accepted issuers. Tokens whoseissclaim doesn’t match one of these values are rejected. -
verify_claims: Set tofalseso that theissclaim is checked againstissuers_allowedinstead of requiring it to equalconfig.issuer. Without this, tokens fromrealm-bwould fail claim verification.
Validate the flow
Token from realm-a
Get a client credentials token from realm-a:
TOKEN_A=$(curl -s -X POST \
http://$KEYCLOAK_HOST:8080/realms/master/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=client-a" \
-d "client_secret=$DECK_CLIENT_A_SECRET" | jq -r .access_token) && echo $TOKEN_ASend the token to Kong Gateway:
curl -i -X GET "http://localhost:8000/anything" \
--no-progress-meter --fail-with-body \
-H "Authorization: Bearer $TOKEN_A"You should see a 200 response.
When you decode the token in the forwarded Authorization header, the iss claim should be http://localhost:8080/realms/master.
Token from realm-b
Get a client credentials token from realm-b:
TOKEN_B=$(curl -s -X POST \
http://$KEYCLOAK_HOST:8080/realms/realm-b/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=client-b" \
-d "client_secret=$DECK_CLIENT_B_SECRET" | jq -r .access_token) && echo $TOKEN_BSend the token to Kong Gateway:
curl -i -X GET "$KONNECT_PROXY_URL/anything" \
--no-progress-meter --fail-with-body \
-H "Authorization: Bearer $TOKEN_B"curl -i -X GET "http://localhost:8000/anything" \
--no-progress-meter --fail-with-body \
-H "Authorization: Bearer $TOKEN_B"You should see a 200 response.
When you decode the token in the forwarded Authorization header, the iss claim should be http://localhost:8080/realms/realm-b, confirming that Kong Gateway accepted the token from the second IdP and forwarded it unchanged.
Cleanup
Clean up Konnect environment
If you created a new control plane and want to conserve your free trial credits or avoid unnecessary charges, delete the new control plane used in this tutorial.
Destroy the Kong Gateway container
curl -Ls https://get.konghq.com/quickstart | bash -s -- -d