Express + Passport + CORS: session allowed for multiple domains - javascript

I am trying to make the frontend (React JS) work with my backend server (Express JS). I am still fighting with CORS. The frontend requests are still blocked by CORS.
According to CORS documentation I have set my Express instance to use cors() as middleware:
const app = express();
// Middlewares
const whitelist = [
'http://localhost:3000',
'http://localhost:3001'
];
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
app.use(cors(corsOptions));
If someone asks why am I using CORS with localhost at all, is because I was told to do so since I had to send withCredentials: true header from axios requests to persist session after login.
I just added axios.defaults.withCredentials = true to intercept requests in the frontend.
The way it was working before adding more domains to corsOptions was setting up a middlewares to let the server work with the frontend:
export const setHeaders = (req, res, next) => {
res.header('Access-Control-Allow-Origin', process.env.APP_URL);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
};
...
app.use(setHeaders);
If I remove the previous code, it won't work even for one domain.
So, what I have to change in order to let the server fetch data from multiple domains? Thanks in advance.

Add credentials and allowedHeaders options to your corsOptions config
credentials: true,
allowedHeaders: ['Origin, X-Requested-With, Content-Type, Accept'],
Read https://www.npmjs.com/package/cors#configuration-options
Also, you can whitelist domains with 127.0.0.1 as you might want to access them via localhost or 127.0.0.1
Full code
const app = express();
// Middlewares
const whitelist = [
'http://localhost:3000',
'http://127.0.0.1:3000'.
'http://localhost:3001',
'http://127.0.0.1:3001',
];
const corsOptions = {
credentials: true,
allowedHeaders: ['Origin, X-Requested-With, Content-Type, Accept'],
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
app.use(cors(corsOptions));

Related

Have an error with cors, if my cors is enable?

I want to do a request get or post to my firebase backend api
this is my request
const verify = async (address) => {
const params = { address }
const { data } = await client.get('test', { params })
}
and my backend i am using cors
const app = require('express')()
const cors = require('cors')
const options = {
origin: true,
methods: 'GET,POST,PUT,DELETE,OPTIONS,HEAD,PATCH',
preflightContinue: false,
optionsSuccessStatus: 204,
}
app.use(cors(options))
app.get('/test', (req, res) => {
console.log('console.log', req.body)
res.status(200).send('hello world!')
})
module.exports = {
Module: () => app,
}
I don't know what happened, but I am getting an error in cors:
Access to XMLHttpRequest at 'https://...functions.net/test?address=AABB' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I suspect you need to insert the URL, on which your Vue App is running into options.origin
Or does options.origin: true set headers to '"Access-Control-Allow-Origin": *'?
EDIT: First, you need req.query.address in order to access the address query param, that you sent from Vue.
EDIT: Without setting your options to cors() I get the Access-Control-Allow-Origin: * Response Header from Express. Might not want to do this in production, but set origin to some URL that use the API.
const app = require('express')()
const cors = require('cors')
app.use(cors())
app.get('/test', (req, res) => {
console.log('console.log', req.query.address)
res.status(200).send(`The address is ${req.query.address || "not existing"}`)
})
app.listen(3000, () => console.log("Express running..."))
I use this. Find this solution on Stackoverflow :
//#region CORS - Area...
var whitelist = [
"http://127.0.0.1:3000",
"https://127.0.0.1:3000",
"https://localhost:3000",
"http://localhost:3000",
];
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
console.log("Blocked-Origin:", origin);
callback(new Error("Not in CORS-Origin-List"));
}
},
methods: ["POST, DELETE, GET"],
optionsSuccessStatus: 200,
};
app.use(cors(corsOptions));
//#endregion CORS - Area...
And this solution is not recommended, but for Test ok...
app.use(
cors({
optionsSuccessStatus: 200,
credentials: true,
origin: "*",
methods: ["GET", "POST", "DELETE"],
})
);
Note: origin: "*", not true

How to Form Authentication Header for Axios Request to Node.js App Using Passport Local Authentication?

I have a node.js app and am developing a separate single page app (that will eventually be converted into Android and iOS native apps). I'm setting up an API on the node.js app and am struggling with authentication. The node.js app is using passport-local-mongoose for authentication and I store user data in a MongoDB backend. For testing/dev, the single page app is running on http://localhost:1234/.
My endpoint looks like:
exports.getDevicesAPI = async (req, res) => {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET, POST');
res.header('Access-Control-Allow-Headers: Authorization');
const devices = await Device.find({ owner: req.user._id });
res.json(devices);
};
I can GET this no problem with something like:
const axios = require('axios');
const url = 'http://localhost:7777/api/devices';
function getDevices() {
axios
.get(url)
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
}
I want to add authenticate = passport.authenticate('header', {session: false, failWithError: true}); on the server side to provide authentication, but the following gives me Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:7777/api/devices. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing):
const axios = require('axios');
const url = 'http://localhost:7777/api/devices';
const username = myUsername;
const password = myPassword;
const axiosConfig = {
headers: {
'Content-Type': 'application/json',
},
Authorization: {
username,
password,
},
};
function authenticate() {
axios
.post(url, axiosConfig)
.then(function(response) {
console.log('Authenticated');
})
.catch(function(error) {
console.log('Error on Authentication');
});
}
Routes (for testing):
router.get('/api/devices', catchErrors(deviceController.getDevicesAPI));
router.post('/api/devices', catchErrors(deviceController.getDevicesAPI));
What am I missing?
You are having issues with CORS(Cross-Origin Resource Sharing) Restrictions. Read more about CORS here.
I believe this part of your code is meant to handle the CORS:
exports.getDevicesAPI = async (req, res) => {
// ...
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET, POST');
res.header('Access-Control-Allow-Headers: Authorization');
// ...
};
However, the mistake here is that the setting of these CORS headers is tied to a route, i.e the getDevicesAPI route which is not supposed to be. For requests that are likely to modify resources in another origin(e.g the POST to getDevicesAPI route), the browser would first send a preflight request with the OPTIONS Http method before sending the actual request, the response to the preflight request is where the necessary CORS response-headers is expected to be set. You can find explanations on preflight requests here.
I would typically add a middleware like this above the other routes:
router.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', '*');
next();
});

Access-Control-Allow-Credentials error -- nodejs

I am getting the cors error. Am I missing anything? Below is the code which I have and the error which I am getting.
App Info:
Back-end is uploaded on lambda using serverless npm === which created api-gateway.
Mongodb is hosted on aws-ec2 instance .
Front-end/React is hosted on s3 bucket.
Thank you so much!
Access to fetch at '[node.js api-url, which is hosted on api-gateway/lambda]' from origin '[front-end react-url, which is hosted on aws-s3 bucket]' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is 'false' which must be 'true' when the request's credentials mode is 'include'.
Node.js code:
db.initialize();
initAxios(defaults);
const app = express();
if (process.env.ENV === 'production') {
app.server = https.createServer(config.sslOptions, app);
} else {
app.server = http.createServer(app);
}
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true,
}));
app.use(expressSession({
secret: process.env.JWT_SECRET_KEY,
resave: true,
saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session());
var corsOptions = {
origin: function (origin, callback) {
callback(null, true)
},
credentials: true
}
app.use(cors(corsOptions));
// I added the below part so maybe it would work but it didn't :)
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();
});
// I added the above part so maybe it would work but it didn't :)
app.use(morgan('combined', {
stream: logger.stream
}));
app.use(`/api/v${process.env.API_VERSION}`, router);
Front-end React Code:
export async function login(data) {
return fetch(`[api-url]auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
// credentials: 'same-origin',
body: JSON.stringify({
username: data.username,
password: data.password,
}),
})
.then((response) => {
return response.json()
})
.then(onSuccess)
.catch(onFail)
}
Before it was like this:
app.use(cors({
credentials: true,
origin: true,
}));
So I converted into:
app.use(cors(corsOptions));
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();
});
Thank you!
Your API endpoint is the API Gateway, not the Lambda, so you need to enable CORS on the actual Gateway.
There are multiple ways of doing this, but if you are using Serverless Framework for your deployment, there is a very good tutorial for enabling CORS here.
The quick and dirty way is just to add 'cors: true' under 'events: -http:' when you're describing your function endpoint in your serverless.yml.
Example:
events:
- http:
path: product
method: post
cors: true

Enable Cors in Gulp/BrowserSync

I've created an Angular project with yeoman's boilerplate code (generator-gulp-angular)
And now in my controller I'm trying to make a http request like this:
$http.get('http://food2fork.com/api/search?key='+key+'&page=1').then(function(response) {
vm.all = response.data;
});
But I keep getting this error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://food2fork... (Reason: CORS header 'Access-Control-Allow-Origin' missing)
I did my research and found I needed to add this Access Control to my server,
using the middleware property, which I did, but I still keep getting an error here is my server.js file
var path = require('path');
var gulp = require('gulp');
var conf = require('./conf');
var cors = require('cors');
var browserSync = require('browser-sync');
var browserSyncSpa = require('browser-sync-spa');
var util = require('util');
var proxyMiddleware = require('http-proxy-middleware');
function browserSyncInit(baseDir, browser) {
browser = browser === undefined ? 'default' : browser;
var routes = null;
if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) {
routes = {
'/bower_components': 'bower_components'
};
}
//here is where I added the middleware
var server = {
baseDir: baseDir,
middleware: function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
},
routes: routes
};
browserSync.instance = browserSync.init({
startPath: '/',
server: server,
browser: browser
});
}
browserSync.use(browserSyncSpa({
selector: '[ng-app]'// Only needed for angular apps
}));
gulp.task('serve', ['watch'], function () {
browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]);
});
gulp.task('serve:dist', ['build'], function () {
browserSyncInit(conf.paths.dist);
});
gulp.task('serve:e2e', ['inject'], function () {
browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []);
});
gulp.task('serve:e2e-dist', ['build'], function () {
browserSyncInit(conf.paths.dist, []);
});
`
But still the error persists, any help?
You are requiring http-proxy-middleware but not using it !
If You want to resolve Cross Origin for all URL containing /api, first
you should forward your Angular requests to your BrowserSync Server
So
$http.get('http://food2fork.com/api/search?key='+key+'&page=1')
Should become
$http.get('/api/search?key='+key+'&page=1')
The BrowserSync will receive the call
In Browser relay the call to the real server in the back with
target :'http://food2fork.com/'
and add Cross Origin headers when the response comes back with
changeOrigin: true,
the full config becomes :
var proxyMiddleware = require('http-proxy-middleware');
const jsonPlaceholderProxy = proxyMiddleware('/api', {
target: 'http://food2fork.com/api',
changeOrigin: true,
logLevel: 'debug'
});
module.exports = function() {
return {
server: {
middleware: [jsonPlaceholderProxy],
baseDir: baseDir
}
};
};
You should add additional information on the server side which headers and methods are allowed. For example:
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
or
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
This will tell the server to accept the headers send by the client (e.g. Origin) as well as the HTTP methods which you plan to support (e.g. GET).
For further informations on CORS see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Making CORS Request in Node.js/Express and AngularJS

I have seen many answers in stack overflow which says setting response headers will make you "CORS" request.But no solution worked for me.I have written the following code:
//Server.js Code
var express = require('express'),
app = express();
app.all('*',function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
next();
I am trying to access the content from the URL using $http in client side:
//Controller.js
$http.get('http://domainA.com/a/ipadapi.php?id=135&client=ipad').success(function(response){
alert("I got response");
});
It's showing the following error in console.
XMLHttpRequest cannot load http://domainA.com/a/ipadapi.php?id=135&client=ipad The 'Access-Control-Allow-Origin' header has a value 'http://example.xxxxx.com' that is not equal to the supplied origin. Origin 'http://localhost:3000' is therefore not allowed access.
Note:I am new to nodeJS,Express and AngularJs
When you are passing credentials with CORS, you need to lock down the accepted origins. Try changing your origins from * to "localhost:3000"
See cross origin resource sharing with credentials
Change the header info from
res.setHeader("Access-Control-Allow-Origin", "*");
TO
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
If you're not the owner of domainA then you cannot send CORS headers from that domain. You can use your Node server as middleware, and proxy the request from your server to domainA. Your server can send CORS headers back to your angular app. pseudo code with hapi and needle:
import Hapi from 'hapi'
import needle from 'needle'
const server = new Hapi.Server()
server.connection({
port: 9090
, routes: {
cors: true
}
})
const handler = (req, reply) => {
const url = 'https://domainA.com'
, data = {
body: 'code'
}
needle.post(url, 'body=${data.body}', function(err, res) {
let json = JSON.parse(res.body)
reply(json.data)
})
}
server.route({
method: 'GET',
path: '/route/{id}',
handler: handler
}
)
server.start( err => {
if( err ) {
console.error( 'Error was handled!' )
console.error( err )
}
console.log( 'Server started at ${ server.info.uri }' )
})

Categories