simple-ui: improve the oidc id token refresh (#2534)
* simple-ui: improve the oidc id token refresh * make sure we get the latest context after refresh * further simplification of the method * remove principal oidc check * remove relying on previous oidcuser, as the infinite loop was fixed on spring security * simplify the granted authorities fetch * rollback some changes to simplify review * lint
This commit is contained in:
@@ -58,8 +58,8 @@ public class SimpleUIApp implements AppShellConfigurator {
|
|||||||
|
|
||||||
private static final Function<OAuth2TokenManager, RequestInterceptor> AUTHORIZATION = oAuth2TokenManager -> requestTemplate -> {
|
private static final Function<OAuth2TokenManager, RequestInterceptor> AUTHORIZATION = oAuth2TokenManager -> requestTemplate -> {
|
||||||
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (oAuth2TokenManager != null && authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken) {
|
if (authentication instanceof OAuth2AuthenticationToken authenticationToken) {
|
||||||
String bearerToken = oAuth2TokenManager.getToken(oAuth2AuthenticationToken);
|
String bearerToken = oAuth2TokenManager.getToken(authenticationToken);
|
||||||
requestTemplate.header("Authorization", "Bearer " + bearerToken);
|
requestTemplate.header("Authorization", "Bearer " + bearerToken);
|
||||||
} else {
|
} else {
|
||||||
requestTemplate.header(
|
requestTemplate.header(
|
||||||
@@ -108,16 +108,14 @@ public class SimpleUIApp implements AppShellConfigurator {
|
|||||||
@Bean
|
@Bean
|
||||||
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(final HawkbitMgmtClient hawkbitClient) {
|
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(final HawkbitMgmtClient hawkbitClient) {
|
||||||
final OidcUserService delegate = new OidcUserService();
|
final OidcUserService delegate = new OidcUserService();
|
||||||
return userRequest -> {
|
return (userRequest) -> {
|
||||||
OidcUser oidcUser = delegate.loadUser(userRequest);
|
OidcUser oidcUser = delegate.loadUser(userRequest);
|
||||||
|
|
||||||
final OAuth2AuthenticationToken tempToken = new OAuth2AuthenticationToken(
|
final OAuth2AuthenticationToken tempToken = new OAuth2AuthenticationToken(
|
||||||
oidcUser,
|
oidcUser,
|
||||||
emptyList(),
|
emptyList(),
|
||||||
userRequest.getClientRegistration().getRegistrationId()
|
userRequest.getClientRegistration().getRegistrationId()
|
||||||
);
|
);
|
||||||
final List<SimpleGrantedAuthority> grantedAuthorities =
|
final List<SimpleGrantedAuthority> grantedAuthorities = getGrantedAuthorities(hawkbitClient, tempToken);
|
||||||
getGrantedAuthorities(hawkbitClient, tempToken);
|
|
||||||
return new DefaultOidcUser(
|
return new DefaultOidcUser(
|
||||||
grantedAuthorities,
|
grantedAuthorities,
|
||||||
oidcUser.getIdToken(),
|
oidcUser.getIdToken(),
|
||||||
|
|||||||
@@ -10,30 +10,22 @@
|
|||||||
package org.eclipse.hawkbit.ui.simple.security;
|
package org.eclipse.hawkbit.ui.simple.security;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
|
|
||||||
import org.springframework.security.oauth2.client.endpoint.RestClientRefreshTokenTokenResponseClient;
|
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
||||||
public class OAuth2TokenManager {
|
public class OAuth2TokenManager {
|
||||||
|
|
||||||
private final OAuth2AuthorizedClientService clientService;
|
private final OAuth2AuthorizedClientService clientService;
|
||||||
private final OAuth2AuthorizedClientManager clientManager;
|
private final OAuth2AuthorizedClientManager clientManager;
|
||||||
private final OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> tokenResponseClient;
|
|
||||||
|
|
||||||
OAuth2TokenManager(
|
OAuth2TokenManager(
|
||||||
final OAuth2AuthorizedClientService clientService,
|
final OAuth2AuthorizedClientService clientService,
|
||||||
@@ -41,42 +33,25 @@ public class OAuth2TokenManager {
|
|||||||
) {
|
) {
|
||||||
this.clientService = clientService;
|
this.clientService = clientService;
|
||||||
this.clientManager = clientManager;
|
this.clientManager = clientManager;
|
||||||
this.tokenResponseClient = new RestClientRefreshTokenTokenResponseClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken(final OAuth2AuthenticationToken authentication) {
|
public String getToken(final OAuth2AuthenticationToken authentication) {
|
||||||
return Optional.ofNullable(authorizedToken(authentication)).orElse(
|
final String currentToken = ((DefaultOidcUser) authentication.getPrincipal()).getIdToken().getTokenValue();
|
||||||
((DefaultOidcUser) authentication.getPrincipal()).getIdToken().getTokenValue()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to refresh the id token if it is expired and adds it to the request.
|
|
||||||
*/
|
|
||||||
private String authorizedToken(final OAuth2AuthenticationToken authentication) {
|
|
||||||
String registrationId = authentication.getAuthorizedClientRegistrationId();
|
String registrationId = authentication.getAuthorizedClientRegistrationId();
|
||||||
OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId(registrationId).principal(authentication).build();
|
|
||||||
|
|
||||||
// This ensures that there is a client already, otherwise we won't be able to call the manager for authorization
|
// This ensures that there is a client already, otherwise we won't be able to call the manager for authorization
|
||||||
OAuth2AuthorizedClient authorizedClient = clientService.loadAuthorizedClient(registrationId, authentication.getName());
|
OAuth2AuthorizedClient authorizedClient = clientService.loadAuthorizedClient(registrationId, authentication.getName());
|
||||||
if (authorizedClient == null) return null;
|
if (authorizedClient == null) return currentToken;
|
||||||
|
|
||||||
// Will ensure that the token is refreshed if needed; do not rely on it being not null as it won't be available
|
// Will ensure that the token is refreshed if needed; do not rely on it being not null as it won't be available
|
||||||
// during the first calls made to get the rights and generate the authorities
|
// during the first calls made to get the rights and generate the authorities
|
||||||
OAuth2AuthorizedClient refreshClient = clientManager.authorize(request);
|
OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId(registrationId).principal(authentication).build();
|
||||||
if (refreshClient == null) return null;
|
// since Spring Security 6.5 this will trigger a refresh of the id token
|
||||||
|
authorizedClient = clientManager.authorize(request);
|
||||||
|
if (authorizedClient == null) return currentToken;
|
||||||
|
|
||||||
// A small trick to refresh the token if it is expired; the current spring version does not refresh the ID Token when the Access Token is refreshed
|
// we need to fetch the newly created context containing the matching token
|
||||||
// This won't be necessary after Spring Security 6.5; cf. https://github.com/spring-projects/spring-security/pull/16589
|
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
OAuth2AccessToken accessToken = refreshClient.getAccessToken();
|
return ((DefaultOidcUser) securityContext.getAuthentication().getPrincipal()).getIdToken().getTokenValue();
|
||||||
OAuth2RefreshToken refreshToken = refreshClient.getRefreshToken();
|
|
||||||
ClientRegistration clientRegistration = refreshClient.getClientRegistration();
|
|
||||||
// if this is null, please request it via the scopes
|
|
||||||
if (refreshToken == null) return null;
|
|
||||||
|
|
||||||
OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(
|
|
||||||
clientRegistration, accessToken, refreshToken);
|
|
||||||
OAuth2AccessTokenResponse response = tokenResponseClient.getTokenResponse(refreshTokenGrantRequest);
|
|
||||||
return (String) response.getAdditionalParameters().get("id_token");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user