Keycloak impersonate with token-exchange

Viewed 1878

Technical Details:

Keycloak Version: 12.0.2
Java Version: 1.8
Java Admin Client: 12.0.2
Keycloak Spring boot starter

Description

I have a Spring Boot application where internal staff can create and modify Keycloak users, using its official java dependency. This application should also contain an impersonation function.

I tried impersonating with the Java Admin Client, the impersonate function returns a map, which contains the Boolean “sameRealm” and the redirect URL to the account page of Keycloak. With these 2 values I can’t access the session of the impersonated User nor can I really use these attributes for something else.

Afterwards I tried the impersonate function of the Keycloak Rest API, the API returned some Cookies in the header unfortunately I can’t seem to figure out how to use these cookies, but I tried creating these cookies and set them but unfortunately it didn’t work.

Lastly, I tried a token exchange to receive a valid access token and fortunately the function works.

token-exchange function

       Keycloak keycloakService = KeycloakBuilder.builder()
        .serverUrl(serverUrl)
        .realm(realm)
        .clientId(clientId)
        .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
        .clientSecret(clientSecret)
        .build();

       BasicCookieStore cookieStore = new BasicCookieStore();
       CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();

       HttpUriRequest reqBuild = RequestBuilder.post()
         .setUri(serverUrl + "/realms/intern/protocol/openid-connect/token")
         .addHeader("Content-Type", "application/x-www-form-urlencoded")
         .addParameter("client_id", "example")
         .addParameter("client_secret", "example") //
         .addParameter("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
         .addParameter("subject_token", keycloakService.tokenManager().getAccessTokenString())
         .addParameter("requested_token_type", "urn:ietf:params:oauth:token-type:access_token")
         .addParameter("requested_subject", userId)
         .addParameter("audience", "target-client")
         .build();

       HttpResponse res = httpClient.execute(reqBuild);
       String resBody = EntityUtils.toString(res.getEntity());
       System.out.println(resBody);

With the above code I receive a valid access-token from Keycloak. But unfortunately, I don’t know how I can use this token to complete the Impersonation process.

The setup is as following: id.example.com provides the Spring Boot Application, sso.id.example.com runs KeyCloak instance. For my understanding, I should create a cookie for sso.id.example.com

If someone has another solution on how I can Impersonate a user and then acquire the associated session. I would appreciate the help.

1 Answers

Your resBody.content is of type AccessTokenResponse (assuming you got a 200 response) which has an token property. The token should be the auth_token for the user you want to impersonate. As a sanity check you can use https://jwt.io/ to debug the jwt token returned to make sure it is for the correct user.

You can use this token for subsequent calls as the person you are impersonating.

I don't believe you need to set a cookie store, i am certain you can remove this and it should continue to work.

import org.keycloak.admin.client.Keycloak

...
val keycloakService = KeycloakBuilder.builder()
        .serverUrl(serverUrl)
        .realm(realm)
        .clientId(clientId)
        .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
        .clientSecret(clientSecret)
        .build();

val httpClient = HttpClientBuilder.create().build();

val reqBuild = RequestBuilder.post()
         .setUri(serverUrl + "/realms/intern/protocol/openid-connect/token")
         .addHeader("Content-Type", "application/x-www-form-urlencoded")
         .addParameter("client_id", "example")
         .addParameter("client_secret", "example") //
         .addParameter("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
         .addParameter("subject_token", keycloakService.tokenManager().getAccessTokenString())
         .addParameter("requested_token_type", "urn:ietf:params:oauth:token-type:access_token")
         .addParameter("requested_subject", userId)
         .addParameter("audience", "target-client")
         .build();

val response = httpClient.execute(reqBuild)
val entity = if (response.statusLine.statusCode == 200) {
    val mapper = ObjectMapper().registerModule(KotlinModule())
    mapper.readValue(response.entity.content, AccessTokenResponse::class.java)
} else {
  // handle error
}

// this should log you in as the impersonated user
val impersonated = Keycloak.getInstance(serverUrl, realm, clientId, entity.token).realm(realm)


Related