Java client to refresh keycloak token
Asked Answered
P

1

10

Imagine,

Following are the 2 clients (2 micro-services) in keyclock.

  • rest-service-1
  • rest-service-2

Following is the role in rest-service-2

  • service-2-user

To do service to service call, ie: rest-service-1 calls rest-service-2

'rest-service-1' is configured with following values in Keycloak

Access Type: confidential
Service Account Enabled: Yes

Also, under 'Service Account Roles' for rest-service-1, following role is added/mapped

Role for client rest-service-2: service-2-user

After setting up the 2 clients and service account for calling client in keyclock. I created a Spring boot 2.0.3 project and used the following code to get the token.

@Bean
public AuthzClient authzClient(KeycloakSpringBootProperties kcProperties) {
  //org.keycloak.authorization.client.Configuration
  Configuration configuration =
      new Configuration(kcProperties.getAuthServerUrl(), 
                        kcProperties.getRealm(), 
                        kcProperties.getResource(),
                        kcProperties.getCredentials(), null);

  return AuthzClient.create(configuration);
}

Here is how I get access token

@Autowired
private AuthzClient authzClient;

public AccessTokenResponse token() {
  return authzClient.obtainAccessToken();
}

Following is the token received:

{
  "access_token": ${access-token},
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": ${refresh-token},
  "token_type": "bearer",
  "id_token": null,
  "not-before-policy": 0,
  "session_state": "6f284b2f-5bb6-4018-8acd-b83923ebb7d7",
  "scope": "profile email"
}

Note: I replaced tokens for making it short/brief.

QUESTION:

How do use the refresh token stated above and get a new Access Token. Does AuthzClient support that? If so, how do I do that?

Do I need to create a new Instance of TokenCallable and get the token? If so how to instanciate TokenCallable?

Is TokenCallable thread safe?

Premeditation answered 28/6, 2018 at 21:18 Comment(0)
A
6

You cannot do this explicitly by means of AuthzClient class. However, you can use some low-level API from org.keycloak.authorization.client.util package, i.e. the Http class. For example:

public AccessTokenResponse refreshToken(String refreshToken) {
    String url = kcProperties.getAuthServerUrl() + "/realms/" + kcProperties.getRealm() + "/protocol/openid-connect/token";
    String clientId = kcProperties.getResource();
    String secret = (String) kcProperties.getCredentials().get("secret");
    Http http = new Http(kcConfig, (params, headers) -> {});

    return http.<AccessTokenResponse>post(url)
            .authentication()
                .client()
            .form()
                .param("grant_type", "refresh_token")
                .param("refresh_token", refreshToken)
                .param("client_id", clientId)
                .param("client_secret", secret)
            .response()
                .json(AccessTokenResponse.class)
            .execute();
}

@Bean
public org.keycloak.authorization.client.Configuration kcConfig() {
    return new org.keycloak.authorization.client.Configuration(
            kcProperties.getAuthServerUrl(),
            kcProperties.getRealm(),
            kcProperties.getResource(),
            kcProperties.getCredentials(),
            null
    );
}

This solution is fully thread-safe (see CloseableHttpClient for the details).

Amygdaline answered 8/2, 2019 at 19:7 Comment(9)
Hi, this looks very helpfull. One question; by issuing this token refresh request, will the "original access_token" be replaced by the new one created here as a side effect?, or this code will only generate the new access_token (but the replacement of the "old access_token" will still need to be done manually as a 2nd step)? thanks!Substantialize
I tried this myself and I got the new token. But how do I make the applicaiton use the newly generated token? thcSubstantialize
@tony_008 I have updated the answer: you can inject kcConfig into your Component.Amygdaline
thx for taking the time @maslick. I'm able to generate the token. The problem I have is that I'm not able to override the access_token (and refresh_token) requestd by the spring security connector towards the keycloack auth server. I can generate the token but I can't tell my app to use the new token, the app keeps using the old one. Were you using Spring-boot adapter + Spring security right? Any feedback regarding how can I tell the app to start using that newly created token instead of the old one?Substantialize
I think you misunderstood my point. AuthzClient is basically a REST client for Keycloak. My answer explains how one can get a new token from the Keycloak server over REST using the refresh token.Amygdaline
Hi @Amygdaline thx, so just to be 100% sure, you mean the solution you posted concerns itself speciffically with getting a new token over REST using a refresh token, but is not related to how to use that new token within a spring security project right?Substantialize
@tony_008, If you're using keycloak-spring-boot-starter, then once you refresh the token with AuthzClient as I describe above, the Keycloak adapter will be aware of the token has been updated.Amygdaline
Ok I get it, that is exactly what I'd need. One last question though. Didn't you mention in your answer to the original question: "You cannot do this explicitly by means of AuthzClient lib". So your code doesn't seem to use AuthzClient. Is that correct or did I miss something? And thank you for the time you've taken, I appreciate it.Substantialize
You are right! AuthzClient class has high-level API, which you cannot use for getting the access token by providing a refresh token (there is no such method). However, the org.keycloak.authorization.client package has some low-level API represented in the Http class (which I used in the answer above). Normally you don't explicitly refresh the token on the backend, but you may have a use-case for that of course. Instead the token is usually refreshed on the frontend (e.g. javascript keycloak adapter). I have updated the answer (sorry for the confusion). HTHAmygdaline

© 2022 - 2024 — McMap. All rights reserved.