Provider example
Learn how to use a JWT from an IAM provider for claims-based authentication.
In the previous basic JWT guide, you created a JWT filter with an inline JWKS and used your own JWTs to authenticate. Now, learn how to use an IAM provider to enforce a JWT filter with role-based access control (RBAC). This way, users can log in with their own credentials to authenticate to your services.
Before you begin
Follow the Get started guide to install Gloo Gateway.
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.
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 a JSON Web Key Set (JWKS) endpoint to get the email claim information of the JWT from the IAM provider. The claim is placed in an email header that you use to enforce fine-grained RBAC decisions.
Create 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 GlooTrafficPolicy in your namespaces 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: gloo.solo.io kind: GlooTrafficPolicy namespace: httpbin - group: gloo.solo.io kind: GlooTrafficPolicy namespace: gloo-system to: - group: gateway.kgateway.dev kind: Backend name: keycloak EOFCreate a GlooTrafficPolicy with the following JWT rules.
kubectl apply -f- <<EOF apiVersion: gloo.solo.io/v1alpha1 kind: GlooTrafficPolicy metadata: name: jwt-gw-policy namespace: gloo-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http glooJWT: 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.glooJWTConfigure 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 keycloakin this example.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, such as forgetting a trailing slash.tokenSourceSpecify where the JWT token is retrieved, such as the jwtheader in this example.jwksThe remote URL is the JWKS endpoint from your IAM provider, such as the Keycloak example. If you use a different IAM provider, update the endpoint accordingly. You created the Backend in a previous step. claimsToHeaderExtract 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 you use in a later step.Create an HTTPRoute for the httpbin app on the same Gateway that the GlooTrafficPolicy targets.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-jwt-route namespace: httpbin spec: parentRefs: - name: http namespace: gloo-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.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 scope that you configured in the JWT filter, like the
emailscope. Typically, you add the scopes that you need in a field such asscope: openid email. - For steps to get a JWT, follow your provider’s documentation. For example, you might use the Keycloak guide along with a tool to help get the auth code that you exchange for the token, such as the OpenID Connect Debugger.
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)Example output:
echo $TOKEN eyJhbGc...JWT token output, such as by copying the token into the jwt.io tool:
{ "exp": 1752604325, "iat": 1752604265, "jti": "fec7f625-80a6-47c8-9330-bc2f6af88c39", "iss": "http://$KEYCLOAK_URL/realms/master", "aud": "account", "sub": "83c7e61b-xyz", "typ": "Bearer", "azp": "58e66a46-xyz", "sid": "4a6f48cf-xyz", "acr": "1", "realm_access": { "roles": [ "default-roles-master", "offline_access", "uma_authorization" ] }, "resource_access": { "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }, "scope": "email profile", "email_verified": false, "name": "Alice Doe", "preferred_username": "user1", "given_name": "Alice", "family_name": "Doe", "email": "user1@example.com" }Repeat the request to your httpbin app. This time, include the JWT that you just created as part of the
jwtheader.Example output: Verify that you get back a 200 success status code. Notice that the
emailscope from the claim is added to theX-Solo-Email-Claimheader that you included 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" ] }, ... }
Step 3: Add RBAC rules
Now that you extracted the email claim from the JWT, apply RBAC rules to restrict access.
Update the GlooTrafficPolicy with the following RBAC rules.
kubectl apply -f- <<EOF apiVersion: gloo.solo.io/v1alpha1 kind: GlooTrafficPolicy metadata: name: jwt-gw-policy namespace: gloo-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http glooJWT: 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 glooRBAC: policies: viewer: permissions: methods: - GET pathPrefix: /anything principals: - jwtPrincipal: claims: email: user1@example.com # matcher: ExactString # optional, can be ExactString, Boolean, ListContains # provider: keycloak # optional, specify if needed # nestedClaimDelimiter: "." # optional, for nested claims # disable: false # optional, to explicitly disable RBAC EOFReview the following table to understand this configuration.
Setting Description glooRBACConfigure the RBAC rules for the policy. viewerName the RBAC policy that you configure in subsequent fields. permissionsRestrict permissions of the RBAC role. In this example, the viewer role is restricted to use only the GETmethod along the/anythingendpoint.principalsConfigure the principals that the RBAC rules apply to. In this example, the email claim is used to restrict permissions for the example user1@example.comuser.Repeat the request to your httpbin app with the JWT. This time, the request is denied because you tried to access an endpoint that you are no longer authorized to (
/get).Example output:
HTTP/1.1 403 Forbidden RBAC: access deniedSend a
GETrequest to your httpbin app along the/anythingendpoint that you have access to. This time, the request succeeds.Example output:
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" ], "form": { "test: This is anything.": [ "" ] }, }, ... }
Cleanup
You can optionally remove the resources that you set up as part of this guide.
kubectl delete GlooTrafficPolicy -n gloo-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