title: Spring OAuth: Custom form for authenticating authorization endpoint
body: <p>How can I set up a custom login form to protect my /oauth/authorize endpoint in an Spring Boot Application that is both a Authorization and Resource Server? I want to achieve that a user has to log in to make requests to /oauth/authorize. All resources, that are not /login and /oauth/** should be handled as secured resources protected by OAuth (requiring a valid access token)</p>
  
  <p>So when a user calls localhost:1234/oauth/authorize?client_id=client&amp;response_type=token&amp;redirect_uri=<a href="http://localhost:5678" rel="nofollow">http://localhost:5678</a> he is first redirected to the login form and after successfully loggin in redirect to the /oauth/authorize endpoint where the implicit OAuth flow proceeds.</p>
  
  <p>When I try the following it works, using the standard basic authentication popup window</p>
  
  <pre><code>@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");
      }
  
  }
  </code></pre>
  
  <p>The Resource config</p>
  
  <pre><code>@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
      }
  }
  </code></pre>
  
  <p>The Web Security config</p>
  
  <pre><code>@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();
      }
  
  }
  </code></pre>
  
  <p>but as soon as I replace httpBasic() with the following it fails:</p>
  
  <pre><code>@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();
      }
  
  }
  </code></pre>
  
  <p>and my POST from the login Page is not redirected, it always just returns to /login</p>
  
  <p>The output from the console is as following</p>
  
  <pre><code>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
  </code></pre>