User credentials OAuth with Keycloak
Follow along with an OIDC OAuth example that uses Keycloak as an identity provider.
If you import or export resources across workspaces, your policies might not apply. For more information, see Import and export policies.
Before you begin
Follow the getting started instructions to:
-
Configure an HTTP listener on your gateway and set up basic routing for the sample apps.
-
Make sure that the external auth service is installed and running. If not, install the external auth service in your single or multicluster environment.
kubectl get pods -A -l app=ext-auth-service
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:
-
Create a namespace for your Keycloak deployment.
kubectl create namespace keycloak
-
Create the Keycloak deployment.
kubectl -n keycloak apply -f https://raw.githubusercontent.com/solo-io/workshops/master/gloo-mesh-2-3/all/data/steps/deploy-keycloak/keycloak.yaml
-
Wait for the Keycloak rollout to finish.
kubectl -n keycloak rollout status deploy/keycloak
-
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
-
Set the Keycloak admin token. If you see a parsing error, try running the
curl
command by itself. You might notice that your network is blocking the requests, which might require updating the security settings 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
-
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", "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", "enabled": true, "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
-
Open the Keycloak frontend.
open $KEYCLOAK_URL
-
Log in to the admin console, and enter
admin
as the username andadmin
as your password. -
In the Keycloak admin console, go to Manage > Users, and verify that the users that created earlier are displayed. You might need to click on View all users to see them.
-
In the Keycloak admin console, go to Configure > Clients, and verify that you can see a client that equals the output of $KEYCLOAK_CLIENT.
Configure an OAuth policy
Create the external auth policy that uses Keycloak as an identity provider.
- Create a Kubernetes secret with the Keycloak OIDC secret.
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: oauth namespace: httpbin type: extauth.solo.io/oauth data: client-secret: $(echo -n ${KEYCLOAK_SECRET} | base64) EOF
- Create an external auth server to use for your policy.
kubectl apply -f - <<EOF apiVersion: admin.gloo.solo.io/v2 kind: ExtAuthServer metadata: name: ext-auth-server namespace: httpbin spec: destinationServer: ref: cluster: $CLUSTER_NAME name: ext-auth-service namespace: gloo-mesh-addons port: name: grpc EOF
- Create a route table for the httpbin app and external auth policy. Note that the route table selects the virtual gateway that you created before you began, and routes to the httpbin service.
kubectl apply -f - <<EOF apiVersion: networking.gloo.solo.io/v2 kind: RouteTable metadata: name: httpbin namespace: httpbin labels: expose: "true" spec: hosts: - '*' virtualGateways: - name: istio-ingressgateway namespace: bookinfo cluster: $CLUSTER_NAME workloadSelectors: [] http: - name: httpbin labels: oauth: "true" matchers: - uri: exact: /get - uri: prefix: /callback forwardTo: destinations: - kind: SERVICE ref: name: httpbin namespace: httpbin cluster: $CLUSTER_NAME port: number: 8000 EOF
- Create an external auth policy that uses Keycloak for OIDC.
kubectl apply -f - <<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: ExtAuthPolicy metadata: name: httpbin namespace: httpbin spec: applyToRoutes: - route: labels: oauth: "true" config: server: name: ext-auth-server namespace: httpbin cluster: $CLUSTER_NAME glooAuth: configs: - oauth2: oidcAuthorizationCode: appUrl: "https://${INGRESS_GW_IP}:443" callbackPath: /callback clientId: ${KEYCLOAK_CLIENT} clientSecretRef: name: oauth namespace: httpbin issuerUrl: "${KEYCLOAK_URL}/realms/master/" scopes: - email session: failOnFetchFailure: true redis: cookieName: keycloak-session options: host: redis:6379 headers: idTokenHeader: jwt EOF
Review the following table to understand this configuration. For more information, see the API reference.
Setting | Description |
---|---|
applyToRoutes |
Use labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you previously applied. If omitted, the policy applies to all routes in the workspace. |
server |
The external auth server to use for the policy. |
oauth2 |
Configure the OAuth 2.0 protocol details to use to authenticate requests. The example uses Keycloak as the external identity provider. |
appUrl |
The public URL of the app that you want to set up external auth for. This setting is used in combination with the callbackPath attribute. |
callbackPath |
The callback path, relative to the appUrl setting. After a user authenticates, the identity provider redirects the user to this callback URL. Gloo Gateway intercepts requests with this path, exchanges the authorization code received from the IdP for an ID token, places the ID token in a cookie on the request, and forwards the request to its original destination.
The callback path must have a matching route in the route table that is associated with the external auth policy. For example, you could simply have a
/ path-prefix route which would match any callback path. The important part of this callback “catch all” route is that the request goes through the routing filters including external auth. |
clientId |
The client ID token that you got when you registered your app with the identity provider. In this example, you set the client ID before you began in the demo setup. |
clientSecretRef |
The Kubernetes secret that has the client secret that you got when you registered your app with the identity provider. The secret must exist on the same cluster as the ExtAuthServer resource that this policy refers to. In this example, you created the secret in an earlier step. |
issuerUrl |
The URL of the OpenID Connect identity provider. Gloo Gateway automatically discovers OIDC configuration by querying the .well-known/openid-configuration endpoint on the issuer_url . In this example, Gloo Gateway expects to find OIDC discovery information at "${KEYCLOAK_URL}/realms/master/" . |
scopes |
Scopes to request in addition to the openid scope, such as email in this example. |
session |
Details on how to store the user session details. In this example, the cookie is stored as by the name keycloak-session in a Redis instance. |
idTokenHeader |
Forward the ID token to the destination after successful authentication. In this example, the ID token is sent as a JWT. |
Verify the OAuth policy
- Copy the output of the following command and open the path in your web browser to access the
httpbin
app. You are redirected to the authentication page from the Keycloak identity provider.echo "https://${INGRESS_GW_IP}:443/get"
- Enter the user credentials from Keycloak, such as the following values from the demo setup.
- Username:
user1
- Password:
password
- Username:
You are authenticated and returned back to the httpbin
home page.
Cleanup
You can clean up the resources that you created in this guide by running the following commands.
- Remove Keycloak from your cluster.
kubectl delete namespace keycloak
- Delete the resources that you created for the external auth policy.
kubectl delete secret -n httpbin oauth kubectl delete routetable -n httpbin httpbin kubectl delete extauthserver -n httpbin ext-auth-server kubectl delete extauthpolicy -n httpbin httpbin