You can install an OpenID Connect (OIDC) identity provider (IdP) such as Keycloak in your cluster. Then, configure the Gloo Gateway external auth server with the OIDC details. You can adapt these steps for other IdPs.

Before you begin

  1. Follow the Get started guide to install Gloo Gateway, set up a gateway resource, and deploy the httpbin sample app.

  2. Get the external address of the gateway and save it in an environment variable.

Step 1: Install Keycloak

You might want to test how to restrict access to your applications to authenticated users, such as with external auth or JWT policies. You can install Keycloak in your cluster as an OpenID Connect (OIDC) provider.

The following steps install Keycloak in your cluster, and configure two user credentials as follows.

  • Username: user1, password: password, email: user1@example.com
  • Username: user2, password: password, email: user2@solo.io

Install and configure Keycloak:

  1. Create a namespace for your Keycloak deployment.

      kubectl create namespace keycloak
      
  2. Create the Keycloak deployment.

      kubectl -n keycloak apply -f https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/policy-demo/oidc/keycloak.yaml
      
  3. Wait for the Keycloak rollout to finish.

      kubectl -n keycloak rollout status deploy/keycloak
      
  4. Set the Keycloak endpoint details from the load balancer service.

      export ENDPOINT_KEYCLOAK=$(kubectl -n keycloak get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].*}'):8080
    export HOST_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f1)
    export PORT_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f2)
    export KEYCLOAK_URL=http://${ENDPOINT_KEYCLOAK}
    echo $KEYCLOAK_URL
      
  5. Set the Keycloak admin token. If you see a parsing error, try running the curl command by itself. You might notice that your internet provider or network rules are blocking the requests. If so, you can update your security settings or change the network so that the request can be processed.

      export KEYCLOAK_TOKEN=$(curl -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token)
    echo $KEYCLOAK_TOKEN
      
  6. Use the admin token to configure Keycloak with the two users for testing purposes. If you get a 401 Unauthorized error, run the previous command and try again.

      # Create initial token to register the client
    read -r client token <<<$(curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"expiration": 0, "count": 1}' $KEYCLOAK_URL/admin/realms/master/clients-initial-access | jq -r '[.id, .token] | @tsv')
    export KEYCLOAK_CLIENT=${client}
    echo $KEYCLOAK_CLIENT
    
    # Register the client
    read -r id secret <<<$(curl -k -X POST -d "{ \"clientId\": \"${KEYCLOAK_CLIENT}\" }" -H "Content-Type:application/json" -H "Authorization: bearer ${token}" ${KEYCLOAK_URL}/realms/master/clients-registrations/default| jq -r '[.id, .secret] | @tsv')
    export KEYCLOAK_SECRET=${secret}
    echo $KEYCLOAK_SECRET
    
    # Add allowed redirect URIs
    curl -k -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X PUT -H "Content-Type: application/json" -d '{"serviceAccountsEnabled": true, "directAccessGrantsEnabled": true, "authorizationServicesEnabled": true, "redirectUris": ["*"]}' $KEYCLOAK_URL/admin/realms/master/clients/${id}
    
    # Add the group attribute in the JWT token returned by Keycloak
    curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"name": "group", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "config": {"claim.name": "group", "jsonType.label": "String", "user.attribute": "group", "id.token.claim": "true", "access.token.claim": "true"}}' $KEYCLOAK_URL/admin/realms/master/clients/${id}/protocol-mappers/models
    
    # Create first user
    curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user1", "email": "user1@example.com", "firstName": "Alice", "lastName": "Doe", "enabled": true, "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
    
    # Create second user
    curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user2", "email": "user2@solo.io", "firstName": "Bob", "lastName": "Doe", "enabled": true, "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
      
  7. Open the Keycloak frontend.

      open $KEYCLOAK_URL
      
  8. Log in to the admin console, and enter admin as the username and admin as your password.

  9. In the Keycloak admin console, go to Users, and verify that the users that created earlier are displayed. You might need to click on View all users to see them.

  10. In the Keycloak admin console, go to Clients, and verify that you can see a client ID that equals the output of $KEYCLOAK_CLIENT.

Step 2: Configure Keycloak

Now that you installed Keycloak and set up clients to use, configure Keycloak for certain OAuth use cases. The following instructions assume that you are still logged into the Administration Console from the previous step.

You might integrate OIDC with your apps. In such cases, you might need particular details from the OIDC provider to fully set up your apps. To use Keycloak for OAuth protection of these apps, you need certain settings and information from Keycloak.

  1. For authorization code OAuth: Confirm that you have the following environmental variables set. If not, refer to the Before you begin and Step 1: Install Keycloak sections.

      echo $CLUSTER_NAME
    echo $INGRESS_GW_ADDRESS
    echo $KEYCLOAK_CLIENT
    echo $KEYCLOAK_URL
      
  2. If you plan to use access token validation: Get a JWKS URI to use for an inline OAuth policy.

    1. From the sidebar menu options, click Realm Settings.
    2. From the General tab, scroll down to the Endpoints section and open the OpenID Endpoint Configuration link. In a new tab, your browser opens to a URL similar to http://$KEYCLOAK_URL:8080/realms/master/.well-known/openid-configuration.
    3. In the OpenID configuration, search for the token_endpoint field. Save the value as an environment variable, such as the following example.
        export TOKEN_ENDPOINT=$KEYCLOAK_URL/realms/master/protocol/openid-connect/token
        
    4. In the OpenID configuration, search for the jwks_uri field, and copy this value, similar to http://$KEYCLOAK_URL:8080/realms/master/protocol/openid-connect/certs.
        export KEYCLOAK_JWKS_URI=<jwks_uri>
        
    5. In a new browser tab, open the jwks_uri that you previously copied.
    6. From the opened jwks_uri, copy and save the entire value of the keys as an environment variable.
        export KEYCLOAK_CERT_KEYS={"keys":[{"kid":"_YYA...","kty":"RSA","alg":"RSA-OAEP","use":"enc","n":"r4AXlC9sR..."}]}
        
  3. If you plan to use the developer portal: Configure your Keycloak client with the appropriate settings for the developer portal frontend authentication.

    1. From the Keycloak sidebar menu options, click Clients.
    2. Click the Client ID that matches the $KEYCLOAK_CLIENT that you previously set.
    3. From the client Settings tab, find the Access settings and update the following fields.
      • Valid redirect URIs field with * wildcard or the domain that you want to use for redirects.
      • Valid post logout redirect URIs field with * wildcard or the domain that you want to use for logout redirects.
      • Web origins field with * wildcard to allow all origins.
    4. Scroll down to the Capability config section and update the following sections:
      • Toggle the Client authentication setting to Off to enable public access.
      • In the Authentication flow section, enable the Standard flow and Direct access grants options.
    5. At the bottom of the form, click Save. The Client successfully updated message pops up to confirm your settings are saved.
    6. From the client Advanced tab, find the Advanced settings. Set the Proof Key for Code Exchange Code Challenge Method dropdown to S256. For more information on PKCE, refer to RFC 7636.
    7. Scroll down and click Save. The Client successfully updated message pops up to confirm your settings are saved.
  4. If you plan to use the developer portal: Get the OIDC endpoint details.

    1. From the Keycloak sidebar menu options, click Realm Settings.
    2. From the General tab, scroll down to the Endpoints section and open the OpenID Endpoint Configuration link. In a new tab, your browser opens to a URL similar to http://$KEYCLOAK_URL:8080/realms/master/.well-known/openid-configuration
    3. In the OpenID configuration, search for the token_endpoint field. Save the value as an environment variable, such as the following example.
        export TOKEN_ENDPOINT=$KEYCLOAK_URL/realms/master/protocol/openid-connect/token
        
    4. In the OpenID configuration, search for the authorization_endpoint field. Save the value as an environment variable, such as the following example.
        export AUTH_ENDPOINT=$KEYCLOAK_URL/realms/master/protocol/openid-connect/auth
        
    5. In the OpenID configuration, search for the end_session_endpoint field. Save the value as an environment variable, such as the following example.
        export LOGOUT_ENDPOINT=$KEYCLOAK_URL/realms/master/protocol/openid-connect/logout
        

Step 3: Add custom claims

Optionally, you can add custom claims to the ID tokens that Keycloak generates for a user. For example, you might want to create policies that grant access based on custom claims. If you use Gloo Portal, you can use custom claims to authorize users to ApiProducts by creating a PortalGroup.

Gloo Portal includes a sample React frontend app for a developer portal. To protect the frontend app with an OAuth login, the OIDC provider must return access and ID tokens with the following claims. Keep in mind that your OIDC provider might also require the users to have values for these claims in order to return the claim (such as a first and last name, not just a username, configured in the user profile).

Claims required to log in:

  • email
  • name
  • preferred_username
  • sub

Claims required for admin tasks such as managing subscriptions:

  • group: admin, note that this claim cannot be returned as an array.

Some OIDC providers do not return all of these claims by default. In the AuthConfig, you might be able to request additional scopes, such as openid, profile, and email to get back tokens with these claims. Otherwise, create custom claims and claim mapping for these tokens in your OIDC provider.

In Keycloak, the name and group:admin claims are not returned by default. The following steps set up these custom claims. The steps also create a group called Users and assign user1 to this group, but not user2. The group membership is mapped to an X-groups claim in the ID token. The X-groups custom claim is later used as an example for PortalGroups. For more information about claim mapping, see the Keycloak docs.

  1. If you are not already, log in to the Keycloak console as the admin user with the admin password.

      open $KEYCLOAK_URL
      
  2. Configure the required group claim for admins in the client.

    1. From the menu, click Manage > Clients.

    2. From the list of clients, click the client ID that you use, such as 1234567-1aa1-1a11-2bb2-cc1234567.

    3. Click the Client scopes tab, then click the assigned client scope with your client ID, such as 1234567-1aa1-1a11-2bb2-cc1234567-dedicated.

    4. Click Add mapper > By configuration.

    5. Click User Attribute.

    6. In the Name field, enter a name for the mapper, such as group-mapper.

    7. In the Token Claim Name field, enter the name for the claim in the ID token, group.

    8. Confirm that the Add to ID token setting is toggled on.

    9. Confirm that the Multivalued setting is toggled off. You must set this value so that the claim has only one value and is not an array. The Gloo Portal sample React frontend app does not accept an array for the group: admin membership.

    10. Click Save.

  3. Repeat the previous step to add the required name claim, which is not included in Keycloak tokens by default.

    1. From the Client details > Dedicated scopes > Mappers tab, click Add mapper > By configuration.

    2. Click User Property.

    3. In the Name field, enter a name for the mapper, such as name-mapper.

    4. In the Token Claim Name field, enter the name for the claim in the ID token, name.

    5. Confirm that the Add to ID token setting is toggled on.

    6. Scroll down and click Save.

  4. Configure the custom X-groups claim for the client.

    1. From the Client details > Dedicated scopes > Mappers tab, click Add mapper > By configuration.

    2. Click Group Membership.

    3. In the Name field, enter a name for the mapper, such as group-mapper.

    4. In the Token Claim Name field, enter the name for the claim in the ID token, such as X-groups.

    5. Confirm that the Add to ID token setting is toggled on.

    6. Scroll down and click Save.

  5. Create the admin and Users group.

    1. From the menu, click Manage > Groups.

    2. Click Create group.

    3. Enter the name Users, then click Create.

    4. Click Create group again, enter admin, then click Create.

    5. In the list of groups, click Users.

    6. Click the Members tab, then click Add member.

    7. Select user1, then click Add.

    8. In the list of groups, click admin.

    9. Click the Members tab, then click Add member.

    10. Select user1 and admin, then click Add.

    11. Click the Attributes tab, then click Add attribute.

    12. Enter group for the key and name for the value, and then click Save.

  6. Send a request to the Keycloak token endpoint to get back an ID token.

      curl -X POST \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "client_id=$KEYCLOAK_CLIENT" \
      -d "client_secret=$KEYCLOAK_SECRET" \
      -d "grant_type=password" \
      -d "username=user1" \
      -d "password=password" \
      -d "scope=openid" \
      $KEYCLOAK_URL/realms/master/protocol/openid-connect/token   
      

    Example response:

      {
      "access_token": "eyJhb...",
      "expires_in": 60,
      "refresh_expires_in": 1800,
      "refresh_token": "eyJhbG...",
      "token_type": "Bearer",
      "id_token": "eyJhbG...",
      "not-before-policy": 0,
      "session_state": "8ed...",
      "scope": "openid profile email"
    }
      
  7. To verify that the ID token includes the claims, decode the id_token claims by using a tool such as jwt.io or openssl.

      echo '<id_token>' | awk -F. '{print $2}' | base64 -d
      

    Example output: Notice that the required email, name, preferred_username, sub, and groups claims are returned, along with the X-groups custom claim.

      {
      "exp": 1732140757,
      "iat": 1732140697,
      "jti": "091d4648-...",
      "iss": "$KEYCLOAK_URL/realms/master",
      "aud": "72182354-...",
      "sub": "d1150127-...",
      "typ": "ID",
      "azp": "72182354-...",
      "sid": "8ed0cbaa-...",
      "at_hash": "Iww...",
      "acr": "1",
      "email_verified": false,
      "name": "Alice Doe",
      "preferred_username": "user1",
      "email": "user1@example.com",
      "group": "admin",
      "X-groups": [
        "/Users",
        "/admin"
      ]
    }
      

Step 4: Create an OAuth policy

Create the resources needed to enforce an OAuth policy that uses Keycloak as the OIDC provider.

  1. Create an AuthConfig resource with your external authentication rules. The following example sets up access token validation. To validate access tokens, you configure the Gloo Gateway external auth server to use the Keycloak JWKS server at the address that you previously got when you configured Keycloak. For more options, see the OAuth2 API docs.

      kubectl apply -f - <<EOF
    apiVersion: enterprise.gloo.solo.io/v1
    kind: AuthConfig
    metadata:
      name: oauth-keycloak
      namespace: httpbin
    spec:
      configs:
        - oauth2:
            accessTokenValidation:
              jwt:
                remoteJwks:
                  url: "${KEYCLOAK_JWKS_URI}"
    EOF
      
  2. Create a RouteOption resource that refers to the AuthConfig resource that you just created.

      kubectl apply -f- <<EOF
    apiVersion: gateway.solo.io/v1
    kind: RouteOption
    metadata:
      name: httpbin-oauth
      namespace: httpbin
    spec:
      options:
        extauth:
          configRef:
            name: oauth-keycloak
            namespace: httpbin
    EOF
      
  3. Create an HTTPRoute resource for the httpbin app that requires authentication with Keycloak for requests along the /status/200 path. If the request does not include a valid token, the request is denied with a 403 Forbidden HTTP status code.

      kubectl apply -f- <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin-oauth-keycloak
      namespace: httpbin
    spec:
      hostnames:
      - "www.httpbin.org"
      parentRefs:
      - name: http
        namespace: gloo-system
      rules:
        - matches:
          - path:
              type: Exact
              value: /status/200
          filters:
            - type: ExtensionRef
              extensionRef:
                group: gateway.solo.io
                kind: RouteOption
                name: httpbin-oauth
          backendRefs:
            - name: httpbin
              port: 8000
    EOF
      

Step 5: Verify the OAuth policy

  1. Verify that your resources are in an Accepted state. If not, review the messages. Common issues include referencing the wrong RouteOption resource in the extensionRef filter, creating multiple RouteOption resources and attaching them via the targetRefs option, or missing ReferenceGrants for resources that are in different namespaces.

      kubectl describe AuthConfig -n httpbin oauth-keycloak
    kubectl describe RouteOption -n httpbin httpbin-oauth
    kubectl describe HttpRoute -n httpbin httpbin-oauth-keycloak
      
  2. Send a request to the protected httpbin route. Verify that you get back a 403 error status code.

      curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.httpbin.org:8080"
      

    Example output:

      HTTP/1.1 403 Forbidden
      
  3. Generate a token for user1 in your Keycloak OIDC provider. If you get a 404 response, verify that the Keycloak URL for the OIDC token is correct. Common errors include using a different realm.

      export USER1_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)
      
  4. Repeat the request. This time, include the token that you just created as part of the authorization header. Verify that you get back a 200 success status code.

      curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.httpbin.org:8080" -H "Authorization: Bearer $USER1_TOKEN"
      

    Example output:

      HTTP/1.1 200 OK
      

Cleanup

You can optionally remove the resources that you set up as part of this guide.
  1. Remove Keycloak from your cluster.

      kubectl delete namespace keycloak
      
  2. Delete the resources that you created for the external auth policy.

      kubectl delete AuthConfig -n httpbin oauth-keycloak
    kubectl delete RouteOption -n httpbin httpbin-oauth
    kubectl delete HttpRoute -n httpbin httpbin-oauth-keycloak