For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Set up staged JWT auth
Verify JWTs from an identity provider with a remote JWKS and staged enforcement, then explore other entJWT options.
Learn how to use an identity provider (IdP) to enforce a staged JWT filter with a remote JWKS. Verifying the token after external authentication makes the verified claims available to role-based access control (RBAC). This way, users can log in with their own credentials to authenticate to your services. After you set up the filter, see the Other configurations section for more entJWT options.
Before you begin
Follow the Get started guide to install Solo Enterprise for kgateway.
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
Step 1: Set up an IAM provider
Complete the Keycloak OAuth guide to set up an IAM provider. Make sure that you have the following details for your IAM provider.
- The issuer domain of the IAM provider.
- The port that the issuer domain listens on, such as 443 for HTTPS.
- The JWKS endpoint of the issuer domain.
- To get a JWT to test the steps, you typically need the client ID and secret of the auth application, the auth endpoint, as well as user credentials.
Example endpoint values by IAM provider:
- Issuer domain:
${KEYCLOAK_URL}/realms/<realm>/ - JWKS endpoint:
${KEYCLOAK_URL}/realms/<realm>/protocol/openid-connect/certs - Auth endpoint:
${KEYCLOAK_URL}/realms/<realm>/protocol/openid-connect/token
Step 2: Create a JWT filter
Use the Keycloak JSON Web Key Set (JWKS) endpoint to validate incoming JWT keys. You also extract claims from the JWT and add them as headers so that you can use them to enforce fine-grained RBAC decisions.
Set the environment variables for your IAM provider. You captured these values when you completed the Keycloak OAuth guide in the previous step.
echo $HOST_KEYCLOAK echo $PORT_KEYCLOAK echo $KEYCLOAK_URL echo $KEYCLOAK_CLIENT echo $KEYCLOAK_SECRETCreate a Backend to expose the endpoint of your IAM provider.
kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: Backend metadata: name: keycloak namespace: keycloak spec: type: Static static: hosts: - host: ${HOST_KEYCLOAK} port: ${PORT_KEYCLOAK} EOFCreate a ReferenceGrant to allow the EnterpriseKgatewayTrafficPolicy in your namespace to access the Keycloak Backend.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: allow-keycloak-access namespace: keycloak spec: from: - group: enterprisekgateway.solo.io kind: EnterpriseKgatewayTrafficPolicy namespace: httpbin - group: enterprisekgateway.solo.io kind: EnterpriseKgatewayTrafficPolicy namespace: kgateway-system to: - group: gateway.kgateway.dev kind: Backend name: keycloak EOFCreate an EnterpriseKgatewayTrafficPolicy with your JWT rules. In this example, you verify incoming JWTs from the
jwtheader against the Keycloak JWKS endpoint that you created earlier, and add the verifiedemailclaim into anx-solo-claim-emailheader before forwarding the request to the upstream service. The JWT policy is enforced after the external auth stage.kubectl apply -f- <<EOF apiVersion: enterprisekgateway.solo.io/v1alpha1 kind: EnterpriseKgatewayTrafficPolicy metadata: name: jwt-gw-policy namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http entJWT: afterExtAuth: providers: keycloak: issuer: ${KEYCLOAK_URL}/realms/master tokenSource: headers: - header: jwt jwks: remote: url: ${KEYCLOAK_URL}/realms/master/protocol/openid-connect/certs backendRef: name: keycloak namespace: keycloak kind: Backend group: gateway.kgateway.dev claimsToHeaders: - claim: email header: x-solo-claim-email EOFReview the following table to understand this configuration.
Setting Description targetRefsSelect the routing resource to apply the policy to, such as a Gateway, ListenerSet, or HTTPRoute. The example uses the httpGateway that you configured as part of the getting started.entJWTConfigure the rules for the JWT filter. afterExtAuthThe staged JWT lets you select whether to apply the JWT filter before or after external authentication. In this example, the JWT filter applies after external auth. providersConfigure the details of your IAM provider, such as the JWKS endpoint that you want to use to validate incoming JWTs. issuerEnter the issuer domain of the IAM provider. This value must match the issclaim in the JWT that the IAM provider returns. Common errors such asJwt issuer is not configuredmight indicate a different issuer or a missing trailing slash.tokenSourceSpecify where the JWT token is retrieved from, such as the jwtheader in this example.jwksThe remote JWKS endpoint and Backend reference for your IAM provider. If you use an IAM provider other than Keycloak, update the endpoint accordingly. claimsToHeadersExtract and add claims from the JWT as headers in the response. In this example, the emailclaim from the JWT is added to anx-solo-claim-emailheader that the gateway forwards to the upstream service.Create an HTTPRoute that routes requests along the
jwt.example.comdomain to the httpbin app.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-jwt-route namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - jwt.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: httpbin port: 8000 EOFSend a request to the protected httpbin route without a JWT. Verify that you get back a
401 Unauthorizederror code.curl -vik http://$INGRESS_GW_ADDRESS:8080/get \ -H "host: jwt.example.com:8080"curl -vik localhost:8080/get \ -H "host: jwt.example.com:8080"Example response:
HTTP/1.1 401 Unauthorized Jwt is missingGet a JWT through an authorization code flow, and save the ID token in an environment variable.
- To get a JWT, you often need the client ID and secret of the auth application, as well as user credentials.
- Make sure that you get back a JWT that includes the claims that you configured in the JWT filter, like the
emailclaim. - For steps to get a JWT, follow your provider’s documentation. For example, you might use the Keycloak guide to get the token from the provider that you set up earlier.
Example command:
export TOKEN=$(curl -Ssm 10 --fail-with-body \ -d "client_id=${KEYCLOAK_CLIENT}" \ -d "client_secret=${KEYCLOAK_SECRET}" \ -d "username=user1" \ -d "password=password" \ -d "grant_type=password" \ "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token) echo $TOKENVerify that the
emailclaim is included in your token.echo $TOKEN | python3 -c " import sys, base64, json payload = sys.stdin.read().strip().split('.')[1] padding = 4 - len(payload) % 4 decoded = base64.urlsafe_b64decode(payload + '=' * padding) print(json.dumps(json.loads(decoded), indent=2)) "Repeat the request to your httpbin app. This time, include the JWT that you just created as part of the
jwtheader.curl -vik http://$INGRESS_GW_ADDRESS:8080/get \ -H "host: jwt.example.com:8080" \ -H "jwt: $TOKEN"curl -vik localhost:8080/get \ -H "host: jwt.example.com:8080" \ -H "jwt: $TOKEN"Example output: Verify that you get back a 200 success status code. Notice that the
emailclaim is added to theX-Solo-Email-Claimheader as defined in the JWT filter. In the example, the user email isuser1@example.com.HTTP/1.1 200 OK { "args": {}, "headers": { "Accept": [ "*/*" ], "Host": [ "jwt.example.com:8080" ], "User-Agent": [ "curl/8.7.1" ], "X-Solo-Claim-Email": [ "user1@example.com" ] }, ... }
Other configurations
The entJWT field supports more options than the previous example shows. Add the following fields to the EnterpriseKgatewayTrafficPolicy as needed. Each snippet shows only the relevant part of the entJWT configuration.
Enforce JWT before external auth
Use beforeExtAuth instead of afterExtAuth to verify the JWT before external authentication runs. The provider options are the same; only the stage changes.
entJWT:
beforeExtAuth:
providers:
keycloak:
# issuer, jwks, and other provider settingsChange the validation policy
By default, only requests that present a valid JWT are allowed. Set validationPolicy on the stage to allow requests with a missing or failed token, such as when you chain JWT with another authentication method.
entJWT:
afterExtAuth:
validationPolicy: AllowMissingOrFailed
providers:
keycloak:
# provider settings| Value | Behavior |
|---|---|
RequireValid (default) | Allow only requests that authenticate with a valid JWT. |
AllowMissing | Allow requests with no JWT, but reject requests that present an invalid JWT. |
AllowMissingOrFailed | Allow requests even when the JWT is missing or fails verification, such as to fall through to another authentication method. |
Read the token from a custom location
By default, the token is read from the Authorization header as a bearer token. Use tokenSource to read it from other headers or query parameters. The filter tries each entry in order.
providers:
keycloak:
tokenSource:
headers:
- header: x-jwt
prefix: "Bearer "
queryParams:
- access_token
# other provider settingsKeep the token for the upstream service
By default, the gateway removes the token’s header before it forwards the request. Set keepToken to forward the token to the upstream service.
providers:
keycloak:
keepToken: true
# other provider settingsAdjust the clock skew
The filter allows 60 seconds of clock skew when it checks time-based claims such as exp and nbf. Set clockSkewSeconds to change the tolerance.
providers:
keycloak:
clockSkewSeconds: 120
# other provider settingsDisable JWT for a stage
Set disable on a stage to turn off JWT authentication for that stage. This is useful to override a JWT policy that is applied at a higher level in the configuration hierarchy.
entJWT:
afterExtAuth:
disable: {}Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl delete EnterpriseKgatewayTrafficPolicy -n kgateway-system jwt-gw-policy
kubectl delete ReferenceGrant -n keycloak allow-keycloak-access
kubectl delete Backend -n keycloak keycloak
kubectl delete HTTPRoute -n httpbin httpbin-jwt-route