0

I have Jhipster running with Oauth2 + Keycloak.

I have a use case where I need to update user last and first name from the Jhipster React UI, so I used the Keycloak admin client via a service account to update user attributes in Keycloak.

The problem is that the information needs to be re-fetched to the OIDC token to let the user see the changes immediately. (similar issue here: https://github.com/jhipster/generator-jhipster/issues/7398 )

Is there any suggestion how to setup Spring Security to be able to re-fetch/refresh my token with the latest information form Keycloak, or any explicit call to do it?

Thanks for the answears!

laci1210
  • 99
  • 1
  • 3
  • Hi, did you find a solution for this? I'm in the same situation, user updated correctly via admin client, but Spring application still relying on old token data. I was thinking about an hacky solution: invalidate the access token, and let the client application send another request, after the first for user update. The invalid access token then would make the app client refresh the access token, and sync the data from the updated token. I'm looking for a better solution, this doesn't convince me much. – funder7 Jan 11 '21 at 15:56

1 Answers1

1

So from workflow point of view I was able to solve the problem by:

  1. Changing the data via Keycloak admin client
  2. Change the data in the Spring Security Context

I had a wrong assumption about spring security that it validates the token data against the actual token stored in the context on every call. It turned out the spring security has no problem by changing the data in the context, so on the next login I can get a valid token what is inline with the actual data.

This is the code I was able to change the context with:

    public void updateUserRole(AbstractAuthenticationToken abstractAuthenticationToken)
{
    SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneByLogin)
        .ifPresent(user -> {
            Set<Authority> authorities = user.getAuthorities();
            Authority authority = new Authority();
            authority.setName(AuthoritiesConstants.USER);
            authorities.remove(AuthoritiesConstants.INVITED);
            authorities.add(authority);
            user.setAuthorities(authorities);
            this.clearUserCaches(user);
            log.debug("Changed Information for User: {}", user);
        });
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    List<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.USER));

    Map<String, Object> claims = ((OidcIdToken)((DefaultOidcUser)((OAuth2AuthenticationToken)abstractAuthenticationToken).getPrincipal()).getIdToken()).getClaims();
    String userNameKey = ((OAuth2AuthenticationToken)authentication).getAuthorizedClientRegistrationId();
    String tokenValue = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getTokenValue();
    Instant issuedAt = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getIssuedAt();
    Instant expiresAt = ((OidcIdToken) ((DefaultOidcUser) ((OAuth2AuthenticationToken) abstractAuthenticationToken).getPrincipal()).getIdToken()).getExpiresAt();
    OidcIdToken oidcIdToken = new OidcIdToken(tokenValue, issuedAt, expiresAt, claims);
    DefaultOidcUser user = new DefaultOidcUser(authorities, oidcIdToken, "name");
    OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(user, authorities, userNameKey);


    SecurityContextHolder.getContext().setAuthentication(oAuth2AuthenticationToken);
}
laci1210
  • 99
  • 1
  • 3