title: Spring OAuth2 not redirecting back to client on form login
body: <p>I am working on a sample Spring Boot app using OAuth2. The issue is that the client hosted on <code>localhost:8080</code> calls out to <code>https://localhost:8443/oauth/authorize</code> to authorize itself (implicit grant type) but since <code>/oauth/authorize</code> requires that the user be authenticated they are redirected to the login page at <code>https://localhost:8443/login</code>.</p>
  
  <p>This is all expected, but when the user lands on the login page all of the query strings including redirect_uri are missing. The user logs in and is redirected to <code>https://localhost:8443</code> rather than the specified redirect_uri of <code>http://localhost:8080</code>.</p>
  
  <p>Is there some way to have the user redirected back to the client after logging in w/ the server's login form? Am I missing something in my config? I can post more as needed.</p>
  
  <p>The authorize request looks like: <code>https://localhost:8443/oauth/authorize?response_type=token&amp;state=6c2bb162-0f26-4caa-abbe-b65f7e5c6a2e&amp;redirect_uri=http%3A%2F%2Flocalhost%3A8080&amp;client_id=admin</code></p>
  
  <p>SecurityConfig:</p>
  
  <pre><code>@Configuration
  public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  
      private final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class);                        
  
      @Override
      public void configure(WebSecurity web) throws Exception {
  
          web.ignoring().antMatchers("/resources/**");
      }       
  
      @SuppressWarnings("deprecation")
      @Override
      protected void configure(HttpSecurity http) throws Exception {                  
  
          http
              .requestMatchers()
                  .antMatchers("/**")
          .and()
              .addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
              .exceptionHandling()
                  .accessDeniedPage("/login?authorization_error=true")
          .and()
              .authorizeRequests()
              .antMatchers("/resources/**", "/csrf").permitAll()
              .anyRequest().authenticated()
          .and()
              .formLogin()
                  .loginPage("/login")
                  .usernameParameter("j_username")
                  .passwordParameter("j_password")
                  .defaultSuccessUrl("/", false)
                  .failureUrl("/login?authentication_error=true")
                  .permitAll()
          .and()
              .logout()
                  .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                  .logoutSuccessUrl("/login")
                  .invalidateHttpSession(true)
                  .deleteCookies("JSESSIONID", "CSRF-TOKEN")
                  .permitAll()
         .and()
              .headers()
                  .frameOptions()
                  .disable();
      }
  </code></pre>
  
  <p>OAuthConfig:</p>
  
  <pre><code>@Configuration
  @EnableAuthorizationServer
  protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
  
      @Inject
      @Qualifier("authenticationManagerBean")
      private AuthenticationManager authenticationManager;
  
      @Bean
      public TokenStore tokenStore() {
  
          return new InMemoryTokenStore();
      }
  
      @Primary
      @Bean
      public ResourceServerTokenServices tokenServices() {
  
          DefaultTokenServices tokenServices = new DefaultTokenServices();
          tokenServices.setSupportRefreshToken(true);
          tokenServices.setTokenStore(tokenStore());
  
          return tokenServices;
      }        
  
      @Bean
      public ApprovalStore approvalStore() throws Exception {
  
          TokenApprovalStore approvalStore = new TokenApprovalStore();
          approvalStore.setTokenStore(tokenStore());
  
          return approvalStore;
      }
  
      @Override
      public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  
          clients
              .inMemory()
                  .withClient("read-only")
                      .secret("readme")
                      .resourceIds(RESOURCE_ID)
                      .authorizedGrantTypes("implicit", "password", "refresh_token")                        
                      .authorities(Constant.USER)
                      .scopes("read")
                      .autoApprove(true)
                      .redirectUris("https://localhost:8443")
                  .and()
                  .withClient("admin")
                      .secret("admin")
                      .resourceIds(RESOURCE_ID)
                      .authorizedGrantTypes("implicit", "password", "refresh_token")
                      .authorities(Constant.USER, Constant.ADMIN)
                      .scopes("read", "write")
                      .autoApprove(true)
                      .redirectUris("https://localhost:8443", "http://localhost:8080")
                  .and()
                  .withClient("super-admin")
                      .secret("super")
                      .resourceIds(RESOURCE_ID)
                      .authorizedGrantTypes("implicit", "password", "refresh_token")
                      .authorities(Constant.USER, Constant.ADMIN)
                      .scopes("read", "write", "delete")
                      .redirectUris("https://localhost:8443");
      }
  
      @Override
      public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
  
          configurer
              .tokenStore(tokenStore())
              .authenticationManager(authenticationManager);
      }        
  
      @Override
      public void configure(AuthorizationServerSecurityConfigurer security)
              throws Exception {
  
          security.realm("hubble/client");
      }
  
  }
  
  @Configuration
  @EnableResourceServer
  protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {        
  
      @Override
      public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
  
          resources.resourceId(RESOURCE_ID);
      }
  
      @Override
      public void configure(HttpSecurity http) throws Exception {
  
          http
              .requestMatchers()
                  .antMatchers("/api/**")
          .and()
              .authorizeRequests()
                  .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
                  .antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
                  .antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')")
                  .antMatchers(HttpMethod.PATCH, "/api/**").access("#oauth2.hasScope('write')")
                  .antMatchers(HttpMethod.PUT, "/api/**").access("#oauth2.hasScope('write')")
                  .antMatchers(HttpMethod.DELETE, "/api/**").access("#oauth2.hasScope('delete')")
                  .antMatchers("/api/**").access("hasRole('" + Constant.USER + "')")                  
         .and()
              .anonymous().authorities(Constant.ANONYMOUS)
         .and()
              .sessionManagement()
                  .sessionCreationPolicy(SessionCreationPolicy.STATELESS);                        
      }
  }
  
  @Configuration
  @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
  protected static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {     
  
      @Override
      protected MethodSecurityExpressionHandler createExpressionHandler() {
  
          OAuth2MethodSecurityExpressionHandler methodHandler = new OAuth2MethodSecurityExpressionHandler();
  
          return methodHandler;
      }
  }
  </code></pre>