Use Google Workload Identity to authenticate with Keycloak?

We are running GKE with workloads using workload identities. Would it be possible to use these ID tokens to authenticate as Keycloak users?

Is this the documentation under Using token exchange - Keycloak?

Or differently, can we use the JWTs of the Kubernetes service accounts to authenticate with Keycloak?

Regarding the token exchange, you might find other use cases discussed in this discussion.

As for the other point, Keycloak supports client authentication using signed JWTs, with the client’s public key obtained externally. Perhaps you could play with that.

You could do this with a custom authenticator.

In the AuthenticatorFactory you would configure the JWKS endpoint that is used to verify the digital signature of the JWT. In your authenticator you would validate the JWT using the keys from the keys endpoint. I have not found a a way to cache the .JWKS data based on the given call chain and instantiation patterns.

  1. validate the JWT
  2. read the username/email from the claims
  3. call getuser to make sure the user exists
  4. attach the user
UserModel user = context.getSession().users().getUserByUsername(context.getRealm(), <usernamefromtheJWT>);
context.setUser(user);
context.success();

Actually, I got it working. Working on a write-up right now. No custom authenticator needed.

1. Keycloak settings

  1. Start a local Keycloak instance:
docker run --name keycloak \
-p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.0.7 \
start-dev \
--features=token-exchange,admin-fine-grained-authz
  1. Open the Keycloak administration interface at http://localhost:8080 and login.
  2. Create a new realm exchange.
  3. Go to Identity providers and add the Google default identity provider.
  4. Obtain Client ID and Client Secret by following the steps documented here: Using OAuth 2.0 to Access Google APIs | Authorization | Google for Developers. You should be able to set this up in your playground project, for example. As application type, select Desktop.
  5. Go to clients and create a new client external-to-internal.
  6. Use default settings, but make sure to enable Client authentication.
  7. Go back to Clients and select the realm-management client.
  8. Navigate to the Authorization tab, then the Policies sub-tab and select Create client policy.
  9. As policy type, select Client.
  10. As name, enter external-to-internal.
  11. In clients, select external-to-internal.
  12. Now navigate to Identity providers again and select the google provider.
  13. Go to the Permissions tab and enable them.
  14. Next, click the token-exchange scope name under Permission list.
  15. At last, select the external-to-internal policy and save the changes.
  16. The Keycloak setup is now done.

2. Fetch workload identity token

  1. Set up workload identity for any pod in the cluster. This example is for GCP.
  2. Exec into the pod. Pod requires curl to be installed, though.
  3. Fetch a workload identity access token:
curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"

4.WARNING: This will also work if the pod doesn’t have a workload identity, but will return an invalid access_token.
5. You should now have a valid access_token for your workload.

3. Exchange the external token for an internal token

  1. First, get the client secret for the external-to-internal client created in step 1.5.
  2. Then, make a request to Keycloak to do the token exchange:
export REALM=exchange
export CLIENT_ID=external-to-internal
export CLIENT_SECRET=
export IDENTITY_PROVIDER_ALIAS=google
export WORKLOAD_ACCESS_TOKEN=
curl --location "http://localhost:8080/realms/$REALM/protocol/openid-connect/token" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "client_secret=$CLIENT_SECRET" \
--data-urlencode "subject_token=$WORKLOAD_ACCESS_TOKEN" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
--data-urlencode "subject_issuer=$IDENTITY_PROVIDER_ALIAS" \
--data-urlencode "audience=$CLIENT_ID"
  1. Verify the received token.

Readings

Token-Exchange Use-cases · keycloak keycloak · Discussion #26502