A solution for HTTP access control (CORS) issues

A part of building new applications is running into errors and working to find solutions. I am currently working on an application that uses the SongKick API and the Spotify API to generate Spotify playlists based on artists performing in the user location within date range preferences. The error that I encountered involves Cross-Origin Resource Sharing or CORS. This post discusses CORS, what it is, why it's important, how I encountered CORS, and a solution to the problem.

So what is CORS and why is it important?

CORS (Cross-origin resource sharing) according to wikipedia:

"defines a way in which a browser and server can interact to determine whether or not it is safe to allow the cross-origin request. "

Further, in current browsers that implement CORS whenever information is requested from one webpage to another on different domains (the cross-origin request) there are certain credentials and permissions that must be provided by the user domain and the domain where the information is being requested from in order for the browser to allow the cross-origin request. Most modern browsers implement CORS and it is currently the recommended standard of the W3C for accessing web resources that lie on different domains.

Why is it important? For your safety and the safety of others. No, really it's an important security concept that prevents CSS or Javascript to request resources from a different source and from preventing attacks from lurking hackers. To use an appropriate metaphor, modern web browsers are kind of like bouncers for a private club (the club being the domain you want info form), if you don't know the password you ain't getting in.

Fun fact: OWASP (Open Web Application Security Project) listed Cross-Site Scripting (one of the most popular ways of bypassing the same-origin request policies) as number 3 on their top 10 list of threats in 2013 and CORS provides the framework and standardization to reduce this threat and grants companies the ability to share information without having to worry about being vulnerable to cyber attacks.

Here are a few more great resources to dive head first into CORS:

My CORS issue

Let's look at how this plays out in real life. In the application I am developing, Songkick.com has artist data that my site discovershowcase.herokuapp.com wants access to. This is considered a cross-origin request because the information being requested is not coming from the same domain. Traditionally, this wouldn't be allowed under browser's same origin policy. However, if CORS is supported, Songkick.com would include in its server response what are called response headers allowing my site access to the data. The required header as a part of the CORS standard is Access-Control-Allow-Origin followed by either specific domains that can access the data or a * wildcard, indicating that any domain can access the data. Omitting this header will cause the CORS request to fail.

So, when I deployed my application to Heroku and tried using the application online, I received the following error:

ErrorPic

This error is saying that the Songkick.com API is not responding with the appropriate header, Access-Control-Allow-Origin in order to allow my domain to communicate with it.

From this error and what we have just discussed about CORS it appears that Songkick.com does not implement CORS. After further research from what I can tell there are no plans for Songkick to implement CORS into their API anytime soon. So I had to find a way to "bypass" the CORS issue.

How to fix this - the solution

To make my application work with CORS I have implemented the use of Node route to act as somewhat of a proxy server to get the data from API instead of going through the browser.

When I was getting the error initially, the information was being requested in the following manner:

My application => Browser => Songkick API => Browser => My application

And I was getting the error because Songkick wasn't providing Chrome (the bouncer) with the appropriate password. Instead, and after consultation with a mentor you can request information in the following way to bypass the browser at least initially which will avoid the CORS header issues:

solution

Let's look at some code to make sense of the diagram above

The first code snippet (figure 1) is the function that takes in parameters from the UI and will then call the Node route "/songkick". When line 60 is encountered and the variable queryURL is called inside the axios GET request, this triggers the Node route which leads us to figure 2 (axios is a great promise based http client).

Axios call figure 1

Figure 2 is the abstracted route call using express router that directs the "/songkick" route to the apiProxyController in figure 3.

route figure 2

Here in figure 3, is where the Node route takes in the parameters from the queryURL variable from figure 1 in the form of req.params and is injected into the songkick api data request query (stored as the variable url). In line 30, a GET request is made using the request library passing in the url as the parameter. This effectively bypasses the browser avoiding the CORS issue because the request is now coming from a server route not the browser.

Since this route is first called within the getConcerts function from figure 1, once the GET request finishes, the results are returned and dealt with inside the .then((results)=>{}) block in line 61, figure 1.

Node Call figure 3

So the solution?
  1. Create a server route that will be triggered by the user through the UI
  2. Make sure it captures all appropriate parameters
  3. Use an http request library like axios or request to make the request to the API in question (effectively bypassing the CORS issue)
  4. Return the results to browser and handle the data.

Note: You don't have to wrap a request call inside of an axioscall, a server route by itself should do the trick. But in my instance, I already had quite a bit of code to handle to results inside of the axios call, so for now and until I refactor, this works for me.

Until next time...


Shout Outs

Subscribe to The Finley Code

* indicates required