I have a local NodeJS app that uses Axios to connect to a local server and now I am trying to hit the deployed server. The call looks like:
let apiCall = "https://api.myproduct.io/api/all";
console.log("making axios call to " + apiCall);
const resp = await axios
.get(
apiCall,
{
headers: {
APIKey: config.API_KEY,
'Access-Control-Allow-Origin': '*'
},
},
{ crossdomain: true, credentials: "same-origin" }
)
.then((response) => {
console.log("axios response " + response);
return response;
})
.catch((e) => {
return e;
});
return resp;
}
And I have this in my vue.config.js:
module.exports = {
devServer: {
proxy: "https://api.myproduct.io",
},
transpileDependencies: ["vuetify"],
};
However, when I deploy this to AWS and try to GET it via Axios I get:
making axios call to https://api.myproduct.io/api/all
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at https://api.myproduct.io/api/all. (Reason:
CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 301.
These calls work from Postman and my mobile app - it's only from my local environment that this fails.
If I'm following correctly, you have deployed a website hosted at domain x.y and are trying to call an API at api.myproduct.io? If that's the case, the CORS headers that api.myproduct.io are responding with are what's causing the issue. You should either change the CORS headers in api.myproduct.io if you have control over that service. Or use a proxy of sorts to call it from a server you do control that can be registered to the same domain.
In short:
x.y calls to api.myproduct.io ❌
x.y calls to x.y/products ✅ (then service that handles /products calls api.myproducts.io)
You can learn more about CORS here
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Related
Variations of my issue have been asked dozens of times, but nothing I’ve tried worked.
My Node.js API works as expected on localhost for GET and POST requests.
On my EC2/nginx instance, the server block was correctly configured and served a static index over https.
Then I configured this server block as a proxy to my API port (8000) and it also returns that the API is listening.
Then I ran a simple GET /index endpoint that is routed and works correctly.
However, a similar POST /checkEmail endpoint (which, remember, works on localhost) here times out with a 504 error. This is where it gets weird.
It won’t even console.log the payload, the first line of the function. That means it isn’t even routed correctly. Here are my routes:
const API = require("./controllers/api");
module.exports = [
{ method: 'POST', path: '/checkEmail', options: API.checkEmail },
{ method: 'POST', path: '/sendEmail', options: API.sendEmail },
{ method: 'POST', path: '/recaptcha', options: API.recaptcha },
{ method: 'GET', path: '/index', options: API.index }
]
Since that the GET request returns the expected response, then it means all of these are true:
The firewall rules are letting traffic through the 443 (HTTPS) port
That traffic is proxied through the 8000 (API) port
The server block is serving the files correctly
The ssl certificate works
The request is sent to the upstream server
The endpoints:
const axios = require("axios");
const env = require("dotenv").config();
const AWS = require("aws-sdk");
const subscriber = require("./middleware/subscriber");
const sanitizer = require("./middleware/sanitizer");
exports.index = {
cors: {
origin: ["*"]
},
handler: (request, h) => {
return "API Fluente";
}
}
exports.checkEmail = {
cors: {
origin: ["*"]
},
handler: async (request, h) => {
// This is never logged:
console.log(request.payload);
const payload = request.payload;
const email = await sanitizer.email(payload.email);
if(!email) throw new Error("Invalid email");
const response = await verifyEmail(email);
console.log("checkEmail attempt:", email, response.data)
if (response.status === 200) {
return h.response(response.data).code(200);
}
}
}
What I’ve tried:
Minimal/Full server block conf settings
curl the POST request from the EC2 CLI
Change ports from 8000 to something else (eg 8003)
Increasing timeout duration doesn’t make sense because these are simple requests that should return a response in a very short time.
None of these made any difference.
Found the problem! As it turns out, it had nothing to do with AWS services or Node.
I was using the deprecated hapi package, when I should have been using #hapi/hapi.
Yes, I have the dreaded CORS issue (or, at least it appears so)....and I have searched and tried a few solutions, to no avail...
I have no problems using firebase emulator and running my function locally, but when I deploy the function and try to send a POST request using fetch() on the local host client-side app, I get the following CORs browser console error (it won't even get to the server logs):
Access to fetch at 'https://us-central1-XXXX.cloudfunctions.net/deleteAsset' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The FetchEvent for "https://us-central1-XXXXX.cloudfunctions.net/deleteAsset" resulted in a network error response: the promise was rejected.
Promise.then (async)
(anonymous) # firebase-auth-sw.js:77
firebase-auth-sw.js:77
Uncaught (in promise) TypeError: Failed to fetch
at firebase-auth-sw.js:77
Here's my client side fetch request:
UploadImage.vue:
async deleteFile() {
await fetch(
'https://us-central1-XXXX.cloudfunctions.net/deleteAsset',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
public_id: this.imageSrc.public_id
})
}
)
}
And then my firebase cloud function is like so:
/deleteAsset.js:
import cloudinary from 'cloudinary'
import * as functions from 'firebase-functions'
const cors = require('cors')({ origin: true })
cloudinary.config({
cloud_name: functions.config().cloudinary.cloud_name,
api_key: functions.config().cloudinary.api_key,
api_secret: functions.config().cloudinary.api_secret,
secure: true
})
export const deleteAsset = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
try {
functions.logger.log(req.body)
cloudinary.v2.uploader.destroy(
req.body.public_id,
{
invalidate: true
},
(error, result) => {
if (error) {
res.status(500).send(error)
}
res.status(200).send(result)
}
)
} catch (error) {
res.status(500).send('There was an error in deleteAsset function')
}
})
})
Anyone spot any issues or have any advice on how I can further troubleshoot this?
Alright, so I fixed it....the CORS issue was due to the cloud function not having the right IAM permissions. I suspected it after looking at my dashboard (not shown on firebase console, had to go to Cloud Functions over at Google Cloud to see it!) and noticed that it was missing "Allow Unauthenticated". So, I manually deleted the function and redeployed. All good now!
In your cors's options, you're not setting localhost as a valid source.
Change the code from this
export const deleteAsset = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
try {
to this
export const deleteAsset = functions.https.onRequest((req, res) => {
return cors({origin: 'http://localhost:3000'}, req, res, () => {
try {
You should add all the origins you'll use, not only localhost. For furhter details on how to specify the allowed origins, take a look at the documentation.
I am making a simple full-stack project wherein I have a very basic REST API built using NodeJS and Express. It is a server-side rendered website.
While trying to login, when I send a POST request to the login endpoint, I am getting the following error in the console
Access to XMLHttpRequest at 'http://127.0.0.1:3000/api/v1/users/login'
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.
This is the code snippet that sends the POST request
const login = async (email,password) => {
try {
const res = await axios({
method: 'POST',
url: 'http://127.0.0.1:3000/api/v1/users/login',
data: {
email,
password
}
});
if(res.data.status === 'success') {
alert('Logged in successfully');
window.setTimeout(() => {
location.assign('/');
}, 1500);
}
}
catch(err) {
console.log(err);
}
}
Please explain as to why I'm getting that error and how to fix it. The API is not deployed yet and it's running on localhost
Your request origin is:
http://localhost:3000
This is not the same as the domain you're sending the request to:
http://127.0.0.1:3000
To avoid this CORS error, the FQDN must be the same, including hostname and port. (Or you could configure your server for CORS.) As far as the server is concerned, the different host means a completely separate entity, even if it's the same machine.
Change your url to:
url: 'http://localhost:3000/api/v1/users/login',
could you add Access-Control-Allow-Origin header to the request and see ,
const login = async (email,password) => {
try {
const res = await axios({
method: 'POST',
url: 'http://127.0.0.1:3000/api/v1/users/login',
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
data: {
email,
password
}
});
if(res.data.status === 'success') {
alert('Logged in successfully');
window.setTimeout(() => {
location.assign('/');
}, 1500);
}
}
catch(err) {
console.log(err);
}
}
I've deployed my Node.js Koa API app on Heroku. I'm making a call to a USAJOB.gov API and it works on my localhost. However, on Heroku I get this error:
You don't have permission to access "http://data.usajobs.gov/$(SERVE_403)/api/search?" on this server.
Here is my Koa router snippet:
router.get('/jobs', async (ctx) => {
const { keyword, location } = ctx.query;
const url = `https://data.usajobs.gov/api/search?
Keyword=${keyword}&LocationName=${location}`;
const host = 'data.usajobs.gov';
const userAgent = '<redacted>';
const authKey = '<redacted>';
const headers = {
Host: host,
'User-Agent': userAgent,
'Authorization-Key': authKey,
};
await fetch(url, {
headers,
method: 'GET',
});
}
So it turns out that whether or not cross-site Access-Control requests should be made using credentials was what was the issue. I wasn't able to figure out how to configure that with node-fetch but once I read the the solution (link below), switched to axios, configured withCredentials: true, and added headers: { 'X-Requested-With': 'XMLHttpRequest' }, then it all worked.
Source: axios delete method gives 403
I've created a node.js microservice using the libraries express and request.
The idea is to forward a post-request by the client application from the first endpoint ("http://example.com/proxy/x") to the second one ("http://example.com/x"). If I do the request to /proxy/x via POSTMAN locally to my "localhost:/proxy/x" instance, it works just fine. The request gets the expected response without any problem. If I push it to the cloud environment using a containering system and ask the endpoint via "http://example.com/proxy/x" it says "CANNOT /GET /x" and fails with a 404 error.
To cover the "404" error, I've even created a app.get listener on "/proxy/x".
Now it returns "error = true". That means my POST is turned to a GET call.
The url is already hardcoded, before I've generated it with a combination of req.protocol and req.get('host'). Seems to generate a valid url, still does not work.
POST http://example.com/proxy/target
app.post('/proxy/target', (req, res) => {
// const currentURL = req.protocol + '://' + req.get('host');
const requestBody = req.body;
request.post({
url: 'http://example.com/target',
followAllRedirects: true, // I've also tried to outcomment it
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic user:password',
},
form: requestBody
},
(err, response, body) => {
if (err) {
return console.log(err);
} else {
// console.dir(response);
return res.status(response.statusCode).send(response.body);
}
}
);
});
app.route('/target')
.post(function (req, res) {
//some code
});
Expected result should be the response body the call delivers, if I'm callign /target directly.
{
'success':true
}
It actually is responding with:
Cannot GET /x/
As it works perfectly on my localhost, it might be the case that my cloud dev environment is the problem.
I would be glad for additional ideas. The cloudsystem is a containering environment called CloudFoundry.
Update:
It seems like CloudFoundry is using NGINX per default: https://docs.cloudfoundry.org/buildpacks/nginx/index.html
Other developers had similar issues when using NGINX: https://www.digitalocean.com/community/questions/post-request-redirects-to-get-in-nginx-proxy-and-nodejs