This is my first question posed, so I apologize if it is not formatted well.
I have been trying to figure out how to deal with the following CORS error, as well as, the CORS preflight error:
...has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://example.com/' that is not equal to the supplied origin.
I have spent the last few days reading every question on this topic, and all other documentation I could find on CORS/Cloud Functions/Axios/etc. I am using React, Node, Express, Axios, Google Firebase to host, and Google Cloud Functions.
I am trying to access the PayPal API to gain a bearer token to make further API requests. I understand some of the code pertaining to the request headers might be redundant. I have just been trying to throw anything at this.
Does anyone have any ideas?
The node file - index.js
const axios = require("axios");
const express = require("express");
const cors = require("cors")({ origin: true });
const app = express();
app.use(cors());
app.use(express.json());
app.post("/v1/oauth2/token/", cors(), (req, res) => {
res.set("Access-Control-Allow-Origin", "https://example.com/");
var data = qs.stringify({
grant_type: "client_credentials",
});
var config = {
method: "post",
url: "https://api-m.sandbox.paypal.com/v1/oauth2/token/",
headers: {
"Access-Control-Allow-Origin": "https://example.com/",
Authorization:"xyz",
"Content-Type": "application/x-www-form-urlencoded",
},
data: data,
};
axios(config)
.then(function (response) {
let bearerToken = response.data.access_token;
res.status(201).send(bearerToken);
})
.catch(function (error) {
console.log(error);
});
});
exports.api = functions.https.onRequest(app);
The react file - payment.js
import axios from "../axios/axios";
const oneTimePaypalPayment = async () => {
const response = await axios.post("/v2/checkout/orders");
console.log(response);
};
The axios file - axios.js
import axios from "axios";
const instance = axios.create({
headers: {
"Access-Control-Allow-Origin": "https://example.com/",
"Access-Control-Allow-Headers": "https://example.com/",
},
baseURL: "https://us-central1-example.cloudfunctions.net/api/"
});
export default instance;
What I have tried
I have tried using the wildcard " * " just to try to get it to work but no luck. I read on another answer that Google Cloud Functions do not recognize the '*' anyways. I have also tried all of the code below, and a lot of other ways to manipulate the Access-Control-Allow-Origin on the request header
const allowCrossDomain = function (req, res, next) {
res.header("Access-Control-Allow-Headers", "*");
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
next();
};
app.use(allowCrossDomain);
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-Headers", "*");
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
next();
});
const corsOptions = {
origin: "https://example.com/",
};
app.use(cors(corsOptions))
app.options("/v1/oauth2/token", cors(corsOptions));
app.use(cors({origin:true}));
Any thoughts would be greatly appreciated
In a cloud function that you are exporting or working with use below for Origin :
exports.your function = async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
Related
This is my node,js API,that works with no problems using postman, but when I try to make a request from a different origin like a react project the request is blocked
const express = require('express');
const mongoose = require('mongoose');
const app = express();
const port = process.env.PORT || 9000;
const routes = require('./routes/routes');
const token = require('./config/config');
const cors = require('cors')
app.use(cors())
app.use(express.json());
app.use('/api', routes);
app.listen(port, () => console.log('server listening on port', port));
const url = "mongodb://localhost/titles_db";
mongoose.connect(url,{})
.then( () => console.log('DB connected'))
.catch( (e) => console.log('Erorr on db connection'));
and this is the function that is called on my request
searchTitles = (req, res) => {
const terms = req.query.terms;
const format = req.query.format;
titleSchema.find({title: {$regex:terms, $options: 'i'}})
.then( data => {
if(format == 'json')
res.json(data);
else{
res.setHeader("Content-Type", "text/plain");
res.send(data);
}
})
.catch( error => res.json( {message: error}))
}
and here is the function that makes the request on the frontend
const getFieldText = e => {
setTerm({term: e.target.value });
const url = `http://localhost:9000/api/titles/?terms=${e.target.value}&format=json`
fetch(url)
.then(response => console.log(response))
.then(data => console.log(data));
}
even including cors library on node
const cors = require('cors')
app.use(cors())
I get this response
Response { type: "cors", url: "http://localhost:9000/api/titles/?terms=aaaaaa&format=json", redirected: false, status: 403, ok: false, statusText: "Forbidden", headers: Headers, body: ReadableStream, bodyUsed: false }
I added an options array but I have the same result
var corsOptions = {
origin: 'http://localhost:3000',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions))
configure the cross headers like this (in your server node config):
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', "http://localhost:8080");
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, authorization, Access-Control-Allow-Origin');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', 'true');
// Pass to next layer of middleware
next();
});
Tried everything I could find on here in regards to setting up cors for my node server. Tried aliasing my localhost and that doesn't seem to work either. Also tried using the CORS unblock extension.
error: localhost/:1 Access to fetch at
'http://localhost:8080/api/login' from origin 'http://localhost:3000'
has been blocked by CORS policy: Response to preflight request doesn't
pass access control check: It does not have HTTP ok status.
:8080/api/login:1 Failed to load resource: net::ERR_FAILED
im trying to use magic link authentication in my react app. I got this POST request being made to my node server
const res = await fetch(`http://localhost:8080/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + didToken,
},
});
my server code is
const express = require("express");
const cors = require("cors");
const { Magic } = require('#magic-sdk/admin');
require('dotenv').config();
const app = express()
const magic = new Magic(process.env.MAGIC_SECRET_KEY);
app.use("*", (req, res) => res.status(404).json({ error: "not found" }));
// Allow requests from client-side
app.use(cors({origin: process.env.CLIENT_URL}));
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Authorization'
);
res.sendStatus(200);
next();
});
app.post('api/login', async (req, res) => {
console.log("login fired")
try {
const didToken = req.headers.authorization.substr(7);
await magic.token.validate(didToken);
res.status(200).json({ authenticated: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = app
app.use(cors({origin: process.env.CLIENT_URL}));
I'd be curious what this URL is. If you want an open CORS policy you don't need to set anything any there.
Put a "/" in front of this route
app.post('/api/login', async (req, res) => {
I was able to reproduce your problem locally and this server setup worked for me to fix it.
const express = require("express");
const cors = require("cors");
const port = 8080;
const app = express();
app.use(cors());
app.post("/api/login", async (req, res) => {
console.log("login fired");
try {
res.status(200).json({ authenticated: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
module.exports = app;
I'm using Typescript Fetch wrapper to do post and get requests and getting empty object on post(get works fine) (Before I used Vanilla Js and all worked fine)
Nodejs:
const express = require('express');
const fs = require('fs');
const app = express();
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
next();
});
app.use(express.json());
app.post('/login', (req, res) => {
let isLogged = login(req.body);
console.log(req.body);
res.status(200).json(isLogged);
});
My Typescript fetch Wrapper:
async function fetchWrapper<T>(path: string, config: RequestInit): Promise<T> {
const request = new Request(path, config);
const response = await fetch(request);
if (!response.ok) {
throw new Error(
`name: ${response.status}, message: ${response.statusText}`
);
}
// return empty object
return response.json().catch(() => ({}));
}
export async function post<T, U>(
path: string,
body: T,
config?: RequestInit
): Promise<U> {
const init = { method: 'post', body: JSON.stringify(body), ...config };
return await fetchWrapper<U>(path, init);
}
my post request:
const res = await fetch.post(`${url}/login`, {
body: inputData,
headers: { 'Content-Type': 'application/json' },
});
input data is not empty
The problem here that you are using wrong Content-Type header value. express.json parses application/json content type, while you are sending application/x-www-form-urlencoded. The solution is either to change the content-type you are sending, or add another middleware like bodyparser to parse application/x-www-form-urlencoded body.
I was having trouble sending post requests with content-type application/json to my backend due to cors restrictions.
I've started using 'cors' module and also enabled pre-flight requests for these routes.
My requests will now be answered and processed properly but I'll still get the following error on my console, which I'm not sure if it has side effects I'm not aware of.
Method OPTIONS is not allowed by Access-Control-Allow-Methods in
preflight response.
OPTIONS https://example.com/api/postRequest
net::ERR_FAILED
const cors = require('cors');
const corsOptions = {
origin: 'https://example.com',
optionsSuccessStatus: 200,
};
app.options('/api/postRequest', cors(corsOptions), function (req, res, next) {
res.header("Access-Control-Allow-Methods", "*");
res.setHeader('Content-Type', 'application/json');
next()
})
app.post('/api/postRequest', cors(corsOptions), async (req, res) => {
res.header("Access-Control-Allow-Methods", "*");
res.setHeader('Content-Type', 'application/json');
//do other stuff and send response
}
To enable all http methods, use:
const corsOptions = {
origin: 'https://example.com',
optionsSuccessStatus: 200,
methods: '*'
};
I have a backend server writen in NodeJS, which use express.
I have the latest Angular as frontend, I post a data (GPG file) to the nodeJS server, and I try to get that data in NodeJS code, and print it out in the server console, but all I get is an empty object.
All I want to do is to either pass the Blob data to node server, or to pass a plain text to node server, and read it from node code.
const express = require('express'),
app = express(),
port = process.env.PORT || 3000;
app.listen(port);
const cors = require('cors');
app.use(cors());
//create a cors middleware
app.use(function (req, res, next) {
//set headers to allow cross origin request.
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.post('/decrypt', (res, req) => {
// Here I try to access the data that I passed by POST method
console.log(res.body);
return 'data back';
})
This is my Angular Code:
import { Injectable, Input } from '#angular/core';
import { HttpClient, HttpResponse, HttpHeaders } from '#angular/common/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import { Observable } from 'rxjs/Observable';
import { saveAs } from 'file-saver/FileSaver';
#Injectable()
export class DatabaseService {
private API_GET_LIST_FILES = 'http://localhost:3000/files';
private API_GET_FILE = 'http://localhost:3000/download?name=';
private BASE_URL = 'http://localhost:3000/';
constructor(private http: HttpClient) { }
getFile(key: string) {
return this.http.get(this.API_GET_FILE + key, {
responseType: 'blob'
})
.map(res => {
return {
filename: key.split('/').pop(),
data: res
};
})
.subscribe(res => {
console.log('start download:', res);
// no matter what I pass here to the decrypt function, I can't get it in nodeJS server
this.decrypt(res.filename)
.subscribe(
next => console.log(next)
);
saveAs(res.data, res.filename);
}, error => {
console.log('download error:', JSON.stringify(error));
}, () => {
console.log('Completed file download.');
});
}
decrypt(res): Observable<any> {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/octet-stream'
})
};
return this.http.post(this.BASE_URL + 'decrypt', res, httpOptions);
}
}
If I pass the ***res*** to the decrypt function, I will get a lot info but looks weird to me.
Do what Anand suggested, set headers to application/json (or just skip httpOptions completely as that is default) and send {name: res}. Request body then should be just that.
For file upload you should use Express middleware like Multer or Multiparty. On Angular side for example ng2-file-upload.
Express method callback signature is (req, res, next) not (res, req), it is confusing when reading your code :(
And if you just return from callback, it will hang until http request times out (I think). You should do res.status(200).end() or res.json({done: true}) or anything similar.