0

There are a few questions similar to this on Stack Overflow, and none of the proposed solutions worked, so I'll walk through the case and what I've tried.

I have a server application hosted on Cloud Run, which can only be accessed with the appropriate Bearer token in the request Authorization header. I've tried accessing it via Postman and an Axios request from a local Nodejs server, with the Authorization header, and it worked fine. With React (create-react-app specifically), I get the following error: Access to XMLHttpRequest at 'https://myserver-lhp5a9xp5a-ue.a.run.app/api/rules' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

On the server side, I get the 403 error that Cloud Run gives when the incorrect Authorization token is passed. Also, when I allow unauthenticated access from the Cloud Run side (so remove the need for an Authorization header), the request works fine, so it looks like this is indeed an issue with the Authorization header and not CORS.

In addition, I'm handling CORS on the server side. Here's my server-side code:

var express = require('express');
var router = express.Router();
const cors = require('cors');

router.options('/api/rules', cors());
router.get('/api/rules', cors(), (req, res, next) => {
  res.status(200).send()
});

Here's my React code:

const axiosInstance = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL
});

const buttonClickHandler = async (event) => {
  const resp = await axiosInstance.get('/api/rules'
   , {
     headers: {
       'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsImtpZ...' // I used this token within the same minute when trying the request via Postman or from my Nodejs app, so a token expiry isn't the issue.
     }
   }
  )
  console.log(resp.data)
}

Here's what I tried so far:

  • Using fetch instead of axios - same error
  • Using the same token, within the same 5 seconds, to send the request from Postman or a Nodejs server - it worked fine.
  • Using an axios interceptor to set the Authorization - same error
  • Removing the single quotes around Authorization - same error
  • Sending the request to my Nodejs server instead and doing a console.log of the header to make sure the Authorization token is being passed correctly (it is)
  • Not using an an axios instance but spelling out the full URL in the request - same error
  • Trying a different endpoint on my Cloud Run server - same error
  • Deploying my React app to be served from a https endpoint and sending the request from there - same error
  • Adding Accept: '*/*' to the headers
  • Adding 'Accept': '*/*' to the headers
  • Adding 'Content-Type': 'application/json' to the headers
  • All combinations of the three above points
kgaspard
  • 302
  • 2
  • 10
  • How did you use the authentication middleware ? Is there something like `router.get('/api/rules', **auth**, cors(), (req, res, next) => {` ? – Đăng Khoa Đinh May 29 '21 at 17:31

2 Answers2

1

I found the answer after some digging, thanks @aniket-kolekar for pointing me in the right direction.

When Postman or a Nodejs server query an endpoint like GET, POST, PUT, DELETE, they send the call without checking the OPTIONS first. Create-React-App does.

The service I was querying is hosted on Cloud Run and doesn't allow unauthenticated invocations. So while I was including the authorization header to make my GET call, it wasn't being included in the pre-flight OPTIONS call. In fact, CORS prevents auth headers from being included in an OPTIONS call.

A Cloud Run PM replied in this post that this is a known issue with Cloud Run. The way I'll get around it for now is to host two services on Cloud Run - one that doesn't require authentication, and effectively acts as a proxy server to route calls from the client service to the shielded server service.

kgaspard
  • 302
  • 2
  • 10
0

TLDR;

  • CORS is a mechanism built into the web browser. It’s not a UI code issue.
  • To fix CORS problems, you need to make changes on the API (server) side.

Here is the behind the scenes working:

Browser: Sends OPTIONS call to check the server type and getting the headers before sending any new request to the API endpoint. Where it checks for Access-Control-Allow-Origin. Taking this into account Access-Control-Allow-Origin header just specifies which all CROSS ORIGINS are allowed, although by default browser will only allow the same origin.

Postman: Sends direct GET, POST, PUT, DELETE etc. request without checking what type of server is and getting the header Access-Control-Allow-Origin by using OPTIONS call to the server.

You will have to configure Access-Control-Allow-Origin header in your server to resolve the CORS issue.

Aniket Kolekar
  • 383
  • 6
  • 19
  • 1
    Thanks! But I don't think that's the answer unfortunately: 1) I already have OPTIONS and cors() handling on my server side (good point - will add that code in my question) 2) When I allow unauthenticated invocations on the server side (which, in principle, has nothing to do with CORS), the request works fine – kgaspard May 29 '21 at 14:02