Deciphering CORS

An API serves as the backbone for the applications and makes it data rich, but sometimes life gets tough when you try to play with APIs that are not on the same origin where the app is located.

Due to security reasons, a request is not successfully fulfilled if the requested resource does not reside in the same origin from where the request has arrived. And this proves to be a bottleneck as the applications won't be able to request/consume resources that are not on the same origin.

CORS - Cross Origin Resource Sharing

To understand CORS and to make out the most of it safely, it is important to understand a bit how browsers work and its behaviour in such cases.

The Same Origin Policy

This Same Origin Policy restricts the browser from performing certain actions by scripts or documents based on their origin. The origin is domain name or the string component from the url before the first slash. There are some security reasons due to which browsers won't allow you to access resources that are not from the same origin.

Scenarios when intentionally or unintentionally you break the Same Origin Policy :

A Step towards Cross-Origin Resource Sharing

CORS allows us to maintain separation of concern where the frontend (the look and feel, the HTML) can reside on a different server, and the backend that provides data to the former with API endpoints, is on another server.

Places where you see CORS in action:

  • You are using a third party API e.g Facebook, google, twitter, etc.
  • You are creating web app and utilising the API from another server.

For the case when you are using third party APIs you do not need to handle or worry much, as most of the things are being handled by these services. However, when building an API yourself, your server has to take care and identify and respond to requests accordingly.

Implementing CORS

CORS is a set of response headers depending on which the browser decides whether to allow the request or not. Requests dealing with CORS can mainly be divided into two types

  1. Simple requests such as GET and POST. You don't need to care, it works like a normal request.
  2. Preflight Requests with custom headers, custom response headers. More on preflight requests

Here you are trying to mess with the browser, which results the browser to send a preflight request to the server.
This request is to ask the server if the request with the headers and the data is permissible or not. Point to note here is that client does not need to worry about this, as this is being handled and sent by browser, but server has to take care of this request so that the CORS cycle can complete gracefully.

Preflight CORS Request

Let's take an example where we want to get all the users with pagination, we have an access token that needs to be passed to the API server in Authorization header and the project's backend has been hosted on http://projectbackend.com. When we create an ajax request for the mentioned case, browser will send an OPTIONS request to the API server with the relevant headers.

You will be sending something like this

    jQuery.ajax(
    {
        type: "POST",
        url: "http://projectbackend.com/GetAllUsers",
        headers : {
            "Authorization" : "525045075934759",
            "ItemsPerPage" : 10,
            "PageNumber" : 1
        }
    },

This will result the browser to trigger a preflight request, which is basically a request with type OPTIONS that is to check if the current request that holds the actual request header information is allowed or not. The server will respond to this OPTIONS with variety of headers that tells the browser if the request is allowed or not, and exactly what is allowed for the request.

  • Access-Control-Allow-Origin : Needs to be the origin of the request or * for allowing any application to use this as resource.
  • Access-Control-Allow-Methods : Tells about the methods that are allowed while sending the actual request, that is a comma seperated values of methods such as GET, POST, PUT, DELETE, PATCH
  • Access-Control-Allow-Headers : Tells about the request headers that are allowed while requesting for the resource. This is also a comma seperated values of headers that can be allowed. Example : authorisation, x-client-token.
  • Access-Control-Expose-Headers : This tells the browser headers that client can access. Server may be sending a ton of headers in response, but in case of CORS request the client[javascript in browser] is not able to access those headers unless they are not exposed. Server has to allow/handle this if client needs to access the response headers.

An example of the CORS options request

Access-Control-Request-Headers:authorization, content-type, itemsperpage, pagenumber  
Access-Control-Request-Method:POST  
Cache-Control:no-cache  
Connection:keep-alive  
Host:projectbackend.com/GetAllUsers  
Origin:http://localhost:3000  

This will let the server, projectbackend.com, know the headers and the method of the request that is about to happen, so if the server is good with it, it should respond with something like this

Access-Control-Allow-Headers:Content-Type, Accept, Authorization, PageNumber, ItemsPerPage  
Access-Control-Allow-Methods:GET, POST, OPTIONS  
Access-Control-Allow-Origin:*  
Access-Control-Expose-Headers:TotalItemsCount  

This tells that

  • Content-Type, Accept, Authorisation, PageNumber, ItemsPerPage, are the allowed request-headers

  • GET, POST and OPTIONS are the allowed methods

  • Request can originate from any where and can ask for resources.

  • TotalItemsCount is the only header that client can access too

In case the origin is not allowed you might get error saying
No 'Access-Control-Allow-Origin' header is present on the requested resource error in browser's console In most of the cases this can be fixed by allowing the origin to request for the resource such as [in php]

header("Access-Control-Allow-Origin: http://localhost:3000");  

OR

In .htaccess

Header add Access-Control-Allow-Origin "*"  
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"  
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"  

Don't forget to activate the apache module headers by running a2enmod headers in case it is not allowed yet.

And if everything goes fine, the browser will send the actual POST request and you will get the response as you expected.

I hope that this article might have given you a better understanding of CORS and how it works.

References :
https://www.w3.org/TR/cors/#security

https://developer.mozilla.org/en-US/docs/Web/HTTP/AccesscontrolCORS#Preflighted_requests

https://remysharp.com/2011/04/21/getting-cors-working