How to add authorization in express routes? - node.js

Let's say i have several routes in express like,
app.use('/login', login);
app.use('/blogs', blog);
app.use('/news', news);
app.use('/profile', profile);
Here, login, blog, news, profile is routes .js files which contains different get/post routes. I.E. /blogs/new or blogs/:id/edit etc.
Suppose I have 2 users in my system, A and B.
A can access /blogs and /news but can't access /profile
B can access /profile but not the other two.
And /login can be accessed by any user.
How do i do this with express and node?
Is there any way i can set a user to be permitted only to access /profile route and it's children route?
P.S i don't want to use passport.

You can use passport.js for this purpose. Here is an example code for adding passport to your application.
1)Add following lines to packages.json
"passport": "^0.4.0",
"passport-jwt": "^3.0.1",
2). And the following code in server.js. Passport.js has multiple strategies for authentication. Here is configuration for jwt
var passport = require('passport');
app.use(passport.initialize());
require('./server/config/passport')(passport);
var auth = passport.authenticate('jwt', { session: false });
app.use('/your/path/*', auth, function (req, res, next) {
next();
});
Passport with check all the paths like /your/path/* ( i hope you know that).

I will demonstrate this with the cookie session. first lets set up a session. i will be using mongod, for different databases, express-session has different packages to save the session.
//------ app.js----------
const session = require("express-session");
const MongoDbStore = require("connect-mongodb-session")(session);
const store = new MongoDbStore({
uri: "mongodb://localhost:27017/yilmaz",
collection: "sessions",
});
// ------ this adds "session" property to the req
app.use(
session({
secret: "anyhting long",
resave: false,
saveUninitialized: false,
store: store,
})
);
First step of authentication is signing up. We save user's email and password. Next step user logs in the system. In this step we check if the email (or username) and password are in match.
exports.postLogin = async (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const user = await User.findOne({ email });
if (!user) {
return res.redirect("/login");
}
const match = await bcrypt.compare(password, user.password);
if (match) {
// when we protect the routes we look at this property
// session property is attached by the express-session
// -------THIS IS WHERE WE SAVE THE SESSION TO DATABASE
req.session.isLoggedIn = true;
req.session.user = user;
// after req.session.user is set, everytime client makes a request, our middleware will be checking this
return req.session.save((err) => {
console.log("error in postLogin", err);
return res.redirect("/");
});
}
res.redirect("/login");
};
with checking `req.session.isLoggedin` you can write a middleware and used on the routes.
module.exports = (req, res, next) => {
if (!req.session.isLoggedin) {
return res.redirect("/login");
}
next();
};
in any route you want u can add this middleware
const isAuth = require("../middleware/is-auth");
router.get("/edit-product/:productId", isAuth, adminController.getEditProduct);

Related

req.user undefined despite successful signup with Passport and Express Session on NodeJS

The Problem
I am setting up my NodeJS & Express App, using Passport for authentication with Google Sign In and Facebook Login. Everything works very well when working on localhost, but when I deploy my app (to Vercel) I can't make the sessions work.
The signin process works very well, and I can see the user information attached to req.session, and req.user contains all user information.
However, right after that, when calling any route, req.user is undefined
The code
//Setting up the app
const express = require('express');
//const https = require('https');
//Bodyparser to handle JSON
const bodyParser = require('body-parser');
//Mongoose to connect to MongoDB
const mongoose = require('mongoose');
//To handle environments
const keys = require('./keys')
//To handle user sessions
var session = require("express-session");
var MongoDBStore = require('connect-mongodb-session')(session);
//Passport to hangle Google Sign In
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
//Authentication: serialize and deserialize user
passport.serializeUser(function(user, done) {
console.log(`serialize: user:${user}`);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log(`deserialize user:${user}`)
done(err, user);
});
});
//Authentication: Google Sign In
passport.use(new GoogleStrategy({
clientID: keys.GOOGLE_CLIENTID,
clientSecret: keys.GOOGLE_SECRET,
callbackURL: keys.URL_GOOGLE_CALLBACK
},
function callback(accessToken, refreshToken, profile, done) {
process.nextTick( ()=> {
User.findOne({'googleId': profile.id}, (err,user)=> {
if(err)
return done(err,false);
if(user){
return done(null, user);
}
else{
var newUser = new User();
newUser.googleId = profile.id;
newUser.googleToken = accessToken;
newUser.firstName = profile.name.givenName;
newUser.email = profile.emails[0].value;
newUser.authType = "Google";
newUser.image = profile.photos[0].value;
newUser.save((err)=> {
if(err)
throw err;
return done(null, newUser);
});
}
});
});
}
));
//Importing the models used
const Country = require('./models/country');
const Place = require('./models/place');
const User = require('./models/user');
const Trip = require('./models/trip');
//Starting the app
const app = express();
//Using body parser as global middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }))
//Using cookie bodyParser
app.use(require('cookie-parser')('keyboard cat'))
//Using Cors for routes
//app.use(cors())
//Store for sessions
var storeSessions = new MongoDBStore({
uri: keys.URI_MONGO,
collection: 'sessions'
});
// Catch errors
storeSessions.on('error', function(error) {
console.log(error);
});
//Added but not sure if useful
app.use(cors({credentials: true, origin: true}));
app.set('trust proxy', 1);
//Setting up a session
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: false,
proxy: true,
store: storeSessions,
cookie : {
maxAge: 2419200000
}
}));
//Using passport for authentication
app.use(passport.initialize());
app.use(passport.session());
//Solving CORS issues
app.use((req, res, next) => {
let allowedOrigins = ['http://localhost:3000', 'http://localhost:8080', 'mywebsite.com'];
let origin = req.headers.origin;
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
}
//res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.header('Access-Control-Allow-Credentials', true);
next();
});
//Connection to MongoDB
mongoose.connect(keys.URI_MONGO, {useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connection successful!'))
.catch((error) => { console.error(error); });
//The API call from the Google sign in button
app.get('/authgoogle',
passport.authenticate('google', { scope : ['profile', 'email'] }));
//Callback from Google Sign in
app.get('/authgoogleredirect', function(req, res, next) {
passport.authenticate('google', {
scope : ['profile', 'email']
}, function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect(keys.URL_SIGN_IN); }
req.logIn(user, function(err) {
if (err) { return next(err); }
req.session.user = req.user;
req.session.save(function(){
console.log(`req.session.user in callback: ${req.session.user}`)
console.log(`req.session in callback: ${JSON.stringify(req.session)}`)
res.redirect(keys.URL_LOGGED_IN);
});
});
})(req, res, next);
});
//Middleware to check if user is logged in
function isLoggedIn(req, res, next) {
console.log(`req.session in isloggedin: ${JSON.stringify(req.session)}`);
console.log(`req.user in isloggedin: ${JSON.stringify(req.user)}`);
if (req.isAuthenticated()) {
next();
} else {
data = {
"redirect": true,
"url": keys.URL_SIGN_IN
}
res.send(JSON.stringify(data));
}
}
//--------------------USERS------------------------
//Getting the currently logged in user
app.get('/getcurrentuser', isLoggedIn, function(req, res, next) {
res.send(req.user);
});
What works and what does not
The google sign in works well, and passport.serialize and passport.deserialize work well, I can see the user object.
When I try to reach GET /getcurrentuser (call on all pages), I don't get the req.user object and isAuthenticated() always returns false.
These are the logs of the middleware isloggedin :
req.session in isloggedin: {"cookie":{"originalMaxAge":2419200000,"expires":"2020-08-07T12:09:54.220Z","httpOnly":true,"path":"/"}}
req.user in isloggedin: undefined
The sessions are correctly saved in the session collection in mongoDB.
Serialize and deserialize don't seem to be called after sign in, not sure if that's normal.
What I tried
I read plenty of answers to similar problems but none seem to work:
order of initialization of passport and express session: it should be correct
proxy: added but did not change anything
credentials: include has always been included in my front end fetch call
cookie.secure: false: turned it on and off, without success
cookie session tried to use it instead of express session, without success
It must be something stupid but I can't seem to find it...
What troubles me is why would this work on localhost?
Thanks a lot for your time :)
Alright, if anyone comes across this exact issue, I finally solved it:
req.user always came as undefined, because my front end and back end have different domain names (I'm using the free heroku tier) therefore the cookie was considered third-party, blocked by Google Chrome and never sent to my express app!
As soon as I disabled third-party cookie blocking in Chrome settings the session became persistent!
The passport and session configurations were correct all along...
Thanks #abhinav for your help and time :)
Passport has its own session handling code embedded in its library, if you want to store your passport login user to session-database you can do
router.get("/redirect", passport.authenticate('google',{failureRedirect:'/fail',successRedirect:'/success'}, (req,res)=>{
req.session.user = req.user;
});
and keeping your remaining code same as you have done, this will save your session in the database

Passport req.user not persisting across requests -

Using passport.js local strategy I am trying to use the req.user to obtain current user id so that I can store recipes in the database with the users id. The problem seems to be around the deserialization part of the passport.js file I have in my config file in my app. Whenever I hit the /api/saveRecipe route for some reason it gets deserialized and the req user is then no longer available.
Notes: I am authenticating on my backend server using react on the front end.
Below is my server.js file
Problem: req.user is available after calling passport.authenticate('local') but once api/saveRecipe route is hit req.user is no longer available.
After researching this subject on S.O. it appears that it most often has to do with order in the server file setup but i have looked and reviewed and i believe my setup correct...
const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const routes = require("./routes");
// Requiring passport as we've configured it
let passport = require("./config/passport");
const sequelize = require("sequelize");
// const routes = require("./routes");
const app = express();
var db = require("./models");
const PORT = process.env.PORT || 3001;
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// passport stuff
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));
// We need to use sessions to keep track of our user's login status
// app.use(cookieParser('cookit'));
app.use(
session({
secret: "cookit",
name: "cookit_Cookie"
})
);
app.use(passport.initialize());
app.use(passport.session());
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/public"));
}
// the view files are JavaScript files, hence the extension
app.set('view engine', 'js');
// the directory containing the view files
app.set('pages', './');
// Add routes, both API and view
app.use(routes);
// Syncing our database and logging a message to the user upon success
db.connection.sync().then(function() {
console.log("\nDB connected\n")
// Start the API server
app.listen(PORT, function() {
console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
});
});
module.exports = app;
my passport.js code
//we import passport packages required for authentication
var passport = require("passport");
var LocalStrategy = require("passport-local").Strategy;
//
//We will need the models folder to check passport against
var db = require("../models");
// Telling passport we want to use a Local Strategy. In other words, we want login with a username/email and password
passport.use(
new LocalStrategy(
// Our user will sign in using an email, rather than a "username"
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
function(req, username, password, done) {
// console.log(`loggin in with email: ${username} \n and password: ${password}`)
// When a user tries to sign in this code runs
db.User.findOne({
where: {
email: username
}
}).then(function(dbUser) {
// console.log(dbUser)
// If there's no user with the given email
if (!dbUser) {
return done(null, false, {
message: "Incorrect email."
});
}
// If there is a user with the given email, but the password the user gives us is incorrect
else if (!dbUser.validPassword(password)) {
return done(null, false, {
message: "Incorrect password."
});
}
// If none of the above, return the user
return done(null, dbUser);
});
}
)
);
// serialize determines what to store in the session data so we are storing email, ID and firstName
passport.serializeUser(function(user, done) {
console.log(`\n\n serializing ${user.id}\n`)
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log(`\n\n DEserializing ${id}\n`)
db.User.findOne({where: {id:id}}, function(err, user) {
done(err, user);
});
});
// Exporting our configured passport
module.exports = passport;
const router = require("express").Router();
const controller = require("../../controllers/controller.js");
const passport = require("../../config/passport");
router.post(
"/login",
passport.authenticate("local", { failureRedirect: "/login" }),
function(req, res) {
console.log(`req body -${req.body}`);
res.json({
message: "user authenticated",
});
}
);
router.post("/saveRecipe", (req, res) => {
console.log(req.user)
if (req.isAuthenticated()) {
controller.saveRecipe;
} else {
res.json({ message: "user not signed in" });
}
});
module.exports = router;
The problem is in your router.post('login'). Try changing it to something like this:
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile',
failureRedirect: '/login/failed'})
)
This will correctly set the req.user in your next requests!

Passport js serializeUser & deserializeUser

I have read everything on the argument but still cannot understand it. The documentation on the Passport Js web site is very vague.
I am using Passport JS with the passport-ldapauth Strategy. I do not have a Database. I obviously don't want to hit the LDAP server on each request. I would like to authenticate the user the first time on the POST /login route using the passport strategy with LDAP, store the user in the session and on each subsequent requests I just want to check if the user is already logged in.
I am trying to use the session but I cannot understand how to use Passport + session with the serialize/deserialize flow. Every example I checked use a User.findOne in the deserializeUser function.
As of now I disabled the use of the session for Passport and I am using a custom middleware where I check if req.session.user != null. If that's the case the user is already logged in and I hit next(). Otherwise redirect to login.
Here is some code (for sake of simplicity I deleted the code not related to the question):
Passport configuration:
var express = require('express'),
session = require('express-session'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth');
var app = express();
var LdapStrategyOptions = {
server: {
url: '<url>',
bindDN: '<dn>',
bindCredentials: "<pwd>",
searchBase: '<searchBase>',
searchFilter: '<filter>'
}
};
passport.use(new LdapStrategy(LdapStrategyOptions));
app.use(cookieParser());
app.use(session({
store: new LokiStore({autosave: false}),
resave: false,
saveUninitialized: false
secret: env.get("SESSION_SECRET")
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Routes:
// LOGIN ROUTE
app.get('/login',
function(req, res) {
res.render('login');
});
// LOGIN HANDLER ROUTE
app.post('/login',
passport.authenticate('ldapauth', { session: false }),
function(req, res) {
req.session.userId = req.user.cn;
req.session.user = {
"userId": req.user.cn,
"displayName": req.user.displayName
};
res.redirect('/');
});
// LOGOUT ROUTE
app.get('/logout',
function(req, res) {
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
});
// HOME ROUTE
app.get('/', isLoggedIn, function(req, res) {
res.render('home');
});
IsLoggedIn Middleware:
var isLoggedIn = function(req, res, next) {
if (req.session.user != null){
console.log("is auth ok '" + req.session.user.userId +"'");
return next();
}
console.log("redirect to auth/login");
res.redirect('/auth/login');
}
What am I missing? Is there any security fault in my setup?
Any help is appreciated.
From passportjs docs:
In a typical web application, the credentials used to authenticate a user will only be transmitted during the login request. If authentication succeeds, a session will be established and maintained via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session. In order to support login sessions, Passport will serialize and deserialize user instances to and from the session.
Basically serializeUser is supposed to return a unique user identifier so you can deserializeUser back into JSON later.
So for your implementation you should probably do something along these lines:
DISCLAIMER: I have no experience with LDAP.
passport.serializeUser(function(user, done) {
//We can identify the user uniquely by the CN,
//so we only serialize this into the session token.
done(null, user.cn);
});
passport.deserializeUser(function(cn, done) {
//Directly query LDAP.
//I'm not sure passport caches the result (only calls deserializeUser for new sessions)
//but worst case you can cache the result yourself.
somehowLoadUserFromLDAPByCN(cn, function(err, user) {
done(err, {
userId: user.cn,
displayName: user.displayName
});
});
});
If you only need an id and a display name, it's totally fine to keep them in session. You should only load the full user profile when you need more fields.

Authorization in a NodeJS Express REST server

I am building a REST server in nodejs using express.
I would like to allow certain users to perform certain calls.
i.e. have an admin who can edit other users and see reports, where a user can only perform simple actions.
I was trying to use passport.js and passport-ldapauth, and also I would like to perform different queries for authentication (check credentials) and authorization (check if the user is part of a group).
var fs = require('fs');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var passport = require('passport');
var LdapStrategy = require('passport-ldapauth');
var app = express();
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Allow self signed certificates
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
app.use('/', index);
app.use('/users', users);
var OPTS = {
server: {
url: 'ldaps://...',
bindDN: 'cn=Manager,dc=com',
bindCredentials: 'secret',
searchBase: 'ou=people,dc=com',
searchFilter: '(uid={{username}})',
tlsOptions: {
ca: [fs.readFileSync('/path/to/certificate.crt')]
}
},
handleErrorsAsFailures: true,
failureErrorCallback: (err) => console.log(err)
};
passport.use(new LdapStrategy(OPTS));
passport.use('test', new LdapStrategy(OPTS));
app.use(passport.initialize());
app.post('/login', function(req, res, next) {
passport.authenticate('ldapauth', function(err, user, info) {
if (err) return next(err);
if (!user) return res.status(401).send(info);
res.json(user);
// req.logIn(user, function(err) {
// if (err)
// console.error(err);
// if (err) return next(err);
// return res.json(user);
// })
})(req, res, next);
});
The passport-ldapauth strategy does not allow you to perform any additional checks or queries as far as I know from reading over the documentation. The strategy and Passport in general is aimed at making the login/authentication process seamless and easy as possible. So any additional constraints will need to be handled on your own.
With that said, passport-ldapauth utilizes ldapauth-fork underneath which in turn uses ldapjs. You can try to utilize ldapjs as shown here and here, but I think the easiest solution would be to use ldapauth-fork directly.
We first need to set up ldapauth-fork, so we'll use the following example app/ldap/index.js:
const LdapAuth = require('ldapauth-fork')
const ldap = new LdapAuth({
url: 'ldaps://...',
bindDN: 'cn=Manager,dc=com',
bindCredentials: 'secret',
searchBase: 'ou=people,dc=com',
searchFilter: '(uid={{username}})',
tlsOptions: {
ca: [fs.readFileSync('/path/to/certificate.crt')]
})
ldap.on('error', (err) => { throw err })
module.exports = ldap
Our example app/controllers/auth.js could look something like this:
const jwt = require('jsonwebtoken')
const ldap = require('../ldap')
const { User } = require('../database/models') // mongoose model
const Promise = require('bluebird')
exports.login = async (req, res) => {
const { username, password } = req.body
if (!username || !password) {
res.status(400
res.json({ error: 'Missing username or password.' })
return
}
// ldapauth-fork doesn't support Promises.
// You can try to promisfy it, but I prefer this.
// I've named it `profile`, but you can name it whatever you want.
const profile = await Promise.fromCallback(cb => ldap.authenticate(username, password, cb))
// Since this is a REST API, we need to send back a token.
// For this example, we're creating it by hand.
const token = jwt.sign({ user: profile }, 'secret', {})
// Use epoch time from the token instead of generating it ourselves.
const { exp } = jwt.verify(token, 'secret')
// Finally send the token.
// By convention, the keys are snake case.
res.json({
access_token: token,
token_type: 'Bearer',
expires_in: exp,
user: profile
})
}
Now that we have our token created, we need a way to verify that token. To do that we need to write our own middleware. So for example app/middleware/valid-token.js:
const jwt = require('jsonwebtoken')
exports.needsAdminAccess = (req, res, next) => {
// This token should have already been validated by the `requiresToken` middleware
let token = req.header('authorization').split(' ')[1]
token = jwt.verify(token, 'secret')
// Let's check if they are in the admin group
// Remember that we set the user/profile value in the controller.
if (!token.user.dn.includes('ou=ADMIN')) {
next(new Error('You must be an admin to access this route.'))
return
}
// Any additional checks would go here.
// ...
// If everything is fine then call next to let the request continue.
next()
}
exports.requiresToken = (req, res, next) => {
// Assuming the token is in the header as Authorization: Bearer token
let token = req.header('authorization').split(' ')[1]
// Make sure our secret key matches
token = jwt.verify(token, 'secret')
// Additional checks of the token should be done here as well.
// ...
// Don't forget to call next if all is good
next()
}
Finally we use the middleware wherever you define your routes, for example:
const express = require('express')
const app = express()
const { requiresToken, needsAdminAccess } = require('./middleware/valid-token')
// This route needs a valid token, but not admin rights
app.get('/user', requiresToken, (req, res) => { })
// This route needs a valid token AND admin rights
app.get('/admin', requiresToken, needsAdminAccess, (req, res) => { })
I wrote everything from scratch to hopefully paint a clear picture on how everything works. You can use other packages to validate the token for you, but we need to do verify specific things so we write our own.

Isomorphic react App can't find req.login after logging in

I log in with the user at the login, and the user object gets saved for req.user (or passports user) however after I go to a different route/state it doesnt hold the user object there anymore. To demonstrate I just try to console.log(req.user) and it returns undefined, for a POST method (updateUserProfile) in my controller. I used PostMan to test the POST method, the GET method worked on Postman for grabbing all users. If I refresh the page the app.get(*) will load the req.user and print it fine, its just in the calls. What could be the reason? Could it be my express setup? Example:
My routes:
/**
* Routes for express app
*/
var express = require('express');
var users = require('../controllers/users');
var feedback = require("../controllers/feedbackapi");
var problem = require("../controllers/problemapi");
var pair = require("../controllers/userproblempairapi");
var mongoose = require('mongoose');
var _ = require('lodash');
var Header = require('../../public/assets/header.server');
var App = require('../../public/assets/app.server');
module.exports = function(app, passport) {
// user routes
app.post('/login', users.postLogin);
app.post('/signup', users.postSignUp);
app.get('/logout', users.getLogout);
app.post('/updateUserProfile', users.updateUserProfile);
// google auth
// Redirect the user to Google for authentication. When complete, Google
// will redirect the user back to the application at
// /auth/google/return
// Authentication with google requires an additional scope param, for more info go
// here https://developers.google.com/identity/protocols/OpenIDConnect#scope-param
app.get('/auth/google', passport.authenticate('google', { scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'
] }));
// Google will redirect the user to this URL after authentication. Finish the
// process by verifying the assertion. If valid, the user will be logged in.
// Otherwise, the authentication has failed.
app.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/login'
}));
//Important** on refresh we look at our wildcard call to find out if we're still logged in.
// Retrieves all topics on any endpoint for demonstration purposes
// If you were indeed doing this in production, you should instead only
// query the Topics on a page that has topics
app.get('*', function(req, res, next) {
// We don't want to be seeding and generating markup with user information
var user = req.user ? { authenticated: true, isWaiting: false } : { authenticated: false, isWaiting: false };
// An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during
// that request/response cycle (if any). Otherwise, this property is identical to app.locals
// This property is useful for exposing request-level information such as request path name, authenticated user, user settings, and so on.
// pass in data to be seeded into the TopicStore
res.locals.data = {
UserStore: { user: user }
};
next();
});
// This is where the magic happens. We take the locals data we have already
// fetched and seed our stores with data.
// App is a function that requires store data and url to initialize and return the React-rendered html string
app.get('*', function (req, res, next) {
var html = App(JSON.stringify(res.locals.data || {}), req, res);
html = html.replace("TITLE", Header.title)
.replace("META", Header.meta);
if(process.env.NODE_ENV === 'devhotloader') {
html = html.replace("LINK", '');
} else {
html = html.replace("LINK", Header.link);
}
res.contentType = "text/html; charset=utf8";
res.end(html);
});
};;
My users controller:
var _ = require('lodash');
var User = require('../models/user');
var passport = require('passport');
var ParsonsProblem = require('../models/parsonsproblem');
var Feedback = require('../models/feedback');
var UserProblemPair = require('../models/userproblempair');
/**
* POST /login
*/
exports.postLogin = function(req, res, next) {
// Do email and password validation for the server
/*var Feed = new Feedback({
description: 'Supsuop'
});
var Problem = new ParsonsProblem({
description: 'Test',
feedback: Feed
});
var Pair = new UserProblemPair({
problem_id: Problem,
attempt_quantity: 0,
completed: true
});
*/
console.log(req.body);
passport.authenticate('local', function(err, user, info) {
if(err) return next(err);
if(!user) {
req.flash('errors', {msg: info.message});
}
// Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session
req.logIn(user, function(err) {
if(err) return next(err);
req.flash('success', { msg: 'Success! You are logged in'});
res.end('Success');
console.log(req.user);
});
})(req, res, next);
/*
Feed.save(function(err) {console.log('Feedback saved');});
Problem.save(function(err) {console.log('Problem saved');});
Pair.save(function(err) {console.log('ProblemPair saved');});
console.log(Feed);
*/
};
/**
* POST UpdateUser Profile
*/
exports.updateUserProfile = function(req, res) {
var id = req.user._id;
if (req.body.firstName == "") {
req.body.firstName = req.user.profile.firstName;
}
if (req.body.lastName == "") {
req.body.lastName = req.user.profile.lastName;
}
if (req.body.gender == "") {
req.body.gender = req.user.profile.gender;
}
if (req.body.section == "") {
req.body.section = req.user.profile.section;
}
User.findById(id, function(err, user) {
console.log("ID: " + id);
user.profile.firstName = req.body.firstName;
user.profile.lastName = req.body.lastName;
user.profile.gender = req.body.gender;
user.profile.section = req.body.section;
user.save();
res.end();
});
}
/**
* GET /logout
*/
exports.getLogout = function(req, res, next) {
// Do email and password validation for the server
console.log("User has been logged out");
req.logout();
res.redirect('/');
//res.end():
};
/**
* POST /signup
* Create a new local account
*/
exports.postSignUp = function(req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password,
profile: {
firstName : req.body.firstName,
lastName : req.body.lastName,
section : req.body.section
}
});
//user.profile.firstName = req.body.firstName;
//user.profile.lastName = req.body.lastName;
//user.profile.section = req.body.section;
User.findOne({email: req.body.email}, function(err, existingUser) {
if(existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists' });
res.redirect('/sign');
}
user.save(function(err) {
if(err) return next(err);
req.logIn(user, function(err) {
if(err) return next(err);
console.log('Successfully created');
console.log('Printing user');
console.log(user);
console.log('Print our body from our request');
console.log(req.body);
res.redirect('/');
});
});
});
};
Express Setup:
app.disable('x-powered-by');
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view cache', false);
app.use(bodyParser.json({limit: '100mb'}));
app.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-form-urlencoded
app.use(methodOverride());
app.use(express.static(path.join(__dirname, '../..', 'public')));
// I am adding this here so that the Heroku deploy will work
// Indicates the app is behind a front-facing proxy,
// and to use the X-Forwarded-* headers to determine the connection and the IP address of the client.
// NOTE: X-Forwarded-* headers are easily spoofed and the detected IP addresses are unreliable.
// trust proxy is disabled by default.
// When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies.
// The req.ips property, then, contains an array of IP addresses the client is connected through.
// To enable it, use the values described in the trust proxy options table.
// The trust proxy setting is implemented using the proxy-addr package. For more information, see its documentation.
app.enable('trust proxy');
// Cookie parser should be above session
// cookieParser - Parse Cookie header and populate req.cookies with an object keyed by cookie names
// Optionally you may enable signed cookie support by passing a secret string, which assigns req.secret
// so it may be used by other middleware
app.use(cookieParser());
// Create a session middleware with the given options
// Note session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
// Options: resave: forces the session to be saved back to the session store, even if the session was never
// modified during the request. Depending on your store this may be necessary, but it can also
// create race conditions where a client has two parallel requests to your server and changes made
// to the session in one request may get overwritten when the other request ends, even if it made no
// changes(this behavior also depends on what store you're using).
// saveUnitialized: Forces a session that is uninitialized to be saved to the store. A session is uninitialized when
// it is new but not modified. Choosing false is useful for implementing login sessions, reducing server storage
// usage, or complying with laws that require permission before setting a cookie. Choosing false will also help with
// race conditions where a client makes multiple parallel requests without a session
// secret: This is the secret used to sign the session ID cookie.
// name: The name of the session ID cookie to set in the response (and read from in the request).
// cookie: Please note that secure: true is a recommended option.
// However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies.
// If secure is set, and you access your site over HTTP, the cookie will not be set.
var sess = {
resave: true,
saveUninitialized: true,
// Use generic cookie name for security purposes
key: 'sessionId',
secret: secrets.sessionSecret,
// Add HTTPOnly, Secure attributes on Session Cookie
// If secure is set, and you access your site over HTTP, the cookie will not be set
cookie: {
expires: false,
httpOnly: false,
//secure: false
},
store: new MongoStore({ url: secrets.db, autoReconnect: true})
};
var node_env = process.env.NODE_ENV;
console.log('Environment: ' + node_env);
//if(node_env === 'production') {
//sess.cookie.secure = false; // Serve secure cookies
//}
app.use(session(sess));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
edit: Printing out the req.session and req.user only display when logging in and signing in/out, when transitioning to another view with react-router it doesn't have that info anymore. ("tried with console logs")
I think you have to user react-router to handle internal redirects.

Resources