Google maps API returning No 'Access-Control-Allow-Origin' with Node.js module - node.js

I am using the node module #googlemaps/google-maps-services-js to make requests to the Google maps API. When I make requests to any of their API endpoints (e.g directions API, places API, etc), I get these errors:
xhr.js:125 Refused to set unsafe header "User-Agent"
xhr.js:125 Refused to set unsafe header "Accept-Encoding"
localhost/:1 Access to XMLHttpRequest at 'https://maps.googleapis.com/maps/api/directions/json?
destination=Universal%20Studios%20Hollywood&key=mykey&origin=Disneyland' from
origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource.
Here are the things I've tried:
1. I've tried accessing different API endpoints, (e.g instead of directions, places.)
2. I've tried adding headers manually, but I don't really know what headers i should add to make this work.
3. I have also tried accessing this API from my normal browser (Google). It returned a proper response, so it isn't an improper API key.
Also Note:
I have activated my API key, activated each of the APIs I want to use, and I have set up a billing account with Google Cloud Platform.

Not for browser
#googlemaps/google-maps-services-js is not compatible with browser environments because the Google server DOES NOT return the necessary CORS headers. This package is for Nodejs.
From the readme:
Alternative [Preferred]
Use https://developers.google.com/maps/documentation/javascript/overview.
Your code will look similar to the following:
const directionsService = new google.maps.DirectionsService();
const request = {
origin: 'Disneyland',
destination: 'Universal Studios Hollywood',
travelMode: 'DRIVING'
};
const response = await directionsService.route(request);
A directions tutorial is available.
Alternative 2
Proxy the directions endpoint through your own server and add CORS headers.

are you using cors on your endpoint?
you should allow your endpoint to respond to a different origin (this is why is Cross Origin, because it is different from the location of your endpoint).
here you can find a good/simple example of using cors policy in nodejs

Related

HTTP request working from Postman and Node but not React

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
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.
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.

How does CORS work in AWS, NodeJs and other web services? [duplicate]

This question already has answers here:
How does the 'Access-Control-Allow-Origin' header work?
(19 answers)
Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?
(36 answers)
Closed 4 years ago.
I was working on a AWS project, and noticed that in order to allow CORS, my seniors had set headers property Access-Control-Allow-Origin, in response. So whole response was like:
module. exports.handlerFunction = async ( event, content) => {
// code here for DB calls and other logic
return {
headers: {
"Access-Content-Allow-Origin" : "*"
},
statusCode: 200,
body: result
};
}
And my thoughts were, how is it even working and allowing CORS?. What if we didn't wanted origin to perform any operations and had done "Access-Content-Allow-Origin": "https://example.com".
Since we are setting this in response, so the origin which was not supposed to do anything and just return, now have done everything and then responded with error of CORS. I asked my seniors, "How this is working and How CORS work?", the response was its browser property and browser send a pre-flight request and check for CORS. But we check for CORS at end, once every thing is done, how did pre-flight request skipped all our checks DB and API call and just landed at end and check for response headers. They had no answer and said same pre-flight concept. I asked next question "browser have pre-flight concept to check for CORS, what about postman, cUrl requests and then API call via various programs like node-fetch, request, https API call module in NodeJs do they also make pre flight call".
Also when I was creating my NodeJs express Server application, I used cors.js a NPM module. With that I checked for CORS before entering into any API function, on entry of every call and only allowed permitted source to enter. Code is like:
const CORS = require('cors'),
express = require('express');
const app = express();
let allowedOrigin = ['https://example.com'];
let corsOps = {
origin: (origin, cb) => {
if (allowedOrigin.includes(origin))
cb(null, true);
else
cb(new Error('Not allowed'));
}
};
app.use(CORS(corsOps));
This checked before calling any function and not on response.
I searched a lot about this behavior and have seen multiple examples of using CORS in headers, How does it even work in headers?.
For me it's my backend that stops call and check who is calling backend API.
How can someone who is making requests set property in headers and backend open its access to anyone, just by seeing headers property and not checking source that called?
When browsers execute an AJAX call, they first check if the target origin matches the current origin a.k.a. window.location.origin. If not, they check if the target origin accept window.location.origin as source of a CORS request. If not, the browser shows the infamous No 'Access-Control-Allow-Origin' header is present on the requested resource. error message.
If the request can be made, the browser includes the Origin header in the HTTP request with the value of window.location.origin.
There are multiple ways to set up the CORS policy in the backend:
Accept all origins with the wildcard
Only accept a preconfigured set of origins
Dynamically take the value of the Origin header as the value of the "Access-Content-Allow-Origin on a request by request basis
In the last case, the origin may be obviously spoofed like so:
curl -H"Origin: https://www.example.com" https://api.example.com/some/path
Whether or not a server should serve a request should not obviously depend on the Origin header but on other factors such as an Authorization header or an appropriate authorization Cookie; validity of request parameters etc.

Juptyer Kernel Gateway CROS

I have a Jupyter KernelGatewayApp running on a VM instance in Google cloud. I defined an API which responds to a GET statement.
If I combine the ip address of the VM instance with the port of the Kernel Gateway and the GET statement with the right parameters, I get the desired result.
However, I would like to call the API using a javascript button on another site. This doesn't work as the browser is first sending an OPTIONS statement which I don't manage to respond to correctly.
Concretely, I have the following:
Running Jupyter Kernel Gateway on port 8888 : 33.44.567.789:8888
Working API : 33.44.567.789:8888/api?fname=john&lname=doe
Other website where a javascript button calls the above API : johndoe.me
Returns following error on requestor (browser) side:
XMLHttpRequest cannot load
http://33.44.567.789:8888/api?fname=john&lname=doe. Request header
field Content-type is not allowed by Access-Control-Allow-Headers in
preflight response.
Generates following message on server:
INFO:tornado.access:200 OPTIONS /api?fname=john&lname=doe
(xx.xxx.xx.xxx) 1.2 ms
Because of the thing I read here I added all different sort of parameters when launching the Jupyter Kernel Gateway:
jupyter kernelgateway --KernelGatewayApp.api='kernel_gateway.notebook_http' --KernelGatewayApp.seed_uri='/home/dummy_gmail_com/code/test_api.ipynb' --KernelGatewayApp.allow_origin='http://johndoe.me' --KernelGatewayApp.allow_methods='GET,OPTIONS,POST' --KernelGatewayApp.allow_credentials='true' --KernelGateway.allow_headers='Origin, X-Requested-With, Content-Type, Accept, content-type' --KernelGatewayApp.expose_headers='Origin, X-Requested-With, Content-Type,Accept' --KernelGatewayApp.answer_yes=True
The issue seems to be that the browser is issuing an OPTIONS instead of a GET but I'm not really sure. Is this linked to the cross origin fact ? Is there a way to handle this correctly or a way around this ?
Having had the same problem I found that CORS requests served by Jupyter Kernel Gateway worked with the following parameters:
jupyter kernelgateway --KernelGatewayApp.api='kernel_gateway.notebook_http' --KernelGatewayApp.seed_uri='/home/dummy_gmail_com/code/test_api.ipynb' --KernelGatewayApp.allow_origin='http://johndoe.me'
(Using OP's domains etc. for consistency.)
The parameter KernelGatewayApp.allow_origin='*' also worked/Solved the problem.
However I did go wrong prompted by the initial browser console error message which ran:
Access to fetch at 'http://localhost:8889/convert?degrees=164' from origin
'http://localhost:3000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to
fetch the resource with CORS disabled.
This prompted me to set the request mode to 'no-cors'. The effect of this is to prevent an Origin header being sent with the request. Under these circumstances no amount of tinkering with the kernel gateway parameters resulted in a successful response.
Also when specifying the Allow Origin header as anything other than '*' it needs to be an exact string match to the 'Origin' header. For example:
Allow-Origin: http://127.0.0.1 does not match Origin: http://localhost
Allow-Origin: localhost does not match Origin: http://localhost
Allow-Origin: http://localhost/ does not match Origin: http://localhost
Allow-Origin: http://localhost does not match Origin: http://localhost:3000
etc.
as you can see with service /siteupdate, you can allow origin on a per service basis

Why do I get CORS error when I make axios request based on DynDNS url name and not with IP?

I have a Raspberry with Node.js running on it. CORS is installed and configured. I've set up port forwarding but because the IP keeps changing, I registered at a DynDNS provider.
I use React.js with Axios for API requests on localhost:8080.
Interestingly, if I base my requests on (e.g.)
const ROOT_URL = 'http://81.23.563.80:5000/';
which keeps changing every 24h, then the CORS module is doing it's job and I can perform my requests. But if I want do it right and make my request to
const ROOT_URL = 'http://mydyndnsurl.provider.com/';
then I would get he typical error message:
XMLHttpRequest cannot load http://mydyndnsurl.provider.com/. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access.
Does anyone have a solution for this issue?
You still need to include the port number in ROOT_URL:
const ROOT_URL = 'http://mydyndnsurl.provider.com:5000/';

Specific Endpoint: Access Denied (no Access-Control-Allow-Origin header) on API Request in React/Redux App with Node

I cannot access a custom heroku API endpoint in a react/redux app. The heroku endpoint loads up fine in a browser, and I can see the network request coming back on my console, so it seems something in my app itself is preventing it from returning.
It's the classic 'No 'Access-Control-Allow-Origin' header is present on the requested resource' error, but what's perplexing is that I can use another endpoint (such as openweather's API) and don't get this error.
Do I need to use Express or other middleware to deal with Heroku specifically? I'm creating a store with ReduxPromise middleware right now and am using Axios to make the request. Or is there a server side configuration on Heroku that might change this?
Thanks for your help, pretty new to react/redux/node =)
This is a CORS issue, you need to add a cors configuration into your API request and accept the origins on the server side using access-control-allow-origin header and accepting the referrer url of the request.
These are my configuration options when using redux-api to make the requests, look at the documentation for your specific framework:
export const baseOptions = {
mode: 'cors',
credentials: 'include',
headers
}

Resources