CORS

CORS – Cross Origin Resource Sharing

This is a sequel to my post on cURL.

“Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated” from wiki.

The above explanation is self explanatory. For instance, a resource loaded from Domain A (http://A:3000) such as an HTML web page, makes a request for a resource on Domain B (http://B:4000). The Same-Origin Policy restricts the browser from performing certain actions by scripts or documents based on the origin. The origin is everything in the URL before the path (e.g., in http://A:3000/images/bg.png, http://A:3000 is the origin and /images/bg.png is the path). For certain actions, the browser will compare origins and, if they don’t match, won’t allow things to proceed.

We kept getting CORS error, when we were trying to test our REST API. The server sided REST API logic was written on Node.js and was running on port 3000 on one system. The client side logic was also written on Node.js and was also running on port 3000, but on another system. I had initially tested the API using POSTMAN, and it worked great. But, when we tried integrating the front-end with the back-end, it was erroneous. And it was CORS. I had encountered something similar related to CORS, way back in 2011, during my first job, but it was resolved in few minutes. I cannot recall the exact issue, all I remember is that it had to do only with JQuery (front-end only).

This time, I tried understanding CORS in more detail.

Let’s assume, the server-side code was running on http://A:3000 and the client-side code was running on http://B:3000. We were trying to test the login api, i.e. front-end accepts username/password through a form and on button click, submits the values (HTTP POST) to the server. The server then validates the username/password combination. If successful, server returns the user id, else, returns appropriate error messages. We had agreed upon both the input and output to be JSON only.

Things looked alright, when the input type was x-www-form-urlencoded, i.e. normal form data being passed through JQuery AJAX request. But things went awry, when we restricted it to JSON only (POSTMAN still had no issues).

Following is the list of errors when we tried with JSON input, and how I went about solving each one of them.

1. http://B:3000 made a POST request to http://A:3000/login. But instead of POST, it always made a OPTIONS request.

Now the first thing to wonder about was why did this happen. To be honest, I wasn’t sure about the HTTP OPTIONS method until then. But this is what it is. “This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.”
Nobody uses this OPTIONS method, but it is used for CORS access to API’s. When doing a CORS request, the browser will do a ‘preflight’ OPTIONS request to discover if the server will answer the real request.

The next thing to wonder was what is this preflight.

CORS requests are usually preflighted. This request is basically there to ask the server if the full request is permissible. If POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML/JSON payload to the server using application/xml or text/xml or application/json, then the request is preflighted. Only if the server responds positive to this preflight request, the actual POST request will be sent to the server.

At this point it was clear why the POST request always came as an OPTIONS request for JSON input and not for x-www-form-urlencoded.

2. After I understood about OPTIONS method and the preflight request, following was the next error. XMLHttpRequest cannot load http://A:3000/login. The ‘Access-Control-Allow-Origin’ header has a value that is not equal to the supplied origin. Origin ‘null’ is therefore not allowed access.

I was running test.html locally on the browser as file:///E:test.html. Running it this way, always sets the Origin to Null in the request header. This took quite some time for me to understand and resolve. I then created another local Node.js server running on port 4000 to serve test.html. Alternative to this could have been serving test.html through WAMP Server/Apache on port 80. This resolved the null origin issue. The origin was now set to http://localhost:4000.

3. Following was the third error. XMLHttpRequest cannot load http://A:3000/login. The ‘Access-Control-Allow-Origin’ header has a value http://B:3000 that is not equal to the supplied origin. Origin http://B:3000 is therefore not allowed access.

This wasn’t scary, like the earlier one. I could make sense, what was happening here. On my server sided logic in Node.js, I had restricted “Access-Control-Allow-Origin” to http://B:3000 only. res.header("Access-Control-Allow-Origin", "http://B:3000");
On the client side code, I had to add the below line to JQuery AJAX method. When the CORS makes a preflight request, both the client and server should have the same headers set.
beforeSend: function (request) { request.setRequestHeader("Access-Control-Allow-Origin", "http://B:3000"); }

This is the full JavaScript working code after understanding about CORS, OPTIONS, preflight and setting headers through AJAX.

var login = {username: "sadmin", password: "sadminasd"}; $.ajax({ dataType: "json", beforeSend: function (request) { request.setRequestHeader("Access-Control-Allow-Origin", "http://B:3000"); }, contentType: "application/json", type: "POST", data: JSON.stringify(login), url: 'http://A:3000/login', success: function(data) { console.log("Success", data); //alert("Success "+data); }, error: function(err) { console.log("Error", err); //alert("Error "+err) } });

At one point, we took the approach of solving this problem using JSONP. JSONP was initially the solution prior to CORS for making cross domain requests. But JSONP supports only GET and not POST.

In my next blog, I shall talk more about JSONP and also about JSONP vs CORS.

Advertisements

4 thoughts on “CORS

Add yours

  1. Pingback: cURL | princiya777

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: