Reputation: 11012
Spring Boot 2 with Spring Security 5 can be configured to use an openID connect ID provider for authentication. I managed to setup up my project just by configuring Spring Security - that works fine with all kinds of perfectly preconfigured security mechanisms like mitigation of session fixation.
But it seems that Spring Security does not refresh the tokens (which are stored in the session) by itself when they are expired.
Is there a setting for that or do I have to care for the refresh myself?
Update: Spring Boot 2.1 has been released, so it is time to revisit this problem. I still have no clue if the accessToken can now be automatically refreshed or if I have to write code for doing so...
Upvotes: 16
Views: 14345
Reputation: 41
In case someone get there looking for the answer for newer version of Spring. Currently we have Spring 6. I am using oauth2Login() feature to log in with the use of socials like github, facebook. I wanted to tie user session to access token expireAt property. If expired, should refresh token and try to extend the session before force user to log in once again. Non Reactive solution (Without WebClient
and OAuth2AuthorizedClientExchangeFilterFunction
) within Spring doesn't provide auto mechanism in oauth2Login()
feature. But I configured a bean for
OAuth2AuthorizedClientManager which can be used with oauth2Client()
feature with Spring reactive. However it's probably a cool way to maually refresh the access token and clear manually an authentication context in case of exception.
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager() {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository(), authorizedClientRepository());
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
and Filter
public class ExpiredTokenFilter extends OncePerRequestFilter {
@Resource
private OAuth2AuthorizedClientManager authorizedClientManager;
@Resource
private OAuth2AuthorizedClientRepository authorizedClientRepository;
@Override
protected void doFilterInternal(
@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response,
@Nonnull FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (Objects.nonNull(authentication) && authentication.getPrincipal() instanceof OidcUser) {
validateToken(authentication, request, response);
}
filterChain.doFilter(request, response);
}
private void validateToken(Authentication auth, HttpServletRequest request, HttpServletResponse response) {
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(MY_PROVIDER)
.principal(auth)
.attributes(attrs -> {
attrs.put(HttpServletRequest.class.getName(), request);
attrs.put(HttpServletResponse.class.getName(), response);
})
.build();
try {
authorizedClientManager.authorize(authorizeRequest);
} catch (ClientAuthorizationException e) {
authorizedClientRepository.removeAuthorizedClient(MY_PROVIDER, auth, request, response);
SecurityContextHolder.getContext().setAuthentication(null);
}
}
}
Remember to register Filter in SecurityFilterChain
http.addFilterBefore(expiredTokenFilter, AbstractPreAuthenticatedProcessingFilter.class));
Upvotes: 3
Reputation: 74
According to https://github.com/spring-projects/spring-security/issues/6742 it seems that the token is intentionally not refreshed:
An ID Token typically comes with an expiration date. The RP MAY rely on it to expire the RP session.
Spring does not. There are two enhancements mentioned at the end which should solve some of the refresh issues - both are still open.
As a workaround, I implemented a GenericFilterBean which checks the token and clears the authentication in the current security context. Thus a new token is needed.
@Configuration
public class RefreshTokenFilterConfig {
@Bean
GenericFilterBean refreshTokenFilter(OAuth2AuthorizedClientService clientService) {
return new GenericFilterBean() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
token.getAuthorizedClientRegistrationId(),
token.getName());
OAuth2AccessToken accessToken = client.getAccessToken();
if (accessToken.getExpiresAt().isBefore(Instant.now())) {
SecurityContextHolder.getContext().setAuthentication(null);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
};
}
}
Additionally I had to add the filter to the security config:
@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurer(GenericFilterBean refreshTokenFilter) {
return new WebSecurityConfigurerAdapter() {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(refreshTokenFilter, AnonymousAuthenticationFilter.class)
Implemented with spring-boot-starter-parent and dependencies in version 2.2.7.RELEASE:
I appreciate opinions about this workaround since I'm still not sure if such an overhead is really needed in Spring Boot.
Upvotes: 5
Reputation: 11411
According to the documentation,
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#webclient
When using a WebClient configured correctly, as given in the documentation it will automatically be refreshed.
Spring Security will automatically refresh expired tokens (if a refresh token is present)
This is also supported by the features matrix that refresh tokens are supported.
https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Features-Matrix
There was an older blog on Spring Security 5 that gives you access to beans that you could do this manually,
Authentication authentication =
SecurityContextHolder
.getContext()
.getAuthentication();
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) authentication;
There will be an OAuth2AuthorizedClientService automatically configured as a bean in the Spring application context, so you’ll only need to inject it into wherever you’ll use it.
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
String refreshToken = client.getRefreshToken();
And, failing to find it right now, but I assume as part of the OAuth2AuthorizedClientExchangeFilterFunction
has the calls to do a refresh.
Upvotes: 10
Reputation: 11012
even a bounty of 100 rep points did not yield an answer. So I guess there is currently no mechanism implemented to automatically refresh the access token with Spring Security.
A valid alternative seems to use the spring boot keycloak adapter which is capable of refreshing the token.
Upvotes: 0