OAuth 2!

Why is my fetch request to OAuth server being blocked by CORS? (CORS, Same Origin Policy, Making Cross Origin Requests with <form>)

Background

Arthur Song
Dev Genius
Published in
6 min readSep 15, 2020

--

I spent a decent amount of time trying to get OAuth working for a writing application that integrates Google Drive API. I ran into a bunch of problems with CORS and decided to do some research about what exactly CORS is. I also learned a lot about how to properly make a request to an OAuth server from a browser. If you’ve been struggling with CORS and OAuth 2.0, you might find some of this stuff helpful.

Same Origin Policy

Really quick, before we go into why we need CORS, we need to know about the Same Origin Policy.

ALL requests made by the browser are made using the Same Origin Policy, (except for a few methods that we’ll cover later).

This means that code executed from one origin CANNOT access content from a different origin. A code’s origin is defined by (PROTOCOL, DOMAIN, and PORT).

Another example of a “cross” origin request:

Say you are on a React application hosted at http://localhost:3000 and you try to use fetch to access content from your API at http://localhost:4000 . This request will be denied by the SOP that is enforced by web browsers. (If you have developed full-stack applications, you’ve probably used CORS (Cross-Origin Resource Sharing) to enable cross-origin access. CORS is a way to “loosen” the SOP enforced by browsers and allow cross-origin resource sharing as its name suggests. We’ll get to CORS in a bit.)

Why do browsers enforce SOP?

For security reasons.

Here is an answer from stackexchange that shows how the same origin policy protects users.

“Why is the same origin policy important?

Assume you are logged into Facebook and visit a malicious website in another browser tab. Without the same origin policy, JavaScript on that website could do anything to your Facebook account that you are allowed to do. For example read private messages, post status updates, analyze the HTML DOM-tree after you entered your password before submitting the form.

But of course, Facebook wants to use JavaScript to enhance the user experience. So it is important that the browser can detect that this JavaScript is trusted to access Facebook resources. That’s where the same origin policy comes into play: If the JavaScript is included from a HTML page on facebook.com, it may access facebook.com resources.

Now replace Facebook with your online banking website, and it will be obvious that this is an issue.”

By ensuring that requests can only be made to the SAME origin the request originated from, browsers are able to keep user information secure from malicious websites.

CORS (Cross Origin Resource Sharing)

CORS allows us to loosen up the SOP enforced by browsers. All CORS is a process by which we can safely allow resource sharing between two different origins. It works by specifying extra HTTP headers in both the response and the request. Using these “CORS headers”, the browser decides whether an origin should have access to the requested content.

Configuring CORS

First, the content server needs to configure CORS and specify which Origins should be able to access content from the content server — you can also configure CORS to only allow certain things like Methods (i.e. GET, POST, DELETE) or Headers (i.e. Content-Type); specifying * for Access-Control-Allow-Origins will allow requests from all origins. Check out the documentation for how to configure CORS for your back end servers.

Then, the requester sends an HTTP request to the server with the HTTP header Origin, and the HTTP request mode set to cors (see Sec-Fetch-Mode below).

The server is going to receive the request and return a response with the CORS (e.g. Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers) configuration in the Headers.

It looks like this.

This is the code used to generate that request.

Once the browser gets the response back from the content server, it compares the CORS headers in the response and the request. If the origin is included in Access-Control-Allow-Origin and all other Access-Control-Allow… configurations are met, the browser will allow the content to be served. If not, the request is blocked by the CORS policy.

An important distinction to make here is that the browser is enforcing the CORS policy, NOT the content server. The requesting server as well as the content server are only including extra CORS headers into the HTTP request and response respectively. The browser receives the response and determines whether the CORS headers are satisfactory, and thereby, whether the content should be served.

How can we access content on Servers we don’t have control over for example OAuth servers?

Configuring CORS is an easy way to allow different origins access to the server content, but we can only configure CORS for servers we control.

If we want to use any third-party APIs, we most likely need to receive an access_token from the OAuth server, and the OAuth server won’t have CORS configured to allow our application’s origin access.

Side Note: (I really recommend if you have any confusion on how OAuth 2.0 works to check out this Medium post that explains OAuth 2.0 very simply).

Using any sort of fetch API from a browser to an OAuth server will give you this error.

Using <form> to submit cross origin request to OAuth Server

In the beginning, we said that almost all requests made by the browser are made using the same origin policy. Some requests that don’t enforce the same origin policy include requests made by the tags <script> <img> <forms> <a> <frame> , and <iframe> .

Since OAuth servers return the authorization code as a parameter in a redirect URL, we can use a tag like form to make a Cross origin request to the OAuth server. (Forms have no way of accessing an HTTP response, but a successful request to an OAuth server will simply redirect with the “response” in the URL, so forms are perfect to use).

After the OAuth request is authorized by the server and the user, it will redirect to this URL (the URL that I specified the Oauth server to redirect to; you need to configure this in the Oauth credentials for the API you’re using),

http://localhost:3000/client#state=oauth&access_token=myaccesstoken&token_type=Bearer&expires_in=3599&scope=https://www.googleapis.com/auth/drive

In React, we can easily parse the URL using qs package, and using history from react-router-dom .

Now that we have our access token we can make requests to the google drive API!

Things to take away from this post:

  1. BROWSERS use the same origin policy so that Javascript from one origin can’t access content from another origin.
  2. CORS is simply a mechanism of allowing “cross-origin” access. CORS works by both the requester and the responder providing extra “CORS headers”. The BROWSER will look at the CORS headers of the request/response and determine if the content should be served.
  3. Tags like <form> <script> <img> and others DON’T use SOP. We can easily use a <form> tag to submit a request to an OAuth server and capture the response in the URL.

I hope some of this material made working with CORS and configuring your Oauth 2.0 a little bit easier. Good luck coding!

Resources and further readings:

--

--

Flatiron School Graduate, React.js, NodeJS, Ruby on Rails Developer, Learning Enthusiast