Spring-security-oauth: AuthorizationServerSecurityConfiguration does not whitelist OPTIONS

Created on 24 Oct 2018  路  11Comments  路  Source: spring-projects/spring-security-oauth

A large number of questions on Stack Overflow and elsewhere boil down to "Spring Security OAuth2 always fails OPTIONS preflights with 401". After some debugging, I have traced this to the parallel security chain that is installed by AuthorizationServerSecurityConfiguration, which invokes authorizeRequests fullyAuthenticated for the OAuth token endpoints but fails to whitelist the OPTIONS method. Since OPTIONS is always issued without authorization, this always fails.

Immediately after http should be the rule antMatchers(HttpMethod.OPTIONS).permitAll(), or alternately excluding OPTIONS requests in the requestMatchers section.

I'm aware that this module is on its way out in favor of the New And Improved OAuth2 support, but as authorization-server support there is not production-ready yet, I request a patch for this bug, as the usual workaround is to insert a CorsFilter that blindly (and opaquely) wipes Spring MVC CORS management.

waiting-for-triage

Most helpful comment

I found a way to fix the 401 error on Spring Security 5 and Spring Security OAuth 2.3.5 without turning off security for all OPTIONS requests on the token endpoint. I realized that you can add a security filter to the token endpoint via the AuthorizationServerSecurityConfigurer. I tried adding a CorsFilter and it worked. The only problem I have with this method is I couldn't leverage Spring MVC's CorsRegistry. If anyone can figure out how to use the CorsRegistry, let me know.

I've copied a sample configuration for my solution below:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableAuthorizationServer
public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
    //... other config

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        //... other config

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.applyPermitDefaultValues();

        // Maybe there's a way to use config from AuthorizationServerEndpointsConfigurer endpoints?
        source.registerCorsConfiguration("/oauth/token", config);
        CorsFilter filter = new CorsFilter(source);
        security.addTokenEndpointAuthenticationFilter(filter);
    }
}

All 11 comments

@chrylis The SecurityFilterChain that is built from AuthorizationServerSecurityConfiguration is matched on:

http
  .requestMatchers()
    .antMatchers("/oauth/token", "/oauth/token_key", "/oauth/check_token")

Why would OPTIONS need to be allowed for these endpoints? Maybe I'm not understanding your use case. Can you please provide details on your use case so I can better understand?

@jgrandja When javascript try to send CORS request, the browser uses http OPTIONS method to test whether the server support CORS. So all of these requests("/oauth/token", "/oauth/token_key", "/oauth/check_token") return 401 by default.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Functional_overview

@majie1993 Yes, I understand how CORS works. What I would like to know is the specific flow that is being used where CORS (OPTIONS) should be supported.

Can you please provide your specific use case (flow). As in, which endpoint do you require OPTIONS, for example /oauth/token? Also, which grant_type is the javascript client using? Being more specific will help me understand your use case.

@jgrandja I have an independent application to serve pure HTML and Javascript, its domain is h5.abc.com. To support Javascript http api call to api.abc.com(a site protected by Spring OAuth), all paths should let OPTIONS request permitAll.

@majie1993 You still have not provided me details on your specific flow as asked in my previous comment.

all paths should let OPTIONS request permitAll

I disagree with this for an Authorization Server and it's protected endpoints.

@jgrandja Sorry. After reread the doc and this issue https://github.com/spring-projects/spring-security/issues/4448

Spring Framework provides first class support for CORS. CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it.

I think I got a wrong understanding of CORS and spring security. This kind of 'OPTIONS' request should be handled in spring security and before real authenticate happened.
I should config spring security correctly to handle browser preflight request.

@majie1993 No worries. I'm glad you figured it out.

@chrylis Have you seen the docs on how to integrate CORS with Spring Security?

The answer to "when would you ever need this?" is that best practices for SPAs is never to use implicit flow, as it exposes the token value in the URL, and to use authorization-code instead. This means that the SPA needs to be able to access the OAuth endpoints (at a bare minimum token to complete the exchange, and optionally check_token to get basic information about the logged-in user to customize the UI).

Is there a workaround for this?

I found a way to fix the 401 error on Spring Security 5 and Spring Security OAuth 2.3.5 without turning off security for all OPTIONS requests on the token endpoint. I realized that you can add a security filter to the token endpoint via the AuthorizationServerSecurityConfigurer. I tried adding a CorsFilter and it worked. The only problem I have with this method is I couldn't leverage Spring MVC's CorsRegistry. If anyone can figure out how to use the CorsRegistry, let me know.

I've copied a sample configuration for my solution below:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableAuthorizationServer
public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
    //... other config

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        //... other config

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.applyPermitDefaultValues();

        // Maybe there's a way to use config from AuthorizationServerEndpointsConfigurer endpoints?
        source.registerCorsConfiguration("/oauth/token", config);
        CorsFilter filter = new CorsFilter(source);
        security.addTokenEndpointAuthenticationFilter(filter);
    }
}

@jgrandja: Can you please provide some feedback on this question at Stack Overflow?

@chrylis The SecurityFilterChain that is built from AuthorizationServerSecurityConfiguration is matched on:

http
  .requestMatchers()
    .antMatchers("/oauth/token", "/oauth/token_key", "/oauth/check_token")

Why would OPTIONS need to be allowed for these endpoints? Maybe I'm not understanding your use case. Can you please provide details on your use case so I can better understand?

Was this page helpful?
0 / 5 - 0 ratings