Describe the bug I have configured JWT authentication using spring boot with 'BearerTokenAuthenticationEntryPoint' to handle exceptions for unauthorized requests. CORS is also configured on the app.

When a requests is performed without a JWT token the 'BearerTokenAuthenticationEntryPoint' handles the error and returns a 401 UNAUTHORIZED status.

The problem is that it does not add any 'Access-Control-Allow-Origin' header causing a CORS error on the frontend.

To Reproduce Use the following web configuration and perform a request to any secured controller. You will receive a CORS error instead of just a 401 status code.


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebConfiguration implements WebMvcConfigurer {

    @Value("${public.key}")
    RSAPublicKey key;

    @Value("${private.key}")
    RSAPrivateKey priv;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("*")
                .allowedOrigins("http://localhost:4200")
                .allowedHeaders("*")
                .allowCredentials(true);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                // @formatter:on
                .csrf((csrf) -> csrf.ignoringAntMatchers(new String[] {}))
                // Add jwt authentication with stateless session (HttpSession will not be used
                // to get user data)
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                        .accessDeniedHandler(new BearerTokenAccessDeniedHandler()));

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(this.key).build();
    }

    @Bean
    JwtEncoder jwtEncoder() {
        JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }
}

Expected behavior 'BearerTokenAuthenticationEntryPoint' should add CORS header if CORS has been configured.

Sample spring-api.zip

Comment From: sjohnr

@huco95, thanks for the reaching out and providing a detailed example.

It looks as though you are not allowing a pre-flight request with Spring Security. This is done by calling http.cors(). See the CORS chapter of the reference. Have you tried this?

The problem is that it does not add any 'Access-Control-Allow-Origin' header causing a CORS error on the frontend.

I believe this header should be returned on the OPTIONS request, not a normal request to the resource server. In my testing, adding http.cors() to the security configuration resolves the issue. Please let me know if that solves it for you, or if I'm misunderstanding something in your setup.

Comment From: huco95

Hi @sjohnr You were right, http.cors() was missing and that's why I have the CORS error. Thanks!!