Angularjs CORS trouble with Node, Express, Oauth2, and Passport - angularjs

Recently we have decided to switch our front-end from EJS to Angular separate the frontend and the backend completely. In doing so, we started to run into several issues across multiple browsers. On the back end we are using Node with express along with passport and oauth2. For the front end we are attempting to use angular. EJS works using express.render, but we would prefer to use angular directly by utilizing express as just a RESTful API.
I'm running both the backend and frontend locally at localhost:8080 and localhost:3000, respectfully. When just working with the backend (USING EJS, NOT ANGULAR), I can successfully go to our backend port in the browser, login via passport-oauth, and be redirect to the account page (from the providers login screen) where my json data is rendered via res.json. The problem is I am unable to do this from the frontend UI after removing EJS.
I've tried configuring CORS a dozen different ways while using three different browsers with no luck. The following three snippets are the errors I'm getting in the browsers console while trying to access localhost:8080 from the frontend via $http and $resource (see below for the code). The image below the three code snippets is what the node console is telling me when trying to access port 8080 from each different browser...
Chrome:
XMLHttpRequest cannot load 'PROVIDER-DETAILS-URL'. No 'Access-Control-Allow- Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
Firefox:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at 'PROVIDER-DETAILS-URL'. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at 'PROVIDER-DETAILS-URL'. (Reason: CORS request failed).
Safari:
XMLHttpRequest cannot load http://localhost:8080/auth/PROVIDER. Request header field Accept-Encoding is not allowed by Access-Control-Allow-Headers.
Console Image:
And the code:
Server:
app.js
'use strict';
const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const logger = require('morgan');
const errorHandler = require('errorhandler');
const path = require('path');
const flash = require('connect-flash');
const passport = require('passport');
const expressValidator = require('express-validator');
/**
* Load environment variables, where API keys and passwords are configured.
*/
const config = require('./config/config');
/**
* Route Handlers
*/
const index = require('./routes/index');
const account = require('./routes/account');
const logout = require('./routes/logout');
/**
* API keys and Passport configuration.
*/
const passportConfig = require('./strategy');
/**
* Create Express server.
*/
const app = express();
/**
* Express configuration.
*/
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
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, Authorization");
res.header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT");
next();
});
app.use(cookieParser());
app.use(expressValidator());
app.use(session({
resave : true,
saveUninitialized : true,
secret : config.sessionSecret,
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
/**
* Primary app routes.
*/
app.get('/', index.execute);
app.get('/account', passportConfig.isAuthenticated, account);
app.get('/logout', logout.execute);
/**
* OAuth authorization routes.
*/
app.get('/auth/PROVIDER', passport.authenticate('PROVIDER'));
app.get('/auth/PROVIDER/callback', passport.authenticate('PROVIDER', { failureRedirect : '/'}), function(req, res) {
res.redirect('/account');
});
/**
* Error Handler.
*/
app.use(errorHandler());
/**
* Start Express server.
*/
app.listen(8080, () => {
console.log('App listening on port 8080');
});
module.exports = app;
strategy.js
'use strict';
const passport = require('passport');
const session = require('express-session');
const config = require('./config/config');
const OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
/**
* Put together the right header info for PROVIDER
*/
var authString = new Buffer(config.PROVIDER.clientID + ':' + config.PROVIDER.clientSecret);
var customHeader = {
"Authorization": "Basic " + authString.toString('base64')
};
/**
* OAuth2Strategy containing the customHeader created above.
*/
passport.use('PROVIDER', new OAuth2Strategy({
authorizationURL : config.PROVIDER.authorizationURL,
tokenURL : config.PROVIDER.tokenURL,
clientID : config.PROVIDER.clientID,
clientSecret : config.PROVIDER.clientSecret,
callbackURL : config.PROVIDER.callbackURL,
customHeaders : customHeader,
passReqToCallback : true
},
function( req, accessToken, refreshToken, profile, done ) {
req.session.accessToken = accessToken;
return done(null, profile);
}
));
passport.serializeUser(function(user, done) {
return done(null, user);
});
passport.deserializeUser(function(obj, done) {
return done(null, obj);
});
/**
* Login Required middleware.
*/
exports.isAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) {
console.log('isAuthenticated');
return next();
}
res.redirect('/');
};
/**
* Authorization Required middleware.
*/
exports.isAuthorized = function(req, res, next) {
var provider = req.path.split('/').slice(-1)[0];
if (_.find(req.user.tokens, { kind: provider })) {
next();
} else {
res.redirect('/auth/' + provider);
}
};
index.js
exports.execute = function (req, res) {
if (req.user) {
console.log('========== ROUTES/INDEX.JS | 3 ==========');
res.redirect('/account');
} else {
console.log('========== ROUTES/INDEX.JS | 6 ==========');
res.redirect('/auth/PROVIDER');
}
};
Client:
I combined this to make it a little easier to read.
angular.module('StackOverflowPost', [])
.factory('APIService', function() {
function getData( $q, $http ) {
var defer = $q.defer();
$http.get( 'localhost:8080' )
.success( getDataComplete )
.catch( getDataFailed );
function getDataComplete( response ) {
console.log( response.Authorization );
defer.resolve(response.data.results );
}
function getDataFailed( error ) {
console.log( error.data );
defer.reject( 'XHR Failed for getData - ' + error.data );
}
return defer.promise;
}
})
.controller('MainCtrl', function( APIService ) {
var vm = this;
vm.getDataTest = function() {
APIService.getData().then(function( returnedData ) {
console.log( returnedData );
})
}
});
Any help or direction would be greatly appreciated.
UPDATE (4/28/2016): I updated the original post with more details. I also updated the code to what it is after another week of trial and error.

Please check this
https://gist.github.com/dirkk0/5967221
Code should be
// in AngularJS (client)
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
// in Express/nodeJS
// in NodeJS/Express (server)
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "GET, POST","PUT");
next();
});

Related

CORS error occurs when HTTPS applied to the my server using Let's Encrypt

I have a Digital Ocean Linux server.
I deployed my REST API and also front end in the same server.
My Back End runs using Node.JS and front end using AngularJS.
After enabling HTTPS, I changed all the REST URLs into HTTPS.
Like this :
var mainHost = "https://xxx.xxx.xxx.xx:7000/api";
Now I'm getting this CORS error :
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://xxx.xxx.xxx.xx:7000/api/user_manage/login. (Reason: CORS request did not succeed).
I am using Node.JS CORS plugin also.
var cors = require('cors');
var app = express();
app.use(cors());
This started when I added HTTPS and I tried many things. Including changing the .htaccess and adding some meta tags. Still no good. What is the reason for this ? Why CORS error comes after adding HTTPS ? Please help me to solve this.
This is the controller call for the service layer :
sessionService.getAllSessions(100, 0).then(function (data) {
console.log(data);
$scope.allSessions = data.data;
});
And this is the service layer :
getAllSessions: function (limit, offset) {
return $http({
method: "GET",
url: host.session_manage + '/session/' + limit + '/' + offset
}).then(function (response) {
return response.data;
});
}
I'm not using headers in the service call as you can see in the above section.
This is the server code snippet :
var express = require("express");
var bodyParser = require("body-parser");
var morgan = require("morgan");
var config = require("./config");
// var cors = require('cors');
var app = express();
// app.use(cors());
// app.use(cors({
// origin: 'https://xxxxxx.com'
// }));
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.set('views', './app/views');
app.set('view engine', 'pug');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(morgan('dev'));
var session = require('./app/routes/session')(app, express);
app.use('/api/session_manage', session);
app.listen(config.port, function (err) {
if (err) {
console.log(err);
} else {
console.log("localhost:7000");
}
});
Thank you guys.
You are in production you don't want to allow CORS access for all origins but if you need to allow cross origin requests from some specified host(s) you can do add the following code:
server.use(cors({
origin: 'https://example.com'
}));
This will allow https://example.com to send cross origin requests to your Express server without the Same Origin Policy getting in the way.
You can also enable CORS for a single Express route
server.get('/endpoint', cors(), function (req, res, next) {
res.json({msg: 'This has CORS-enabled for only this route: /endpoint'})
})
If you want to allow multiple origins you need to use a function (for origin instead of a string) that dynamically set the CORS header depending on the origin making the request and a white list that you specify which contains the origin to allow.
var whitelist = ['http://example.com', 'http://othersite.com'];
var options = { origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
server.use(cors(options))

Response had HTTP status code 400 + No 'Access-Control-Allow-Origin' header is present on the requested resource. - MEAN STACK

I need to pass a string to the node and the following response in the browser console:
Failed to load http://localhost:3003/api/buscarCep: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4000' is therefore not allowed
access. The response had HTTP status code 400.
I'm using MEAN stack with Angular 1.
My server node:
const port = 3003
const bodyParser = require('body-parser')
const express = require('express')
const server = express()
const allowCors = require('./cors')
const queryParser = require('express-query-int')
server.use(bodyParser.urlencoded({ extended: true }))
server.use(bodyParser.json())
server.use(allowCors)
server.use(queryParser())
server.listen(port, function() {
console.log(`BACKEND is running on port ${port}.`)
})
module.exports = server
The server is a node and is on the same machine I'm testing. So it's all local. The CORS policies for my server are as follows:
module.exports = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization')
next()
}
Factory in angular:
(function () {
angular.module('primeiraApp').factory('apiExterna', [
'$http',
'consts',
ApiExterna
])
function ApiExterna($http, consts) {
function buscarCep(cep) {
$http.post(`http://localhost:3003/api/buscarCep`, cep)
.then(resp => {
console.log(resp)
return null
}).catch(function (resp) {
console.log("erro")
})
}
return { buscarCep }
}
})()
The CEP parameter is received from a controller. So far so good, the problem happens from now on:
Routes in backend:
const express = require('express')
const auth = require('./auth')
module.exports = function(server) {
/*
* Rotas protegidas por JWT
*/
const protectedApi = express.Router()
server.use('/api', protectedApi)
protectedApi.use(auth)
// rotas da API
const alertasService = require('../api/alertas/alertasService')
alertasService.register(protectedApi, '/alertas')
const ApiService = require('../api/apiExterna/apiService')
protectedApi.post('/buscarCep', ApiService.buscarCep)
"auth" is a module for application authentication.
Here I should receive the CEP:
const _ = require('lodash')
const fetch = require('node-fetch')
const buscarCep = (req, res, next) => {
const cep = req.body.cep
}
module.exports = { buscarCep }
The strangest thing is that for example, everything on that "alerts" route works, but in that post there is no. Am I doing it wrong?
You can use npm library cors which is much easier cors-npm
you don't need to use express-cors
const allowCors = require('./cors')

Express/Angular/Browsersync CORS - No 'Access-Control-Allow-Origin' 403 (forbidden)

I'm as junior as it gets when it comes to web development so please bear with me on this. I'm attempting to access data from the USDA Food Composition Databases NDB API - https://ndb.nal.usda.gov/ndb/doc/index
via an angular $http request from localhost. I'm using an express server and gulp/browsersync and am encountering two errors:
Failed to load resource: http://api.nal.usda.gov/ndb/list?format=json&It=f&max=20&sort=n&offset=15&api_key=API_KEY the server responded with a status of
and
XMLHttpRequest cannot load http://api.nal.usda.gov/ndb/list?format=json&It=f&max=20&sort=n&offset=15&api_key=API_KEY. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 403.
I've tried setting my CORS headers in browsersync as well as my express server but I simply cannot get around this issue. Here is how I've configured the relevant code for this:
The $http request
(function() {
'use strict';
angular
.module('commonSenseDietApp')
.factory('getFoodNamesOnly', getFoodNamesOnly);
/** #ngInject */
function getFoodNamesOnly($log, $http, devEnvironment) {
var service = {
ndbApiKey: devEnvironment.api_key,
ndbApiUrl: devEnvironment.api_url,
getFoodNamesList: getFoodNamesList
};
return service;
function getFoodNamesList(limit) {
if(!limit) {
limit = 30;
}
// For a list of all request parameters visit - https://ndb.nal.usda.gov/ndb/doc/apilist/API-LIST.md
return $http.get(service.ndbApiUrl + '/ndb/list?format=json&It=f' + '&max=' + limit + '&sort=n&offset=15&api_key=' + service.ndbApiKey)
.then(returnFoodNamesList)
.catch(getFoodNamesFail);
function returnFoodNamesList(response) {
return response.data;
}
function getFoodNamesFail(err) {
// return $log.error(err.data);
return console.log(err);
}
}
}
})();
My Browersync/Express Server
'use strict';
var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var http = require('http')
// require database data modeling via mongoose
var mongoose = require('mongoose');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var flash = require('connect-flash');
// Use express and set it up
var app = express();
app.use(cors());
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', false);
next();
});
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json())
var path = require('path');
var gulp = require('gulp');
var conf = require('./conf');
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'
};
}
var server = {
baseDir: baseDir,
routes: routes,
middleware: function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, content-type');
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', false);
next();
}
};
browserSync.instance = browserSync.init({
startPath: '/',
cors: true,
browser: browser,
notify: true,
port: 8080,
server: server,
});
}
browserSync.use(browserSyncSpa({
selector: '[ng-app]'// Only needed for angular apps
}));
gulp.task('serve', ['setenvconstants','watch'], function () {
browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]);
});
gulp.task('serve:dist', ['setenvconstants','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, []);
});
My Angular .config
(function() {
'use strict';
angular
.module('commonSenseDietApp')
.config(config);
/** #ngInject */
function config($logProvider, $httpProvider) {
// Enable log
$logProvider.debugEnabled(true);
// For Access-Control-Allow-Origin and Set-Cookie header
$httpProvider.defaults.withCredentials = false;
}
})();
I'm using gulp and browsersync to serve locally over localhost:8080 but no matter what I try (setting headers in express, setting headers in browsersync, setting browsersync cors option to 'true', setting browsersync https options to true, switching my 'Access-Control-Allow-Origin' to '*' or to "localhost:8080") none of it seems to work. I suspect the NDB API has forbidden my access but I can't get in contact with them to ask about it. Their suggested contact us link - "https://api.data.gov/contact/" leads to nothing.
Any suggestions or tips on this would be greatly appreciated. I'm a total noob here in terms of web development as well as posting to Stack Overflow so please let me know if my question doesn't make any sense and needs further clarification.
I was fortunate enough to stumble upon a solution although I don't quite understand what's happening and would certainly like to.
Turns out I was attempting to run a local server while using my VPN (https://www.privateinternetaccess.com/) which for some reasons was causing my CORS issue. Once I turned the VPN off and began using my local network I was able to run my server and make my requests without a hitch.
I'm not sure why using my VPN would cause a 403 but my guess would be that the API I was attempting to access simply does not allow request from a remote network like the one I was using. I will look into it more and update my answer shortly.
Try serving from https and not http when making your API calls. Being that you are fetching an https location, but issuing an http request, you will get CORS issue.
Look into: https://nodejs.org/api/https.html

How to enable CORS on node js?

I am having a problem that I don't really understand. I have a node js server that server simple index.html page (This is actually angular). My server code looks like this:
var express = require('express');
var app = express();
var cors = require('cors')
var port = 4000;
var path = require("path");
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(express.static('.'))
console.log(__dirname + '/.');
app.use(cors({
origin: true,
credentials: true
}));
app.get("/", function(res, req){
req.sendFile(path.join('/index.html'));
});
app.listen(port,'0.0.0.0' , function(){
console.log("listening on * "+ port);
});
I my html page, I have and angularjs service that is accessing localhost:7000 and socket.io that is accessing localhost:7000.
My service look like this :
if(param){
$scope.isloading = true;
$http.post(
'http://' + $location.host() + ':7000/container',
{ "method": "start", "nomber": param } ,
{}
).then(function(err, data){
console.log("No error : ");
console.log(data);
if (err){
console.log(err);
}
$scope.isloading = false;
console.log("end Loading" + $scope.isloading);
}, function(err, data){
$scope.isloading = false;
console.log("error ");
});
}
and the html call to socket.io is this :
<script>var socket = io.connect('http://localhost:7000');
socket.on("news", function(data){
console.log(data);
});</script>
my problem is that I am unable to allow the angular service and socket.io call at the same time. I have installed CORS on chrome. when I enable it, socket.io don't work, but service works.. When I disable it service don't work and socket.io does: . Can you please help me to find a solution ?
Update
I have tried many of the solution proposed here. But they don't work for me.
Try like this,
app.use(cors({
origin: function (origin, callback) {
var allowed = ['http://localhost:7000'].indexOf((origin || '').toLowerCase()) !== -1;
callback(null, allowed);
}
}));
Since the error message says:
A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin'
header when the credentials flag is true
Why don't you try setting your origin instead?
I would use the following middleware:
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:4000");
next();
});
This is assuming that the credentials flag is absolutely necessary.

enabling cors in meanjs rest api server

I am creating an api (server side) based on Meanjs.org latest version (0.4.0) and i managed to pull off only the MEN part and create one in http://localhost:3000/api
as the frontend part i created an Angularjs in http://localhost:4000/
and then i run both application using (P)ackage (M)anager 2
I am trying to create a user by sending user credentials using $resource like this
angular.module('users').factory('AuthenticationResource', ['$resource',
function($resource) {
return $resource('http://localhost:3000/api/auth/signup', {}, {
post: {
method: 'POST'
}
});
}
]);
...
//In my controller
$scope.signup = function() {
AuthenticationResource.post($scope.credentials, function(response) {
$scope.authentication.user = response;
$state.go($state.previous.state.name || 'home', $state.previous.params);
});
};
While in my server side's express.js
'use strict';
var config = require('../config'),
express = require('express'),
...
cors = require('cors');
...
module.exports.initModulesServerRoutes = function(app) {
// Globbing routing files
config.files.server.routes.forEach(function(routePath) {
require(path.resolve(routePath))(app);
});
};
module.exports.initCorsOption = function(app){
app.options('*', cors());
};
module.exports.init = function(db) {
// Initialize express app
var app = express();
...
// Initialise Cors options
this.initCorsOption(app);
// Initialize modules server routes
this.initModulesServerRoutes(app);
...
return app;
};
I am using node cors package to enable cors and just do app.options('*', cors()); to enable pre-flight across-the-board
But when i am trying to do a POST to http://localhost:3000/api/auth/signup i can see that my user is being saved to the database just fine but it doesn't give me any response and chrome console is giving me this
XMLHttpRequest cannot load http://localhost:3000/api/auth/signup. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4000' is therefore not allowed access.
What did i miss?
I think you are missing app.use before all your routes:
Only 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();
});
If you are using npm cors:
app.use(cors());

Resources