React App not setting cookie in browser with express-socket.io-session - reactjs

I'm trying to sett a session-id cookie on my client react application. Somehow this does not work and all I get is the standart io session cookie in the browser.
On the other hand it works with a static served HTML file which builds up a socket.io connection.
Here is my server-side code:
var app = require("express")();
var server = require('http').Server(app);
var io = require('socket.io')(server);
let expressSession = require('express-session');
let sharedsession = require("express-socket.io-session");
var path = require('path');
var host = 'process.env.HOST' || '0.0.0.0';
var port = process.env.PORT || 4000;
server.listen(port, host, function () {
console.log("Server running on: " + host + " : " + port);
});
if (process.env.NODE_ENV === 'production') {
//Set static folder
app.use(require('express').static('client/build'));
}
let session = expressSession({
secret: 'my-secret',
//store: new redisStore({ host: 'localhost', port: 6379, client: redis, ttl: 260}),
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 10000,
httpOnly: true,
sameSite: true,
secure: true,
},
})
app.use(function(req, res, next) {
res.header('Content-Type', 'application/json;charset=UTF-8')
res.header('Access-Control-Allow-Credentials', true)
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
next()
})
app.use(session);
io.use(sharedsession(session, {
autoSave: true
}));
io.on('connection', function (socket) {
console.log(`socket with id ${socket.id} connection established`);
socket.handshake.session.userdata = Math.random();
socket.handshake.session.save();
console.log(socket.handshake.sessionID);
client:
let socket = io(serverIP, { autoConnect: false });
useEffect(() => {
socket.on("connect", () => {
console.log("Connected");
});
socket.open();
}, []);
on a simple HTML file like this the session cookie gets set:
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
socket = io();
Both cases are over HTTP. The socket connection also gets established with both variants and the io cookie gets set. And the react App is built and being served from the build folder.
What could I be missing? Thanks for any advice!
EDIT FROM 02.03.2019:
As it turns out the error might come from a missing request or something. When I use a local socket.io.js directly added to the HTML everything works fine. If I use socket.io-client directly imported in my react app or a external CDN it wont work. Seems like it only works after there has been I request to a local ressource. Anyone knows what could be the diference between those two approaches and what could be the reason for this behaviour?

Related

Why is my react router not passing my express route to server?

I tried to add social login to my (already working) react/express app, and I got it working in localhost. However, when I deploy it to production, the social login doesn't work. This is how it gets started
Google+
However, in production, it stays at https://sample.com/api/auth/google in my browser. So, it appears the react router is catching it first before express. How?
In localhost, it works because the proxy in package.js
"proxy": {
"/api": {
"target": "http://localhost:4000",
"ws": true
}
Now, how can I do this for production?
By the way, all my server APIs starts with '/api/...'. Also, in my react routes, I don't have a catch-all component.
UPDATE:
Here is my server.js
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
var flash = require('connect-flash');
var cookieParser = require('cookie-parser');
var path = require('path');
var fs = require('fs');
var app = express();
var isSecured = true;
app.isDevMode = app.get('env') == 'development'
require('./server/config/log')(app)
var port = (process.env.PORT || app.isDevMode) ? 4000 : (isSecured ? 443 : 80);
app.set('port', port);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json({ limit: '10mb' }));
app.use(flash()); // use connect-flash for flash messages stored in session
var server = require('http').createServer(app);
if (!app.isDevMode && isSecured) {
var options = {
ca: fs.readFileSync('ca_bundle.crt'),
key: fs.readFileSync('private.key'),
cert: fs.readFileSync('certificate.crt')
}
server = require('https').createServer(options, app);
}
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
console.log('Connected to MongoDB');
var routes = require('./server/routes');
routes.init(app);
if (app.isDevMode) {
app.use(express.static(__dirname));
}
else {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
if (isSecured) {
require('http').createServer(function (req, res) {
res.writeHead(307, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
}
}
server.listen(app.get('port'), function () {
console.log('Server listening on port ' + app.get('port'));
});
});
module.exports = app;
Here is my routes:
app.get('/api/auth/google', passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'] }))
app.get('/api/callback/google', passport.authenticate('google', {successRedirect: '/?action=login&provider=google', failureRedirect: '/?action=login'}))
UPDATE:
Here is the morgan log. I added a number for each line for my reference. Line 4 started when I click the link to send '/api/auth/google', and finished at line 6.
1. GET /api/get/list?parm={%22kind%22:%22Prod%22,%22limit%22:5,%22createdOn%22:-1} 304 - - 22.959 ms
2. GET /images/logo.png 200 3432 - 17.410 ms
3. GET /service-worker.js 200 3097 - 3.398 ms
4. GET /static/js/main.cef8cdac.js 304 - - 5.180 ms
5. GET /images/two.png 304 - - 4.908 ms
6. GET /service-worker.js 200 3097 - 3.838 ms
So, basically, the request didn't come to express server. Actually, if I had a catch all route in react, I can see it's hitting there.
Here is the network log:

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.

Express-session not persistent

im currently trying to save data in express sessions.
Im using the following packages:
"connect-mongo": "^0.8.2"
"express": "~4.0.0"
"express-session": "^1.11.3"
"mongoose": "~3.6.13"
I'm implementing the sessions this way:
// Loading packages
var express = require('express');
var app = express();
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
// Connect to MongoDB
var mongoose = require('mongoose');
var options = { server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS : 30000 } } };
mongoose.connect(process.env.DATABASE, options);
var router = express.Router();
// Session storage
router.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
store: new MongoStore({mongooseConnection: mongoose.connection}),
cookie: {
maxAge: 60*60*24*7*1000
}
}));
// Routes
router.route('/user/me')
.get(function(req, res) {
console.log(req.session);
});
router.route('/auth/login')
.post(function(req, res) {
req.session.userId = req.params.id;
});
app.use('/api', router);
var port = process.env.PORT || 8080;
app.listen(port);
console.log('Magic happens on port ' + port);
Now i have the following problem:
When i try posting to /api/auth/login using a software called "Postman REST Client" (Chrome extension) it works, and the user id will be logged when getting /api/user/me.
But if i try posting to /api/auth/login in an angular.js app, it does not save the session values. I always get a clean session object without the userId.
Also there are no cookies saved.
Could you please help me?
Greetings: Max

Angular + Node.js HTTP & HTTPS (SSL)

I want to use https for the post requests in my payment view. I stumbled over diff tutorials and now my setup is the following (and I still don't get it running):
Certificates
I generated certificates for my development environment using this tutorial:
http://greengeckodesign.com/blog/2013/06/15/creating-an-ssl-certificate-for-node-dot-js/
Node.js
I followed answer #2 to setup the http and https server in node:
Listen on HTTP and HTTPS for a single express app
It looks like that:
environment = require('./config/config.js')()
express = require('express')
bodyParser = require('body-parser')
app = express()
portHTTP = process.env.PORT || 61361
portHTTPS = 3030
mongoose = require('mongoose')
morgan = require('morgan')
http = require('http')
https = require('https')
socket = require('socket.io')
#SSL Server
fs = require('fs')
sslOptions = {
key: fs.readFileSync(__dirname + '/config/ssl/server.key'),
cert: fs.readFileSync(__dirname + '/config/ssl/server.crt'),
ca: fs.readFileSync(__dirname + '/config/ssl/ca.crt'),
requestCert: true,
rejectUnauthorized: false
}
#Database
configDB = require('./config/database.js')(environment)
mongoose.connect(configDB.url,{auth:{authdb:configDB.authdb}}, (err)->
if (err)
console.log(err)
)
db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error:'))
db.once('open', () ->
console.log "Database established"
)
#Express Application
app.use(morgan('dev'))# log every request to the console
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
rootPath = __dirname + '/../../'
app.use('/bower_components', express.static(rootPath + 'bower_components'))
app.use('/partials', express.static(__dirname + '/../partials'))
# Routes
require('./node/routes.js')(app, db) # load our routes and pass in our app and fully configured passport
# Launch the application
https.createServer(sslOptions,app).listen(portHTTPS, () ->
console.log("Secure Express server listening on port #{portHTTPS}")
)
http.createServer(app).listen(portHTTP)
io = socket.listen(https)
#app.listen(portHTTP)
console.log('Unsecure Express server listening on port #{portHTTP} environment: #{environment}')
Angularjs
I installed the angular module:
https://github.com/btford/angular-socket-io
And added it to my index.coffee
angular.module "app", [lots of dependencies, 'btford.socket-io', 'myCodeService']
.config ($stateProvider, $urlRouterProvider) ->
...states...
.factory('mySocket', (socketFactory) ->
return socketFactory({
ioSocket: io.connect('https://localhost:3030/',{secure: true})
#ioSocket: io.connect('https://cdn.socket.io/socket.io-1.3.4.js',{secure: true})
})
)
I added the two script tags into my index.html
<script src="../bower_components/angular-socket-io/socket.js"></script>
<script src="http://localhost:3030/socket.io/socket.io.js"></script>
Gulp Setup
And I serve in gulp with nodemon and browsersync
gulp.task('serve',['watch'], function(cb){
var called = false;
return nodemon({
script: paths.tmp + '/serve/server.js',
watch: [paths.src + '/node', paths.src + '/config'],
ext: 'coffee',
tasks: ['templateCache', 'partials_tmp'],
env: { 'NODE_ENV': 'development' } ,
nodeArgs: ['--debug=9999']
})
....
function browserSyncInit(files, browser) {
browser = browser === undefined ? 'default' : browser;
browserSync.instance = browserSync.init(files, {
startPath: '/#/',
browser: browser,
proxy: "http://localhost:61361"
});
}
I get an empty response from socket.io and I don't see anything in my browser which bugs me to accept the certificate.
There is an https option for browsersync but I don't really know how to split the traffic/ or if this is done automatically in node.js.
io = socket.listen(https) is incorrect. You should be passing the server instance, not the https module:
// ...
var srv = https.createServer(sslOptions,app);
srv.listen(portHTTPS, () ->
console.log("Secure Express server listening on port #{portHTTPS}")
)
io = socket.listen(srv);
// ...
Also your <script> src that points to socket.io.js should be https and not http.
To split the routing in the front end this can be used:
.run ($rootScope, $window, authUserService) ->
#Listens for state changes and redirects User if he is not logged in
$rootScope.$on "$stateChangeStart", (event, toState, toParams, fromState, fromParams) ->
#After login send user to share
if toState.name is "payment" and !authUserService.isSecure()
event.preventDefault()
$window.location.href='https://localhost:3030/#/spenden'
true

Resources