Get Keycloak Access Token Without Password

Overview

Recently I had this use case: I need to integrate Android login with my web app which is using keycloak as IDP. Once the user logs in successfully, the Android app will send an ID token to verify to the backend. Since the user doesn’t have a password (because they haven’t registered for example), it’s not possible to get the token.

STOP!

Before you continue, if you know a better approach, please let me know or share in the comment. This method is risky as far as I know but the deadline can’t wait and I’ve done my search but found no better solution.

Proceed with your understanding of the risk involved.

Okay, let’s continue.

The Flow

The flow is quite simple:

  1. The user logs in in Android, we get the ID Token returned by Google
  2. We get this ID Token, and post to the backend to verify the validity
  3. If the token is valid, we create the user in Keycloak. If the user exists (by email) then creating a user is not needed
  4. We get the keycloak ID of the user (either by creating a new or from our database if the user exists)
  5. We use the keycloak admin client to impersonate the newly created user to get the refresh and access token to return to the Android app
  6. Now the Android app has the token, we can use that call to our backend

I will not do the Android app side in this tutorial, Google already has a good tutorial.

Setup Keycloak

In order to enable this impersonation, you need first to start keycloak with the following options:

-Dkeycloak.profile=preview -Dkeycloak.profile.feature.token_exchange=enabled

If you use docker-compose, so you can pass this option as an env variable:

JAVA_OPTS_APPEND="-Dkeycloak.profile=preview -Dkeycloak.profile.feature.token_exchange=enabled"

Create (or recreate) your keycloak instance before continuing.

Configure your keycloak client

If you have your backend, chances are you already configured a client with client ID and secret. Make sure you enable a service account for this client.

Now, this client needs to have the impersonation right:

configure impersonation for the client

Now the configuration with keycloak is done. Back to your Java code.

Getting User Access Token Without password

All you need to do to impersonate the user and get her access token is this:

    public String impersonate(String userKeycloakId) throws IOException {
        var httpClient = HttpClientBuilder.create().build();
        var url = serverUrl + "/realms/" + realm + "/protocol/openid-connect/token";
        log.info("Impersonate url: {}", url);
        var reqBuild = RequestBuilder.post()
                .setUri(url)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addParameter("client_id", clientId)
                .addParameter("client_secret", clientSecret) //
                .addParameter("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
                .addParameter("subject_token", getKeycloak().tokenManager().getAccessTokenString())
                .addParameter("requested_subject", userKeycloakId)
                .build();
        var response = httpClient.execute(reqBuild);
        if (response.getStatusLine().getStatusCode() == 200) {
            String text = new BufferedReader(
                    new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))
                    .lines()
                    .collect(Collectors.joining("\n"));
            var token = objectMapper.readValue(text, AccessTokenResponse.class);
            log.info("Impersonate token: {}", token);
        } else {
            // handle error
            log.info("Impersonate error: {}", response.getStatusLine().getStatusCode());
        }
        return null;
    }

    private Keycloak getKeycloak() {
        return KeycloakBuilder.builder()
                .serverUrl(serverUrl)
                .realm(realm)
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .clientId(clientId)
                .clientSecret(clientSecret).build();
    }

Obviously, you need to configure your server URL, realm, client id and secret based on your app.

With this, I was able to impersonate the user.

Conclusion

In this post, I’ve shown you how to impersonate a user and get her token without the user’s password. As I mentioned in the first part of the post, this is risky. Use it at your own risk. Anyways, production can’t wait!

1 thought on “Get Keycloak Access Token Without Password”

Leave a Comment