Spring Security with oidc: refresh the tokens

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:

  • spring-boot-starter-web
  • spring-boot-starter-security
  • spring-boot-starter-oauth2-client

I appreciate opinions about this workaround since I'm still not sure if such an overhead is really needed in Spring Boot.


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.