For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Access token validation
Validate an access token that an API client already holds, and reject requests without a valid one.
Protect a route by validating an access token that the client already holds. Kgateway checks the token signature against the Keycloak signing keys and rejects requests that do not carry a valid token, instead of redirecting them to a login page. Use this flow for API clients, which cannot follow a browser redirect.
Before you begin
Complete the Keycloak setup page. This flow needs the realm, the client, the test user, the Backend, and the BackendConfigPolicy that it creates, and it needs the audience mapper so that tokens carry your client ID in the aud claim.
Unlike the authorization code flow, this flow does not need the client secret in a Kubernetes Secret, because the gateway never exchanges an authorization code. It also works over plain HTTP, because it does not use cookies.
Configure access token validation
Create a GatewayExtension that tells the gateway how to validate tokens, and a TrafficPolicy that enforces it on a route.
-
Create a GatewayExtension for JWT validation. The
issuermust match the token’sissclaim from Keycloak.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: GatewayExtension metadata: name: keycloak-jwt namespace: kgateway-system spec: jwt: providers: - name: keycloak issuer: https://keycloak.example.com/realms/myrealm jwks: remote: backendRef: group: gateway.kgateway.dev kind: Backend name: keycloak namespace: kgateway-system url: https://keycloak.example.com/realms/myrealm/protocol/openid-connect/certs audiences: - kgateway-client EOFField Description nameA required, unique name for the provider. The resource is rejected without it. issuerMust match the issclaim in your tokens exactly. Keycloak derivesissfrom the URL that the token request arrives on, so a token that you fetch through a port-forward onhttps://localhost:9443carries that address, not the in-cluster Service name. Decode a real token and read itsissclaim rather than assuming.jwks.remote.backendRefThe network path that the gateway uses to fetch the signing keys. This is the Backendfor Keycloak, so the JWKS endpoint does not have to be reachable from outside the cluster.jwks.remote.urlThe JWKS URL. Kgateway connects through backendRef, and uses this value for the request path andHostheader.audiencesThe accepted values of the audclaim. A token is rejected with a403response if none of its audiences match. Note: Do not addaccounttoaudiencesas a way to make token validation pass.accountis the realm’s built-in client, and every token that the realm issues carries it, so accepting it lets a token minted for any client in the realm through this policy. If your tokens do not contain your client ID, add the audience mapper to the Keycloak client instead. You can decode a token and check itsaudclaim at jwt.io. -
Create a TrafficPolicy that references the JWT GatewayExtension. Make sure that the TrafficPolicy is in the same namespace as the HTTPRoute that it targets.
kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: keycloak-jwt-policy namespace: httpbin spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: httpbin jwtAuth: extensionRef: name: keycloak-jwt namespace: kgateway-system EOF -
Confirm that the TrafficPolicy is attached.
kubectl get TrafficPolicy keycloak-jwt-policy -n httpbin -o yaml
Verify
Use the verification steps below to confirm that the Access Token Validation flow works.
-
Get the JWKS URI from Keycloak:
-
In the Keycloak admin console, go to the Realm settings > General tab.
-
Scroll down to the Endpoints section.
-
Open the OpenID Endpoint Configuration link in a new tab.
-
In the OpenID configuration, find the
jwks_urifield. -
Confirm that the value matches the
jwks.remote.urlfield that you set on theGatewayExtension, for example:https://keycloak.example.com/realms/myrealm/protocol/openid-connect/certs
-
-
Verify that a request without a token is rejected.
curl -vi "http://$INGRESS_GW_ADDRESS:8080/headers" -H "host: www.example.com"Example output:
< HTTP/1.1 401 Unauthorized -
Obtain a token from Keycloak with the
passwordgrant. The-koption accepts the self-signed certificate that the setup steps create.Request the token from the same Keycloak address that you set as the
issueron theGatewayExtension. Keycloak derives theissclaim from the address the request arrives on, so fetching a token from a different address produces a token that the gateway rejects.export TOKEN=$(curl -sk -d "client_id=kgateway-client" \ -d "client_secret=YOUR_CLIENT_SECRET" \ -d "username=testuser" \ -d "password=password" \ -d "grant_type=password" \ "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token" \ | jq -r .access_token) -
Confirm that the token’s
issandaudclaims match yourGatewayExtension. Decode the payload.echo $TOKEN | jq -rR 'split(".")[1] | @base64d' | jq '{iss, aud}'Example output. If
auddoes not include your client ID, add the audience mapper to the Keycloak client.{ "iss": "https://keycloak.example.com/realms/myrealm", "aud": ["kgateway-client", "account"] } -
Send a request with the token in the
Authorizationheader.curl -vi "http://$INGRESS_GW_ADDRESS:8080/headers" \ -H "host: www.example.com" \ -H "Authorization: Bearer $TOKEN"A successful response shows the headers from the httpbin app.
< HTTP/1.1 200 OKA
403response means that the token is valid but itsaudclaim does not match theaudienceslist. A401response means that the token is missing, expired, or signed by an issuer that does not matchissuer.
Cleanup
You can remove the resources that you created in this guide.kubectl delete TrafficPolicy keycloak-jwt-policy -n httpbin
kubectl delete GatewayExtension keycloak-jwt -n kgateway-systemTo remove Keycloak and the shared resources, see the Cleanup section of the Keycloak setup page.