Spring OAuth: Custom Form for Authentication Endpoint Authorization
How do I set up a custom login form to secure my / oauth / authorize endpoint in a Spring Boot application that is both an authorization and resource server? What I want to achieve is that the user has to be logged in to make / oauth / authorize requests. All resources that are not / login and / oauth / ** should be treated as OAuth-protected protected resources (requiring a valid access token)
So when the user calls localhost: 1234 / oauth / authorize? client_id = client & response_type = token & redirect_uri = http: // localhost: 5678 it is first redirected to the login form and after successfully entering the redirect queue to the / oauth / authorize endpoint where the implicit OAuth flow continues.
When I try the following it works using the standard authentication popup
@Configuration
@EnableAuthorizationServer
public class OAuthConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("implicit", "refresh_token")
.scopes("read");
}
}
Resource configuration
@Configuration
@EnableResourceServer
public class ResourceConfiguration
extends ResourceServerConfigurerAdapter
{
@Override
public void configure(final HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests().antMatchers("/login").permitAll().and()
.authorizeRequests().anyRequest().authenticated();
// @formatter:on
}
}
Web Security Configuration
@Configuration
public class WebSecurityConfiguration
extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/oauth/authorize").authenticated().and()
.authorizeRequests().anyRequest().permitAll().and().httpBasic();
}
}
but once i replaced httpBasic () with this:
@Configuration
public class WebSecurityConfiguration
extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/oauth/authorize").authenticated().and()
.authorizeRequests().anyRequest().permitAll().and().formLogin().loginPage("/login").and().csrf()
.disable();
}
}
and my POST from login page is not redirected, it always just returns / login
The output from the console is as follows:
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/css/**'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/js/**'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/images/**'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/**/favicon.ico'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/error'
o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token']
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/oauth/token'
o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token_key']
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/oauth/token_key'
o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/check_token']
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/oauth/check_token'
o.s.s.web.util.matcher.OrRequestMatcher : No matches found
o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration$NotOAuthRequestMatcher@5fe3eb5f
o.s.s.web.util.matcher.OrRequestMatcher : matched
o.s.security.web.FilterChainProxy : /login at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy : /login at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
o.s.security.web.FilterChainProxy : /login at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@51c78264
o.s.security.web.FilterChainProxy : /login at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/logout'
o.s.security.web.FilterChainProxy : /login at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
o.s.s.o.p.a.BearerTokenExtractor : Token not found in headers. Trying request parameters.
o.s.s.o.p.a.BearerTokenExtractor : Token not found in request parameters. Not an OAuth2 request.
p.a.OAuth2AuthenticationProcessingFilter : No token in request, will continue chain.
o.s.security.web.FilterChainProxy : /login at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.security.web.FilterChainProxy : /login at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy : /login at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@90541710: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: BDCE2D7EA7252AEA2506633726B8BA19; Granted Authorities: ROLE_ANONYMOUS'
o.s.security.web.FilterChainProxy : /login at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
o.s.security.web.FilterChainProxy : /login at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy : /login at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/login'
o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /login; Attributes: [#oauth2.throwOnError(permitAll)]
o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@90541710: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: BDCE2D7EA7252AEA2506633726B8BA19; Granted Authorities: ROLE_ANONYMOUS
o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@bba9bfc, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
o.s.security.web.FilterChainProxy : /login reached end of additional filter chain; proceeding with original chain
o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
source to share
Adding @Order (-10) to WebSecurityConfig fixed the issue. I think this annotation makes sure the WebSecurityConfig is used before the ResourceConfig. My final configuration looks like this:
@Configuration
@Order(-10)
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter
{
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests().antMatchers("/oauth/authorize").authenticated()
.and()
.authorizeRequests().anyRequest().permitAll()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.csrf().disable();
// @formatter:on
}
}
source to share