-1

I have backend hosted on Heroku, and frontend on Netlify. When I call endpoint on backend it sends preflight OPTIONS but it gives 403 status. I did search for solution but it still not working.

I want to be able to call "/authenticate" endpoint with "POST" method with body from FE to BE.

Spring security configuration (just configuration methods)

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    ...

    @Override
    public void configure(WebSecurity web) throws Exception
    {
        web.ignoring()
                .antMatchers(HttpMethod.POST, "/authenticate", "/register")
                .antMatchers(HttpMethod.GET, "/token")
                .antMatchers("/h2-console/**")
                .antMatchers("/v2/api-docs",
                "/configuration/ui",
                "/swagger-resources/**",
                "/configuration/security",
                "/swagger-ui.html",
                "/webjars/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {

        http
                .cors()
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/authenticate").permitAll()
                .antMatchers(HttpMethod.GET, "/user-data").authenticated()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOrigins(List.of(<MY-URL>));
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setMaxAge(Long.valueOf(3600));
        configuration.setAllowedMethods(Arrays.asList("GET","POST", "OPTIONS"));
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

And call from FE

var req = new XMLHttpRequest();
    req.open('POST', API_URL + '/authenticate', true);
    req.setRequestHeader("Content-Type", "application/json");
    req.withCredentials = true;
    req.onreadystatechange = function (aEvt) {
        if (req.readyState === 4) {
            if(req.status === 200) {
                console.log(req.responseText);
                isAuthenticationSucessful = true;
            }
            else
                console.log("Error loading site");
        }
    };

    req.send(JSON.stringify({username, password}));

Browser dev-tools:

Reason: CORS header 'Access-Control-Allow-Origin' missing
Reason: CORS request did not succeed
AzJa
  • 111
  • 8
  • config security as `http.cors().configurationSource(corsConfigurationSource()).and()...` – HMD Apr 21 '20 at 20:45
  • @HMD Thank you! Also in `setAllowedOrigins()` I put `myurl.com/` , but browser send request with `Origin: myurl.com` without that trailing slash, and it also was place where fix was necessary – AzJa Apr 21 '20 at 21:46
  • @HMD I was too fast. This don't fixed all my problems. It was sending successful request but responses wasn't accepted by browser. But I made it works, and you can check my answer if you want. And thanks again, your comment helped me go in right direction. – AzJa Apr 23 '20 at 10:51

1 Answers1

0

TL;DR

Make sure that in setAllowedOrigins("https://myrul.com") you don't have trailing slash or you have exactly the same origin that your browser send.

If your endpoint is in web.ignoring(... delete it from here and put it in (with my example endpoint) http.authorizeRequests().antMatchers("/authenticate").permitAll()

(web and http according to my code in question)

Longer

So how I said in my comment, one thing that make it not working correctly was setting setAllowedOrigins("https://myrul.com/") in corsConfigurationSource. Notice that trailing slash.

But I noticed in dev-tools that browser send origin header like this: Origin: https://myrul.com without trailing slash. To make it works I have to change allowed origins to proper origin like this: setAllowedOrigins("https://myrul.com") (without trailing slash). This make browser able to send requests to server, and get 200 response, but browser don't accept response from server cuz CORS.

The next thing was that I have my endpoint in web.ignoring("/authenticate")... and according to this question Spring Security Configuration - HttpSecurity vs WebSecurity this statement prevents Spring Security Filter Chain where it should header Access-Control-Allow-Origin which tell browser that it can accept response. MDN Access-Control-Allow-Origin

So the answer for that was take my endpoint from web.ignoring("/authenticate") to http.authorizeRequests().antMatchers("/authenticate").permitAll().

But this makes another problem, that is it will go now to filter chain and to my custom filter http.addFilterBefore(new JwtFilter()..., so make sure to adopt custom filters to yours need.

AzJa
  • 111
  • 8