First of all thx a lot for the sample code @mbhave and @philwebb
While trying it out I noticed that after a successful login with either GitHub or Google I see following log in the console and get redirected to the /login page as the authorisation was unsuccessful.
Log output
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) ~[spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) ~[spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:204) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:109) [spring-security-oauth2-client-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) [spring-security-web-5.0.0.RC1.jar:5.0.0.RC1]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_112]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_112]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_112]
Is there some configuration missing?
Sample pom.xml
```
<groupId>ch.aaap</groupId>
<artifactId>3APAdmin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>3APAdmin</name>
<description>3AP Admin</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M6</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<!-- We don't need this once there is a 2.0.Release version available -->
<repositories>
<repository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
```
Register a new OAuth application
Adapt application.yml accordingly
Thx a lot for the help
@gabac that's really hard to say. Please describe the precise steps that lead to this situation. In particular, make sure you've registered your app in configuration
@snicoll adapted the issue with the steps to reproduce 馃憤
Thanks. Something looks broken:
loginShouldHaveBothOAuthClientsToChooseFrom test fail for me. The same test passes on the command line (so it could be a glitch of some sort)ping @mbhave
It looks this happens if the redirect-uri for the client registration is a custom one, which is what the sample is doing. If the oauth application is registered with the default redirect-uri used by Spring Security which is /login/oauth2/code/{registration-id}, the login works.
/cc @jgrandja.
@mbhave so the bug is that you can't provide a custom redirect-uri ?
@gabac I'm assuming you didn't change the redirect-uri in the sample? Correct?
If you didn't than it's http://localhost:8080/oauth2/authorize/code/github in application.yml and the same in your GitHub registered app.
The Filter that handles the Authorization Response callback is OAuth2LoginAuthenticationFilter with the default RequestMatcher matching on /login/oauth2/code/* (OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI).
As you can see the sample redirect-uri /oauth2/authorize/code/github does not match the default filter processing uri /login/oauth2/code/*. This was actually changed recently in Spring Security RC1 and hence broke the sample.
When the Authorization Response is redirected back to the application by GitHub to http://localhost:8080/oauth2/authorize/code/github, the Filter does not intercept the request and given that you need to be authenticated as all endpoints are protected, you get the Access Denied exception. So this makes sense.
For a quick test, change the redirect-uri in the sample to http://localhost:8080/login/oauth2/code/github as well in your GitHub app. This should work for you.
Alternatively, if you want to customize or keep the existing redirect-uri in the sample than you need to provide a custom security configuration as follows:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2/authorize/code/*");
}
}
This will configure OAuth2LoginAuthenticationFilter to match requests on /oauth2/authorize/code/*.
Hope this all makes sense?
It seems like, in that case, the only part that is configurable for the redirectUri, is the *. So the configured redirectUri has to match this pattern: /login/oauth2/code/*. We can update the Boot sample to use that pattern instead. Along with documenting this constraint, we need to document the fact that to handle another url, users will need to configure to process it themselves.
I was looking now for a week to solve the issue with the redirect-uri configuration property with oauth2. The property spring.security.oauth2.client.registration.{registrationId}.redirect-uri is not supported at all and has to be manually configured as @jgrandja written above. Is that correct?
Most helpful comment
@gabac I'm assuming you didn't change the
redirect-uriin the sample? Correct?If you didn't than it's
http://localhost:8080/oauth2/authorize/code/githubin application.yml and the same in your GitHub registered app.The
Filterthat handles the Authorization Response callback isOAuth2LoginAuthenticationFilterwith the defaultRequestMatchermatching on/login/oauth2/code/*(OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI).As you can see the sample
redirect-uri/oauth2/authorize/code/githubdoes not match the default filter processing uri/login/oauth2/code/*. This was actually changed recently in Spring Security RC1 and hence broke the sample.When the Authorization Response is redirected back to the application by GitHub to
http://localhost:8080/oauth2/authorize/code/github, theFilterdoes not intercept the request and given that you need to be authenticated as all endpoints are protected, you get the Access Denied exception. So this makes sense.For a quick test, change the
redirect-uriin the sample tohttp://localhost:8080/login/oauth2/code/githubas well in your GitHub app. This should work for you.Alternatively, if you want to customize or keep the existing
redirect-uriin the sample than you need to provide a custom security configuration as follows:This will configure
OAuth2LoginAuthenticationFilterto match requests on/oauth2/authorize/code/*.Hope this all makes sense?