Spring Security OAuth2 Redirect Loop
I have a spring-boot oauth2 client 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 to the SecurityContextHolder, but when the request is redirected to the original URL, the filter chain starts processing again. I noticed that in SecurityContextPersistenceFilter
contextBeforeChainExecution and contextAfterChainExecution both have null authentication.
I based some code on [1] Spring OAuth2 security web application (google) in a redirect loop
Any ideas as to why the redirect loop? Thank you in advance.
[Log snapshots] https://gist.github.com/yterradas/61da3f6eccc683b3a086
Below is the security configuration.
@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; } } }
source to share
I found a mediocre solution that entailed removing almost every filter from SecurityFilterChain
. Unfortunately, I don't have a working copy of the application I was working on. However, it should be easy to reproduce the solution by removing as many filters as possible before it breaks the application, then add only the ones you need. If my memory serves me correctly, the culprit was either SecurityContextPersistenceFilter
or RequestCacheAwareFilter
.
source to share
For others who might come here with the same problem - a redirect loop when using Spring Security oAuth. I had this because I am behind a firewall that blocks any direct access to the Internet, and my Spring Boot + Security application needs to call a userinfo IdP endpoint that was located on the Internet, like Okta, Google, etc. You can fix this by setting up a proxy in your local startup configuration like this:
-Dhttp.proxyHost=yourhttpproxy.company.com
-Dhttp.proxyPort=yourhttproxyport
-Dhttp.nonProxyHosts='localhost|*.yourintranetdomain1.com|*.yourintranetdomain2.com|etc'
-Dhttps.proxyHost=yourhttpsproxy.company.com
-Dhttps.proxyPort=yourhttpsproxyport
-Dhttps.nonProxyHosts='localhost|*.yourintranetdomain1.com|*.yourintranetdomain2.com|etc'
Hope this helps.
source to share