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
:
- Mozilla Dev Network
- Wikipedia
- MaxCDN - Gives a great step by step on how CORS works
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:
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:
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).
figure 1
Figure 2 is the abstracted route call using express router
that directs the "/songkick"
route to the apiProxyController
in figure 3.
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.
figure 3
So the solution?
- Create a server route that will be triggered by the user through the UI
- Make sure it captures all appropriate parameters
- Use an http request library like axios or request to make the request to the API in question (effectively bypassing the CORS issue)
- Return the results to browser and handle the data.
Note: You don't have to wrap a request
call inside of an axios
call, 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
- Music listened to while blogging: Youngr - "Out Of My System"
- SongKick API
- Spotify API
- Thanks to Brian Doyal my Bootcamp Instructor and mentor for helping to diagnose and find a solution