Spring-boot – Spring Security OAuth2 Redirect Loop

spring-bootspring-securityspring-security-oauth2

I have a oauth2 client spring-boot application with dependencies:
– spring-boot 1.2.0.RC1
– spring-security-oauth2 2.0.4.RELEASE
– spring-security 3.2.5.RELEASE

The client authenticates, the authentication is set in the SecurityContextHolder but when the request is redirected to the original url the filter chain starts processing again. I noticed that in the SecurityContextPersistenceFilter the contextBeforeChainExecution and contextAfterChainExecution both have a null authentication.

I have based some of the code on [1]Spring Security OAuth2 (google) web app in redirect loop

Any ideas as to why the redirect loop? Thank you in advance.

[Logs snippet]https://gist.github.com/yterradas/61da3f6eccc683b3a086

Below is the config for security.

@Configuration
public class SecurityConfig {

  @Configuration
  @EnableWebMvcSecurity
  protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationProcessingFilter;

    @Autowired
    private LoginUrlAuthenticationEntryPoint vaultAuthenticationEntryPoint;

    @SuppressWarnings({"SpringJavaAutowiringInspection"})
    @Autowired
    private OAuth2ClientContextFilter oAuth2ClientContextFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      // @formatter:off
      http
          .authorizeRequests()
            .antMatchers("/**").authenticated()
        .and()
          .exceptionHandling().authenticationEntryPoint(vaultAuthenticationEntryPoint)
        .and()
          .addFilterAfter(oAuth2ClientContextFilter, ExceptionTranslationFilter.class)
          .addFilterBefore(oAuth2ClientAuthenticationProcessingFilter, FilterSecurityInterceptor.class)
          .anonymous().disable();
    // @formatter:on
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
      // @formatter:off
    web
       /* TODO:
       disable debug in production
       */
       .debug(true);
    // @formatter:on
    }
  }

  @Configuration
  @EnableOAuth2Client
  protected static class ClientSecurityConfig {

    @Value("${app.name}") private String appId;
    @Value("${app.clientId}") private String appClientId;
    @Value("${app.clientSecret}") private String appClientSecret;
    @Value("${app.redirectUrl}") private String appRedirectUrl;
    @Value("${vault.accessTokenUrl}") private String vaultAccessTokenUrl;
    @Value("${vault.userAuthorizationUrl}") private String vaultUserAuthorizationUrl;
    @Value("${vault.checkTokenUrl}") private String vaultCheckTokenUrl;

    @SuppressWarnings({"SpringJavaAutowiringInspection"})
    @Resource
    @Qualifier("oauth2ClientContext")
    private OAuth2ClientContext oAuth2ClientContext;

    @Autowired
    @Qualifier("securityDataSource")
    private DataSource securityDataSource;

    @Autowired
    private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;

    @Bean
    public OAuth2RestOperations oAuth2RestOperations() {
      AccessTokenProviderChain provider = new AccessTokenProviderChain(
          Arrays.asList(new AuthorizationCodeAccessTokenProvider())
      );
      provider.setClientTokenServices(new JdbcClientTokenServices(securityDataSource));

      OAuth2RestTemplate template = new OAuth2RestTemplate(oAuth2Resource(), oAuth2ClientContext);
      template.setAccessTokenProvider(provider);
      template.setMessageConverters(Arrays.asList(jackson2HttpMessageConverter));

      return template;
    }

    @Bean
    OAuth2ProtectedResourceDetails oAuth2Resource() {
      AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();

      resource.setId(appId);
      resource.setAuthenticationScheme(AuthenticationScheme.query);
      resource.setAccessTokenUri(vaultAccessTokenUrl);
      resource.setUserAuthorizationUri(vaultUserAuthorizationUrl);
      resource.setUseCurrentUri(false);
      resource.setPreEstablishedRedirectUri(appRedirectUrl);
      resource.setClientId(appClientId);
      resource.setClientSecret(appClientSecret);
      resource.setClientAuthenticationScheme(AuthenticationScheme.form);

      return resource;
    }

    @Bean
    ResourceServerTokenServices oAuth2RemoteTokenServices() {
      VaultTokenServices tokenServices = new VaultTokenServices();

      RestTemplate restOperations = new RestTemplate();
      restOperations.setMessageConverters(Arrays.asList(jackson2HttpMessageConverter));

      tokenServices.setRestTemplate(restOperations);
      tokenServices.setClientId(appClientId);
      tokenServices.setClientSecret(appClientSecret);
      tokenServices.setCheckTokenEndpointUrl(vaultCheckTokenUrl);

      return tokenServices;
    }

    @Bean
    LoginUrlAuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
      return new LoginUrlAuthenticationEntryPoint("/vaultLogin");
    }

    @Bean
    OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationProcessingFilter() {
      OAuth2ClientAuthenticationProcessingFilter filter =
          new OAuth2ClientAuthenticationProcessingFilter("/vaultLogin");

      filter.setRestTemplate(oAuth2RestOperations());
      filter.setTokenServices(oAuth2RemoteTokenServices());

      return filter;
    }

  }
}

Best Answer

I think you have 2 OAuth2ClientContextFilters (one is added by @EnableOAuth2Client and you have added another manually to the Spring Security filter chain). You should be able to remove the one you added.

Related Topic