Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP status code 401 even though I’m sending credentials in the request

Recently i have introduced JWT authentication to my Springboot and Angualr2 based App. In There i tried to do a POST request by passing the JWT token as below in my Angualr code

save(jobId: number, taskId: number, note: Note) {

   return this.http.post(environment.APIENDPOINT + '/jobs/' + jobId + '/tasks/' + taskId + '/notes', note, this.setHeaders()).map((response: Response) => response.json());

}

private setHeaders() {
        // create authorization header with jwt token
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));
        console.log("Current token---"+ currentUser.token);
        if (currentUser && currentUser.token) {

  let headers = new Headers();
  headers.append('Content-Type', 'application/json');
  headers.append('authorization','Bearer '+ currentUser.token);
            
   let r = new RequestOptions({ headers: headers })
   return r;

        }
    }

However in the server side it returns status code 401. The issue is in the Springboot side it checks the authorization header as below and it returns null

String authToken = request.getHeader("authorization ");

Then i had a look at the request header and it has the authorization header under Access-Control-Request-Headers as below. But it is not visible to the server side.

enter image description here

Then i read further and found that this could be an issue with CORS configuration. So i have modified my CORS configuration filter to have addExposedHeader as below

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addExposedHeader("authorization");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("DELETE");

    //config.addExposedHeader("Content-Type");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

Still the server complains that it can not find the Authorization header. Did i miss any thing here? Appreciate your help

Solution

After reading the sideshowbarker's comment below, i was able to understand the basics behind the issue. In My Project I have a JWT token filter and inside that it always checks the Authorization header. Then i have modified it as below and now it works as expected

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
try {

    if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
        response.setStatus(HttpServletResponse.SC_OK);
    }else{
        String authToken = request.getHeader(this.tokenHeader);
        jWTTokenAuthenticationService.parseClaimsFromToken(authToken);
        --
    }
    chain.doFilter(request, response);
}Catch(AuthenticationException authEx){
    SecurityContextHolder.clearContext();
    if (entryPoint != null) {
        entryPoint.commence(request, response, authEx);
    }
}

}

like image 223
keth Avatar asked Jul 31 '17 00:07

keth


People also ask

When should you send HTTP status code 401 from the server?

HTTP Status Code 401: "Unauthorized" Error Status code 401 - the "unauthorized" error, is a client-side error. Usually, this means the user's login credentials aren't working. The user entered an incorrect password, or the server doesn't know who the user is, and is asking them to try and log in again.

Which of the following cases will lead to a 401 response status code under the specifications of restful API design?

A 401 error response indicates that the client tried to operate on a protected resource without providing the proper authorization. It may have provided the wrong credentials or none at all. The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource.

Which status code should be returned when the user is authenticated but unauthorized to access the resource?

403 Forbidden is the status code to return when a client has valid credentials but not enough privileges to perform an action on a resource.


1 Answers

You need to configure the server to not require authorization for OPTIONS requests (that is, the server the request is being sent to — not the one serving your frontend code).

That’s because what’s happening is this:

  1. Your code tells your browser it wants to send a request with the Authorization header.
  2. Your browser says, OK, requests with the Authorization header require me to do a CORS preflight OPTIONS to ensure the server allows requests with the Authorization header.
  3. Your browser sends the OPTIONS request without the Authorization header, because the whole purpose of the OPTIONS check is to see if it’s OK to include that header.
  4. Your server sees the OPTIONS request but rejects it with a 401 since it lacks the header.
  5. Your browser expects a 200 or 204 response for the CORS preflight but instead gets that 401. So your browser stops right there and never tries the POST request from your code.

Further details:

The Access-Control-Request-Headers and Access-Control-Request-Method request headers show in the question indicate the browser’s doing a CORS preflight OPTIONS request.

And the presence of the Authorization and Content-Type: application/json request headers in your request are what trigger your browser do that CORS preflight — by sending an OPTIONS request to the server before trying the POST request in your code. And because that OPTIONS preflight fails, the browser stops right there and never attempts the POST.

So you must figure out what part of the server-side code on the server the request is being sent to causes it to require authorization for OPTIONS requests, and change that so it instead responds to OPTIONS with a 200 or 204 success response without requiring authorization.


For specific help on OPTIONS-enabling a Spring server in particular, see the following answers:

  • Disable Spring Security for OPTIONS Http Method
  • Spring CorsFilter does not seem to work still receiving a 401 on preflight requests
  • Response for preflight has invalid HTTP status code 401 - Spring
  • AngularJS & Spring Security with ROLE_ANONYMOUS still returns 401
like image 61
sideshowbarker Avatar answered Oct 21 '22 13:10

sideshowbarker