Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Response for preflight has invalid HTTP status code 401 #34

Closed
igoravramovic opened this issue Apr 13, 2017 · 17 comments
Closed

Response for preflight has invalid HTTP status code 401 #34

igoravramovic opened this issue Apr 13, 2017 · 17 comments

Comments

@igoravramovic
Copy link

igoravramovic commented Apr 13, 2017

I have created angular 2 client
But i receive this error when i call /user method

Response for preflight has invalid HTTP status code 401

@bfwg
Copy link

bfwg commented Apr 13, 2017

Are you running your Angular 2 client on a dev-server? Like is it running on a different port(4200)
?

@igoravramovic
Copy link
Author

Yes i do
I have tried with setting up CorsFilter, as explained in official spring guide to cors, but i still receive the message
What is interesting is that i log in and then receive this message when i call /user method

@bfwg
Copy link

bfwg commented Apr 13, 2017

Sounds to me like the request header from Angular 2 is wrong. Have you set withCredentials: true?

  headers = new Headers({
    'Accept': 'application/json'
  });

...

this.http.get(
  path,
  {
    headers: this.headers,
    withCredentials: true
  }
)
.map(this.extractData)
.catch(this.handleError);

@igoravramovic
Copy link
Author

Still the same problem

Here is my code

Spring Cors Filter:
@component
@order(Ordered.HIGHEST_PRECEDENCE)

public class SimpleCORSFilter implements Filter {

@Override
public void init(FilterConfig fc) throws ServletException {
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
		throws IOException, ServletException {
	HttpServletResponse response = (HttpServletResponse) resp;
	HttpServletRequest request = (HttpServletRequest) req;
	response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");
	response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
	response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Credentials", "true");
	response.setHeader("Access-Control-Allow-Headers",
			"x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");

	if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
		response.setStatus(HttpServletResponse.SC_OK);
	} else {
		chain.doFilter(req, resp);
	}

}

@Override
public void destroy() {
}

}

Angular code
Login service:

sendCredential(model) {

let tokenUrl1 = "http://localhost:8080/auth";
let headers1 = new Headers({ 'Content-Type': 'application/json' });
return this.http.post(tokenUrl1, JSON.stringify(model), { headers: headers1 });

}

sendToken(token) {
let tokenUrl2 = "http://localhost:8080/user";

let headers = new Headers({ 'Accept': 'application/json' });
headers.append('Authorization', 'Bearer ' + token);

return this.http.get(tokenUrl2, { headers, withCredentials: true });

}

Login component

onSubmit() {
this.loginService.sendCredential(this.model)
.map(res => res.json())
.subscribe(
data => {

    localStorage.setItem("token", data.token);
    localStorage.setItem("currentUserName", this.model.username);
    
    this.loginService.sendToken(localStorage.getItem("token")).subscribe(
      data => {
        this.currentUserName = this.model.username;
        localStorage.setItem("currentUserName", this.model.username);
        this.model.username = '';
        this.model.password = '';
      },
      error => console.log(error)
    );
    
  },
  error => console.log(error)
);

}

@bfwg
Copy link

bfwg commented Apr 14, 2017

@igoravramovic
I see, you should remove the Bearer. Like this: headers.append('Authorization', token);.
If you really want to use Bearer you can do something like this:

        if ( authHeader != null && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }

More detail: https://github.com/bfwg/springboot-jwt-starter/blob/master/src/main/java/com/bfwg/security/auth/TokenAuthenticationFilter.java#L52

@szerhusenBC
Copy link
Owner

@bfwg You're right, I didn't include the "Bearer" prefix, yet. But I've opened a ticket for that.

@szerhusenBC
Copy link
Owner

@igoravramovic could you fix your problem?

@igoravramovic
Copy link
Author

Sorry for not responding more quickly, i was not able to take time to try proposed solution
No, this didn't solve my problem i still receive same error message

@jmw5598
Copy link

jmw5598 commented Apr 24, 2017

@igoravramovic

I was having the same issue with the prefight OPTIONS request return a 401. When the OPTIONS request is sent from angular 2, it's sent without the Authorization header. Spring Security tries to authenticate the request without the header and returns a 401. In the JwtAuthenticationTokenFilter, the token is null.

I added a filter before the JwtAuthenticationTokenFilter filter to check for the OPTIONS request and return HttpServletResponse.SC_OK. Here is the filter that I have in my code.

public class CorsFilter extends OncePerRequestFilter {

    static final String ORIGIN = "Origin";

    protected void doFilterInternal(
        HttpServletRequest request, 
        HttpServletResponse response, 
        FilterChain filterChain) throws ServletException, IOException {
    
        String origin = request.getHeader(ORIGIN);
    
        response.setHeader("Access-Control-Allow-Origin", "*");//* or origin as u prefer
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "content-type, authorization");
    
        if (request.getMethod().equals("OPTIONS"))
            response.setStatus(HttpServletResponse.SC_OK);
        else 
            filterChain.doFilter(request, response);
    
    }
}

In my security config I added the bean for the filter and added it to the configuration before JwtAuthenticationTokenFilter.

@Bean
public CorsFilter corsFilter() throws Exception {
    return new CorsFilter();
}


http
    .addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
    .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
    .headers()
    .cacheControl();

This seems to be working for me. Hope this helps you.

@szerhusenBC
Copy link
Owner

@igoravramovic is this helping you?

@jrcastillo
Copy link

jrcastillo commented Apr 28, 2017

@jmw5598 - I'm not sure if this can help, but try putting

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}

On your securityConfig.java class. As this ignores and let http:options request pass through.

@mupi2015
Copy link

@jrcastillo you are absolutely right, and this should solve OP's problem except he has to remove the Bearer part. To understand the original problem, please follow this link https://stackoverflow.com/questions/38368794/angular-2-basic-authentication-not-working

@szerhusenBC
Copy link
Owner

@igoravramovic is not replying since 19th Apr so I close this ticket now.

@quyle1710
Copy link

You can get through this very easy! Let's follow me right now

  1. Create a shortcut on your desktop
  2. Right-click on the shortcut and click Properties
  3. Edit the Target property
  4. Set it to "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:/ChromeDevSession"
  5. In VIsual Studio Code, run ionic serve -l
  6. You're gonna see new tab open http://localhost:8100/ionic-lab. You should be aware that this link is opened in the normal chrome tab, not the "disable-web-security" chrome we have set up.
  7. Double click to the shortcut that we have set up to open the "disable-web-security" chrome tab. Then paste http://localhost:8100/ionic-lab into this tab.

So the reason that we get multiple errors when working with woo-commerce-api is this "web-security" by Google. Then you just disable it and you actually don't need any CORS Extensions. So remove them right now if you have installed.

And this solution i write for people who learn this course https://www.udemy.com/ionic-3-apps-for-woocommerce-build-an-ecommerce-mobile-app/. This is an ionic e-commerce app that using woo-commerce-api to set and get data from Wordpress (local or live server). If you have trouble in other language not ionic, it still works fine.

Actually i have done a lot of searchings on Google to find this solution. I hope this helps all of you. Now, i need to go to bed because tomorrow i have a final report about this ionic project with my lecturer 😃

See ya!
Quy Le

@Waseem-farooqui
Copy link

Waseem-farooqui commented Apr 4, 2018

@jrcastillo thanks for the tip

And if anyone is using HttpSecurity then we need to use this

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()//allow CORS option calls
            .antMatchers("/resources/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .httpBasic();
}

@lastlink
Copy link

I can not for the life of me get cors to work on this project. I tried all of the above & nothing works. The only change I've been able to get working is applying a filter which only works after the user is already logged in showing the new headers on the response, but still fails a preflight request.

POST http://localhost:8080/auth HTTP/1.1
Content-Type: application/json; charset=utf-8

{
    "username":"admin",
    "password":"admin"
}


GET http://localhost:8080/user HTTP/1.1
content-type: application/json; charset=utf-8
authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUyMzk5NTUwNSwiaWF0IjoxNTIzMzkwNzA1fQ.ZJTUYWU4MVEIOR5EjLXqPiIsmBafATuSju6xSkBF8hmx6USM8q7qLXpCc4Wt2ZiIC3jKtSSFThP0sQfwp3xcRQ

@lastlink
Copy link

update after trying a ton of things the solution for me ended up being the following:

package org.tci.filters;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class CorsConfig {

	@Bean
	public FilterRegistrationBean corsFilter() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		CorsConfiguration config = new CorsConfiguration();
		config.setAllowCredentials(true);
		config.addAllowedOrigin("*");
		config.addAllowedHeader("*");
		config.addAllowedMethod("OPTIONS");
		config.addAllowedMethod("HEAD");
		config.addAllowedMethod("GET");
		config.addAllowedMethod("PUT");
		config.addAllowedMethod("POST");
		config.addAllowedMethod("DELETE");
		config.addAllowedMethod("PATCH");
		source.registerCorsConfiguration("/**", config);
		// return new CorsFilter(source);
		final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
		bean.setOrder(0);
		return bean;
	}

	@Bean
	public WebMvcConfigurer mvcConfigurer() {
		return new WebMvcConfigurerAdapter() {
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/**").allowedMethods("GET", "PUT", "POST", "GET", "OPTIONS");
			}
		};
	}
}

and adding

@CrossOrigin(origins = { "*" }, maxAge = 6000)

at the top of each controller

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants