Angular2: Support for withCredentials and useXDomain

Karan Khanna picture Karan Khanna · Apr 10, 2016 · Viewed 9k times · Source

I am new to angular2. I am trying to login to my Django server which is a production server from localhost where i am currently writing my front end code. Also, I am port forwarding to my production server. I keep getting the CORS issue. Here is the error i get in my console:

XMLHttpRequest cannot load https://www.smartkart.ca/api/user/login/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.

I have written my HTTpService and have read almost all the blogs out there to figure out an solution but no luck. I believe i need to use the withCredentials: true property. I read some blogs and have modified my code to support withCredentials but no luck.

Here is my code snippet:

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';
import {Headers} from 'angular2/http';
import {BaseRequestOptions, RequestOptions} from 'angular2/http';


@Injectable()
export class HTTPService {
constructor(private _http: Http) {

}

baseUrl: String = "https://www.smartkart.ca/";
getStoreList() {        
    return this._http.get(this.baseUrl + 'api/current/store/')
        .map(res => res.json());
};

getCookie(name) {
    let value = "; " + document.cookie;
    let parts = value.split("; " + name + "=");
    if (parts.length == 2) 
    return parts.pop().split(";").shift();
};


login(){  
    // var request = new XMLHttpRequest();
    // var params = JSON.stringify({'email': '[email protected]', 'password': 'k0ttawa#1234'});
    // request.open('POST', this.baseUrl+ "api/user/login/", true);
    // request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
    // request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    // request.setRequestHeader("Content-length", params.length.toString());
    // request.setRequestHeader("Connection", "close");
    // request.send(params);             
    var data = JSON.stringify({'email': '[email protected]', 'password': 'blahblah'});     
    var params = data;  
    var headers = new Headers ();
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + btoa('[email protected]:k0ttawa#1234'));
    headers.append("Content-type", "application/x-www-form-urlencoded, application/json");
    // headers.append("Content-length", params.length.toString());
    // headers.append('Access-Control-Allow-Origin', '*');
    // headers.append('Access-Control-Request-Headers', 'x-requested-with');
    // headers.append('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    // headers.append('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    // headers.append('Access-Control-Allow-Credentials', 'true');
    // headers.append('Content-Type', 'pplication/x-www-form-urlencoded');
    // headers.append('X-CSRFToken', this.getCookie('csrftoken'));

    return this._http.post(this.baseUrl+ "api/user/login/", params,
         {
             headers: headers
         }
    ).map(res => res.json())

};

}

here is what i see in my network tab in the browser

enter image description here

Now if i take away the authorization header i do get logged in and i get back the csrf token but i can never get any response from the server due to the CORS issue. Here is a snapshot

enter image description here

I read some blogs where there were workarounds. I tried that too but no success.

Here is my code for the workaround:

import {Injectable} from 'angular2/core';
import {BrowserXhr } from 'angular2/http';

@Injectable()
export class CORSBrowserXHr extends BrowserXhr {

build(): any {
    let xhr = super.build();
    xhr.withCredentials = true;
    return <any>(xhr);
}
}

And in bootstrap i add the following:

/// <reference path="../typings/browser.d.ts" />
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from "./app.component";
import {ROUTER_PROVIDERS} from "angular2/router";
import {APP_BASE_HREF} from 'angular2/router';
import {provide} from 'angular2/core';
import {LocationStrategy, HashLocationStrategy} from 'angular2/router';
import {HTTP_PROVIDERS} from "angular2/http";
import {BrowserXhr} from 'angular2/http';
import {CORSBrowserXHr} from "./CORSBrowserXHr.services";

bootstrap(AppComponent, [ROUTER_PROVIDERS, HTTP_PROVIDERS,       provide(BrowserXhr, {useClass:CORSBrowserXHr}),
provide(LocationStrategy, {useClass: HashLocationStrategy})]);

I understand angular2 is under beta but i believe that by now they must have comeup with a solution for CORS support. If someone has any ideas please comment.

Thanks in advance!!

Answer

Thierry Templier picture Thierry Templier · Apr 10, 2016

In fact, there are two main cases in CORS:

  • Simple requests. We are in this case if we use HTTP methods GET, HEAD and POST. In the case of POST method, only content types with following values are supported: text/plain, application/x-www-form-urlencoded, multipart/form-data. Even if you're in these case and if you use a custom header (a header that you define by your own in your request), you'll fall into the preflighted request.

  • Preflighted requests. When you aren't in the case of simple requests, a first request (with HTTP method OPTIONS) is done to check what can be done in the context of cross-domain requests.

In your case, you're in the second case (preflighted request) since you use a custom header (defined by your own), the Authorization one.

But the corresponding OPTIONS request doesn't define a Access-Control-Allow-Origin header in its response. This would allow the browser to determine if you're (localhost:3000 for example) able to execute the target request.

In fact, it's not a problem in your Angular2 application but in the server application you try to access. The latter should return the appropriate CORS headers like the Access-Control-Allow-Origin one. Otherwise your browser will block the request...

These articles could interest you: