1

I am trying to send a request to my backend which uses HTTP Basic auth for authentication. For testing purposes

username: user
password: password

so the correct header is:

Authorization: Basic dXNlcjpwYXNzd29yZA==

I have tested the request with this header in Chrome Advanced Rest Extension and it works:

enter image description here

I generated the request in Angular2 like this:

  public getCurrentCounter() {
    console.log("Method getCurrentCounter() in CounterService called");

    var request = this.backendURL + "counter";
    var header = this.generateHeader(this.username, this.password);
    console.log(header);

    return this._http.get(request, {
      headers: header
    })
      .map(res => res.json());
  }

  /**
   * Generate HTTP header using HTTP basic Auth
   */
  private generateHeader(username, password) {

    var base64Creds = btoa(username + ":" + password);
    var auth = 'Basic ' + base64Creds;
    console.log(auth);
    var authHeader = new Headers();
    authHeader.append("Authorization", auth);

    return authHeader;
  }

I logged the generated Header Object and it looks like this:

enter image description here

Still I get this response:

XMLHttpRequest cannot load http://localhost:8080/counter. Response for preflight has invalid HTTP status code 401

Anybody an idea what could be wrong?

Jakob Abfalter
  • 4,980
  • 17
  • 54
  • 94

4 Answers4

3

The problem is related to CORS which is not enabled on the server side.

Your service must answer an OPTIONS request with headers like these:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]

Here is a good doc: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server

also look at this: Chrome v37/38 CORS failing (again) with 401 for OPTIONS pre-flight requests

for the basic authentication in angularjs 1.x could you please try:

service.SetCredentials = function (username, password) {
    var authdata = Base64.encode(username + ':' + password);
    $http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"}; //you probably don't need this line.  This lets me connect to my server on a different domain
    $http.defaults.headers.common['Authorization'] = 'Basic ' + authdata; // jshint ignore:line

        };

for the Angularjs 2.x version please have a look at:

Angular2 - set headers for every request

I found extending the BaseRequestOptions very interesting:

class MyRequestOptions extends BaseRequestOptions {
  constructor () {
    super();
    this.headers.append('Authorization', 'Basic ' + authdata);
  }
} 

I hope it helps

Community
  • 1
  • 1
thegio
  • 1,233
  • 7
  • 27
  • Thanks for the answer, sadly did not solve the issue. Also if this would be a CORS issue there would be a 403 response not a 401 – Jakob Abfalter Apr 12 '16 at 14:26
  • 401 Unauthorized is the HTTP status code for authentication errors. Is your server behind a basic authentication? – thegio Apr 12 '16 at 14:31
  • yes and the header for the basic authentication seems to not be set up correctly that the issue here – Jakob Abfalter Apr 12 '16 at 14:45
  • $http.defaults.headers.common['Authorization'] = 'Basic ' + authdata is the trick – thegio Apr 12 '16 at 14:55
  • What do you mean with $http? The http object does not have a `defaults` field. – Jakob Abfalter Apr 12 '16 at 15:00
  • Using Angular 2 RTM with ASP.NET Core (full fx) and when it came to calling a secure api, I had to add the following to my API (server): `public static class CorsBuilder { public static IApplicationBuilder UseCors(this IApplicationBuilder app) { app.UseCors(x => { x.AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .AllowCredentials(); }); return app; } }` – FlyingMaverick Jan 25 '17 at 02:14
3

So turns out the problem was on the backend.

The backend expected the OPTIONS request to be base authenticated as well, but since the OPTIONS request sent from angular2 doesn't have the Authentication Headers, we got an 401 response.

Limiting the request types which are expected to be authenticated on the backend fixed the issue.

Jakob Abfalter
  • 4,980
  • 17
  • 54
  • 94
  • Using Angular 2 RTM with ASP.NET Core (full fx) and when it came to calling a secure api, I had to add the following to my API (server): `public static class CorsBuilder { public static IApplicationBuilder UseCors(this IApplicationBuilder app) { app.UseCors(x => { x.AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin() .AllowCredentials(); }); return app; } }` – FlyingMaverick Jan 25 '17 at 02:12
0

Either you forgot to import the Headers class. Either you need to set the withCredentials property to true on the underlying XHR of your request.

To do that, you can do the following. First extend the BrowserXhr:

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
  constructor() {}
  build(): any {
    let xhr = super.build();
    xhr.withCredentials = true;
    return <any>(xhr);
  }
}

and override the BrowserXhr provider with the extended class:

bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
-1

Can you verify but it seems that you getting this error from an OPTIONS request ? and not a GET request.

Michael Desigaud
  • 2,115
  • 1
  • 15
  • 15