14

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...

rdmueller
  • 10,742
  • 10
  • 69
  • 126

3 Answers3

10

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.

Darren Forsythe
  • 10,712
  • 4
  • 43
  • 54
4

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.

Sandeep Kumar
  • 2,397
  • 5
  • 30
  • 37
aberhallo
  • 64
  • 2
  • For who ever still looking for solution, it is here https://docs.spring.io/spring-security/site/docs/5.1.2.RELEASE/reference/html/servlet-webclient.html#servlet-webclient-setup . WebClient is the remedy. – ejazazeem Mar 11 '21 at 10:04
0

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.

rdmueller
  • 10,742
  • 10
  • 69
  • 126
  • THe latest https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/ documenation states that if given a refresh token spring security should automatically attempt to refresh it. https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Features-Matrix also supports that it is supported – Darren Forsythe Nov 07 '18 at 15:31
  • @DarrenForsythe thanx for the links - how about sating this as an answer so that I can assign the bounty to you? – rdmueller Nov 07 '18 at 18:30
  • so it seems that the web client handles the refresh token. So, as I do understand, the app gets a request on an endpoint and only if the controller tried to use the access token via a webClient, the refresh token will be used. Interesting concept... The Keycloak adapter already refreshes the accesstoken with the incoming request... – rdmueller Nov 07 '18 at 18:34
  • I'm unsure of the underlying implementation of the support for getting of and refresh etc. of tokens with the webclient, which Im currently running into an issue with myself, but from what Ive seen of the code if you've authenticated it would try to refresh if the refresh token available auth object on a request for a given provider. It is also possible to get a hold of lower level beans and do it yourself, https://spring.io/blog/2018/03/06/using-spring-security-5-to-integrate-with-oauth-2-secured-services-such-as-facebook-and-github has an example of that – Darren Forsythe Nov 07 '18 at 18:39
  • any idea if how to do it with new spring security release ??? Can we use any custom filter to refresh token and update the principal ? – Agam Dec 01 '19 at 22:30
  • @agam : try to avoid custom filters. everything should be possible with settings, but they are sometimes hidden. I got best results with keycloak adaptors instead of spring security. – rdmueller Dec 02 '19 at 07:24
  • Thanks. I will take a look at keycloak adaptor. Meanwhile I was able to do that with custom filter. As you said I should avoid it, any specific reason why?? – Agam Dec 02 '19 at 16:29
  • I have added my solution here. would you like to have a look? https://stackoverflow.com/questions/59144160/spring-oauth2-client-automatically-refresh-expired-access-token – Agam Dec 02 '19 at 17:47