Maintain sessions in Node.js - javascript

How can I maintain my SESSIONS in Node.js?
For example, I want to store UserID in SESSION using Node.js. How can I do that in Node.js? And can I use that Node.js SESSION in PHP too?
I want the following in Node.js:
<?php $_SESSION['user'] = $userId; ?>

First install the session package
npm install express-session --save
Initialization of the session on your server page
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({secret: 'ssshhhhh', saveUninitialized: true, resave: true}));
Store session
sess = req.session;
var user_id = 1;
sess.user_id = user_id;
Access the session
sess = req.session;
sess.user_id

Let me divide your question in two parts.
How can I maintain my SESSIONS in Node.js?
Answer: Use express-session middleware for maintaining SESSIONS
Can I use that a Node.js SESSION in PHP too?
Answer:
Yes, you can use that session in PHP too, but keep in mind you have to store that session in the database.

ExpressJS has official session middleware, and it is also the current de-facto standard web framework for Node.js.
If you wish to implement session support on your own, this is how the implementation is normally done, upon every request:
Check if the cookie contains a session ID
If not, create a session object that is either stored in memory, on file, or in a database (or a combination of those), and set the session id in the response cookie to match this object's identifier.
If the cookie does contain a session ID, locate the session object by the ID.
Provide the obtained/created object from step 1 as the persisted session object for the request.
You will also have to implement some timeout mechanism, so that after a while the session objects are deleted, at least from memory.

You could use the express-session middleware.
Combine it with connect-redis or connect-mongo to store your sessions inside a database and save memory if memory is valuable to you (like in a cloud setup).
express-sessions (npm)
If you store it in, say, MongoDB, use the PHP MongoDB driver to pick it up from there.

You don't need to do it by yourself. There are some amazing modules in Node.js that handle this kind of things for you.
You can use session middleware from Express.js, as suggested before.
However, I'd recommend you to use Passport.js. This module does the authentication part for you, has a lot of strategies that you could integrate in your website (log in with Facebook, Google, Twitter, etc.), and deals with all the session stuff automatically, using serializeUser() and deserializeUser() functions whenever you need to.
You can take a look at this here, within the "Sessions" section: Configure Passport.js

Session that gives access/permission to view a user's area, as well as it's a credential, so we can use it over the application.
I used jsonwebtoken to make a token which will has the user's details with time after a successful login attempt by the user. I stored it in Redis, and it can be used for a pre-declared time limit.

To maintain a session is now older, and you should try with using JWT token. It is very effective and easy. But still to maintain the session in Node.js:
In your Express.js configuration:
var cookieParser = require('cookie-parser');
var session = require('express-session');
app.use(cookieParser());
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
rolling: true,
cookie: {
path: '/',
maxAge: 60000 * 1000
},
name: 'SID'
}));
Store session after Login:
var session = req.session;
if (user) {
session.user = user._id;
session.save();
console.log(session);
}
Check Session from middleware:
var session = req.session;
if (session.user) {
req.userid = session.user;
next();
} else {
return res.status(401).send({
code: 401,
message: Constant.authentication_fails
});
}

Follow the below steps:
npm install express-session --save
Write the below code:
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({secret: 'your secret key', saveUninitialized: true, resave: true}));
var userId = 1234;
app.get('/', function (req, res, next) {
req.session.userId = userId;
});

Storing a session in Node.js is fairly easy but you need to understands its step, you could handle this manually, also you can use few NPM modules. Passport can help you to authenticate and login and store the session i would recommend you to read its documentation, Passport allow you to authenticate user with different other platform like Google, github many more.
If you are going to use passport use these below NPM module
Passport
Passport Local
Express-flash
Express-session
2 -Import these modules in your main app.js:
const flash = require('express-flash')
const session = require('express-session')
const passport = require('passport')
app.use(session({
secret:'secret',
resave:false,
saveUninitialized:false
}))
app.use(flash())
app.use(passport.initialize())
app.use(passport.session())
3- Create the passport.js file. You can name anything. So basic understanding behind this is that you have to check the valid user coming from your input form, and you have to compare the email id with your model. If it is valid, check the password and then return the user. Once that is done, serialize and deserialize your user data to store in the session..
I would recommend to check this part in the documentation for more clear understanding: Overview
const localStrategy = require('passport-local').Strategy
const bycrypt = require('bcrypt')
const User = require('../model/User')
const initalize = function(passport) {
const auth = async(email, password, done) => {
try {
const user = await User.findOne({email:email})
if(!user) {
throw new Error("Incorrect Email ..!")
}
const match = await bycrypt.compare(password, user.password)
if(!match) {
throw new Error('Incorrect Password..!')
}
return done(null, user)
}
catch (error) {
console.log(error)
done(null,false,error)
}
}
passport.use(new localStrategy({usernameField:'email'}, auth))
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
module.exports = initalize
4 - Now go to your login router and use the below code
const passport = require('passport')
require('../passport/passport')(passport)
routes.get('/signin', (req,res) => {
res.render('signin', {
pageTitle: 'sign in'
})
})
routes.post('/signin', passport.authenticate('local', {
successRedirect: '/welcome',
failureRedirect: '/',
failureFlash: true
}))

You can use sessions in Node.js by using the 'express-session' package in Node.js.
You have to install express and express-session in your application:
const express = require('express');
const session = require('express-session');
const app = express();
"secret" is used for the cookie, and we have to add some secret for managing a session.
"request" we use as a request variable as we use $_SESSION in PHP.
var sess;
app.get('/',function(req,res){ // Get request from the app side
sess = req.session;
sess.email; // Equivalent to $_SESSION['email'] in PHP.
sess.username; // Equivalent to $_SESSION['username'] in PHP.
});
Here is full documentation in Code for Geek about the session in Node.js if you want to learn in detail about the session in Node.js.

You can handle the session in two ways.
Using express-session
Using JWT web token and handle your own session (token-based session handling).
I think token-based session handling is more important rather than using express-session. You will get a problem when you scale your server and also a problem with some single device login situation.
For checking I have a token-based session handling Node.js folder structure. You can check it, and it may be helpful.

Related

Where to store user session in Sapper app

I have started moving an app from React to Sapper. I am new to SSR architecture and want to know what the best way is to store the user session and data.
I am using Firebase for my auth and database. After using the client side firebase API to get the session keys and other user data how would I store the data? I have seen some tutorials making a user.js store, but in the Sapper docs I see it recommends using the session store. So which is better? And what would be the flow from client side to the server side session store?
E.g. If I were to make a login folder under which I have the svelte component and the server side route. Would there be a post "endpoint" that would set the session.user?
It's a bit tricky. I managed to get this working with both client and server using a authentication middleware
https://github.com/itswadesh/sapper-ecommerce/blob/master/src/server.js
The best way I have found so far is using JWT's:
Either get a JWT from a third party (Google, facebook, github) or sign your own.
server.js:
express()
.use(
compression({
threshold: 0
}),
sirv('static', {
dev
}),
cookieParser(),
bodyParser.json({strict: false}),
bodyParser.urlencoded({ extended: false }),
async (req, res, next) => {
const token = req.cookies['AUTH']
const profile = token && !dev ? await getBasicUserInfo(token) : false
return sapper.middleware({
session: () => {
return {
authenticated: !!profile,
profile
}
}
})(req, res, next)
}
)
then with every request just add 'credentials':'include to your requests to the server.
you will have to verify the token on every request but this method makes you app super scalable

Heroku Google OAuth Error

I am getting a very frustrating error. When I make the google OAuth request in my local development environment it is working perfectly. (using passport authentication). Pushing to Heroku I am getting a status 200 versus the status 302 I get in development that redirects me to the google oauth login page. The screen is just showing up blank with no errors. I have tried to intentionally put an error with the client ID, but it isn't even registering the request at all.
Log-In
Brings me to a blank screen on heroku, and registers no request at all.
Please Help!
Server-Side Passport:
// .use is generic register
passport.use(
new GoogleStrategy(
{
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
// need url for where user should go on callback after they grant permission to our application on google auth page
callbackURL: "/auth/google/callback",
// have to authorize this callback url in the google oauth console.developors screen because of security reasons
proxy: true // trust the proxy our request runs through so heroku callbacks to the correct url
},
async (accessToken, refreshToken, profile, done) => {
// after authenticated on the next get request to google it will call this with the accessToken, aka callback function
// console.log("access token", accessToken);
// console.log("refresh token", refreshToken);
// console.log("profile", profile);
// check to see if user id already exists before saving it to DB so it does not overlap...mongoose query...asynchronous operation
// using async await
const existingUser = await User.findOne({
googleId: profile.id
});
// get promise response
if (existingUser) {
// already have record
// finish passport auth function
return done(null, existingUser); // passes to serialize user so serialize can pull that user id
}
// we don't have a new record so make one
const user = await new User({
// creates new model instance of user
googleId: profile.id
}).save(); // have to save it to DB
// get promise from save since asynchronize, then finish with response
done(null, user); // passes to serialize user so serialize can get that id
}
)
); // create new instance of GoogleStrategy
Server-Side API:
app.get(
"/auth/google", // passport, attempt to authenticate the user coming in on this route
passport.authenticate("google", {
// google strategy has internal code, that is 'google', so passport will know to find the google passport authenticator
scope: ["profile", "email"] // options object
// specifies to google we want access to this users profile and email information from their account, these are premade strings in the google oauth process not made up
})
);
// in this callback route they are going to have the code, and google will see that and it will handle it differnetly by exchanging the code for an actual profile, it will call the next part of the GoogleStrategy, aka the accessToken to be saved to Database
// #route GET auth/google/callback
// #desc Get callback data from google to redirect user if signed in
// #access Private can only access this after signed in
app.get(
"/auth/google/callback",
passport.authenticate("google"),
// after authenticate process is done, send user to correct route
(req, res) => {
// redirect to dashboard route after sign-in
res.redirect("/surveys");
// full HTTP requrest, so it reloads versus AJAX request which uses react and redux and is much faster
}
);
Client - Side
<div
className="collapse navbar-collapse nav-positioning"
id="navbarNav"
>
<ul className="navbar-nav">
<li className="nav-item google-link">
<a className="nav-link" href="/auth/google">
Google Login
</a>
</li>
</ul>
</div>
Index.js
// Route file, or starter file
const express = require("express");
// node.js does not have support from E6,
// so we use common js modules
// import vs require :
// common vs ES6
// bring in mongoose
const mongoose = require("mongoose");
// tell express it must make use of cookies when using passport
const cookieSession = require("cookie-session");
const passport = require("passport");
// pull in body-parser middleware to get req.body
const bodyParser = require("body-parser");
// connect it to DB in keys so it is not posted to github
const keys = require("./config/keys");
//connect mongoose
mongoose.connect(keys.mongoURI);
// ########## MODELS ################
// THIS MUST BE ABOVE WHERE YOU USE IT, SO ABOVE PASSPORT
require("./models/User");
require("./models/Survey");
// don't have to require recipient because its included inside Survey
// pull in passport service, we are not returning anything in passport, so we do not need const passport because nothing to assign
require("./services/passport");
// Generate a new application that represents a running express app
const app = express(); // vast majority use single app
// this will listen for incoming requests, and route them on to different route handlers
// parser so every time a req has a req.body comes in then it will be assigned to the req.body property
app.use(bodyParser.json());
app.use(
cookieSession({
// age for auth cookies to last... 30 days
maxAge: 30 * 24 * 60 * 60 * 1000,
// give cookie a key
keys: [keys.cookieKey]
})
);
// tell passport to use cookies
app.use(passport.initialize());
app.use(passport.session());
// done with authentication flow
//require that file returns a function, which is then immediately called with the app object
require("./routes/authRoutes")(app);
require("./routes/billingRoutes")(app);
require("./routes/surveyRoutes")(app);
if (process.env.NODE_ENV === "production") {
// if in production make sure express will serve up production assets
// like main.js
app.use(express.static("client/build"));
// Express will serve up index.html file if it doesn't recognize the routes
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
// dynamically figure out what port to listen to... Heroku, heroku will inject env variables in moment of deploy, but only works in production not development environment
const PORT = process.env.PORT || 5000; // if heroku port exists assign it that, else, assign it 5000
app.listen(PORT); // listen for requests and route them to the correct handler on port 5000
/* ###### HEROKU PREDEPLOY ##### */
// specifiy node version and start script for heroku in package.json
// make .gitignore for dependencies which should not be committed on deploy, heroku will install them itself
// app.use wires up middleware for our application
// ############### TIPS
/*
Google first, because its been asked before...
Run in module
*/
I have the same issue with this problem too. I solve it by define an absoluteURI on the config keys. because google look at url callback at https:// and heroku path is http:// which it should be fix when you add proxy: true but it is not.
On the config keys add
dev: absoluteURI: localhost:5000
prod: absoluteURI: http://herokupath
// .use is generic register
passport.use(
new GoogleStrategy(
{
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: absoluteURI + "/auth/google/callback",
proxy: true
},
I believe the problem is that your app on heroku is only listening for http requests. If your link to the OAuth page has the form "https://your-domain.com/auth/google", then your app's routes will not match against that route (because of the https) and so your app will show a blank page, just like it will show for any route that it's not listening for.
One way to get around this problem and still use https (and therefore still show the secure logo next to the url) is to use https for every link except for this OAuth link. Your get and post requests within the app will be using http, but any link visible on the url will use https. Something like this would work:
app.use(function(req, res, next) {
if (process.env.NODE_ENV === "production") {
const reqType = req.headers["x-forwarded-proto"];
// if not https redirect to https unless logging in using OAuth
if (reqType !== "https") {
req.url.indexOf("auth/google") !== -1
? next()
: res.redirect("https://" + req.headers.host + req.url);
}
} else {
next();
}
});
And any frontend link that points to the OAuth login page should be an http link
Please take a look at this SO answer. It looks like your scope params need to be modified for the google auth to work.

Node js authentication in cross domain

I am working on a MEAN application, I am using Angular 4 for my project. For authentication, I have implemented the Passport js Local-strategy. And I am maintaining persistent session using Express-session. Things are working fine till here.
The Problem
In the same domain session works fine and I am able to authenticate the user. But in cross-domain, I am not able to maintain the session. It generates a new session id for each new request in cross-domain.
I then tried Passport-jwt but the problem with it is I don't have the control over user session. I mean I can't logout the user from the server if he is inactive or even on server re-start also the token don't get invalid.
So in simple words, I am looking for an authentication solution in Node js (Express js) in which I can manage authentication in cross-domain.
I have already seen some blog post and SO questions like this, but it doesn't help.
Thank you.
EDIT
Should I write my own code to achieve this? If so I have a plan.
My basic plan is:
The user will send credentials with the login request.
I will check for the credentials in the database. If credentials are valid, I will generate a random token and save it to the database, in the user table and the same token I will provide to the user with success response.
Now, with each request user will send the token and I will check the token for each request in the database. If the token is valid then I will allow the user to access the API otherwise I will generate an error with 401 status code.
I am using Mongoose (MongoDB) so I will be ok to check the token in each request (performance point of view).
I think this is also a good idea. I just want some suggestions, whether I am thinking in right direction or not.
What I will get with this:
The number of logged in user in the application (active sessions).
I can logout a user if he is idle for a certain interval of time.
I can manage multiple login session of the same user (by doing an entry in the database).
I can allow the end user to clear all other login sessions (like Facebook and Gmail offers).
Any customization related to authorization.
EDIT 2
Here I am shareing my app.js code
var express = require('express');
var helmet = require('helmet');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var dotenv = require('dotenv');
var env = dotenv.load();
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var cors = require('cors');
var databaseUrl = require('./config/database.js')[process.env.NODE_ENV || 'development'];
// configuration
mongoose.connect(databaseUrl); // connect to our database
var app = express();
// app.use(helmet());
// required for passport
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
res.send(200);
} else {
next();
}
});
app.use(cookieParser());
app.use(session({
secret: 'ilovescotchscotchyscotchscotch', // session secret
resave: true,
saveUninitialized: true,
name: 'Session-Id',
cookie: {
secure: false,
httpOnly: false
}
}));
require('./config/passport')(passport); // pass passport for configuration
var index = require('./routes/index');
var users = require('./routes/user.route');
var seeders = require('./routes/seeder.route');
var branches = require('./routes/branch.route');
var companies = require('./routes/company.route');
var dashboard = require('./routes/dashboard.route');
var navigation = require('./routes/navigation.route');
var roles = require('./routes/role.route');
var services = require('./routes/services.route');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
require('./routes/auth.route')(app, passport);
app.use('/', index);
app.use('/users', users);
app.use('/seed', seeders);
app.use('/branches', branches);
app.use('/companies', companies);
app.use('/dashboard', dashboard);
app.use('/navigation', navigation);
app.use('/roles', roles);
app.use('/services', services);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
res.status(404).send({ status: 'NOT_FOUND', message: 'This resource is not available.'});
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
let errorObj = {
status: 'INTERNAL_SERVER_ERROR',
message: 'Something went wrong.',
error: err.message
};
res.status(err.status || 500).send(errorObj);
});
module.exports = app;
EDIT 3
For those who don't understand my problem. Explaining the problem in
simple words:
My Express server is running on port 3000.
In order to consume any API from the server, a user must be logged in.
When a user gets logged in from localhost:3000, the server checks the credentials(using Passport-local) and returns a token in the response header.
Now after login, when a user hits any API from localhost:3000, a predefined Header comes with passport-session and then passport verifies the user session using req.isAuthenticated() and all the things works as expected.
When a user gets logged in from localhost:4000 and the server send a token in response header (same as localhost:3000).
When after successful login, the user hits any API from localhost:4000 the passport js function req.isAuthenticated() returns false.
This was happening because in cross-domain the cookie doesn't go to the server we need to set withCredentials header to true at the client side.
I have set withCredentials header to true but still at the server the req.isAuthenticated() is returning false.
One possible solution to get around CORS/cookie/same-domain problems is to create proxy server that will mirror all requests from localhost:3000/api to localhost:4000, and then use localhost:3000/api to access the API instead of localhost:4000.
Best way for production deployment is to do it on your web server (nginx/apache).
You can also do it in node via express and request modules, or use some ready made middleware like this one:
https://github.com/villadora/express-http-proxy
Solution with this middleware is pretty straightforward:
var proxy = require('express-http-proxy');
var app = require('express')();
app.use('/api', proxy('localhost:4000'));
If you want to use sessions (ie. instead of jwt, etc) I think by default they are just in-memory so it will not work as your application scales to multiple hosts. It is easy to configure them to persist though.
See
https://github.com/expressjs/session#compatible-session-stores
You might have tried with passport-jwt. It generates tokens as per the JWT protocol on login. Your requirement is to blacklist the generated token when you logout. To achieve that, you can create a collection in mongodb named "BlacklistToken" with fields userid and token. When the user logs out, you can insert the token and userid in the collection. Then write a middleware to check whether the token is blacklisted or not. if it is redirect to login page.
did you already take a look here:
In this case, responses can be sent back based on some considerations.
If the resource in question is meant to be widely accessed (just like any HTTP resource accessed by GET), then sending back the Access-Control-Allow-Origin: * header will be sufficient,[...]
You may try this (allow any public IP) :
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', '*'); // add this line
// res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
It is normal that the second server re-create a new session, because assuming that you use Express-session, and according to the documentation:
Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
Which mean that you need to find a way to synchronize servers session data ...
Assuming that you find a method to do that, when you will try to connect, both server will retrieve the same user session data and the second will not have to create a new session...
If I understand the problem correctly here, you want the user's session to be stateless on the server. So that whenever the user logs in, the session can be re-used in any instance of the server when you scale your application, or even if you were to just reboot your application.
To achieve this, what you need is to configure the express-session with a database solution. You can do this with mongo using this package https://github.com/jdesboeufs/connect-mongo.
However, best practice is to use something a bit more robust for this sort of use-case, like redis using this package https://github.com/tj/connect-redis.

Checking if a user is logged in with PassportJS / ExpressJS

I have an Angular app built on the MEAN stack, using Passport to log in and authenticate users. I'm a little confused about how authentication works.
I have a route that Angular passes an $http call to to check if a user is logged in (and can thus access certain pages). The route looks like this:
// route to test if the user is logged in or not
app.get('/loggedin', function(req, res) {
res.send(req.isAuthenticated() ? req.user : '0');
});
From what I've read (which is very little, I can't find isAuthenticated() anywhere in the Passport docs...), Passport should be creating a persistent session for my users.
This works for the most-part, but if I close my Chrome application / reset my computer I have to log in again. I assumed that using Passport would mean that I don't need to create a hashed cookie to store login information. Is this not the case?
Other potential cause: I'm in development at the moment and am restarting the server often. Will the Passport sessions not persist through a server restart?
Edit: Here is my session config in app.js:
var session = require('express-session');
app.use(session({ secret: 'heregoesasupersecretsecret' })); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
If you are using passport for authentication in Node.js, you can use a middleware to check the authentication. I use the below code for this.
passport.use(new LocalStrategy({passReqToCallback : true}, function(req, username, password, done) {
models.Accounts.findOne({ where: { username: username}}).then(function(user) {
if (!user) {
console.log('Unkown User');
return done(null, false, {message: 'Unknown User'});
}
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
req.session.username = req.body.username;
return done(null, user);
} else {
return done(null, false, {message: 'Invalid Password'});
}
});
}).catch(function(err) {
return done(null, false);
});
}));
You can use express-session library in Node.js to handle user sessions. You can add a middleware in your code like below.
app.use(session({
secret: 'track_courier_application_secret_key',
cookie: {
maxAge: 300000
},
saveUninitialized: true,
resave: true
}));
In the above code I'm setting maxAge to 300K milli seconds. That is 5 minutes. After this a session object will be attached in every request to the server. You can access this session object using
req.session
Now you can write a get request like below to check if a user is logged in or not.
app.get('/accounts/isloggedin', function(req, res) {
if(req.session.username)
res.status(200).send('Hurray!');
else
res.status(401).send('User not logged in.');
});
You need to use a proper session store to put the sessions in to persist them between requests. Since you're using MongoDB, you could use connect-mongo.
Then you could do something like:
var session = require('express-session'),
MongoStore = require('connect-mongo')(session);
app.use(session({
secret: 'heregoesasupersecretsecret',
store: new MongoStore()
}
));
app.use(passport.initialize());
app.use(passport.session());

Staying authenticated after the page is refreshed using Passportjs

I am building a SPA using express, mongoose and passportjs.
I have created a simple schema for my user:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var User = new Schema({
username: String,
password: String,
first_name: String
}, { collection: 'users' });
User.plugin(passportLocalMongoose);
mongoose.model('User', User);
Configured passport using the User object which mongoose gave me:
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
And configured my app to authenticate the user when navigating to this route:
app.post('/login', passport.authenticate('local'), function(req, res, next) {
if (req.isAuthenticated()) {
return res.json({ state: 'success', user: { first_name: req.user.first_name } });
}
return res.json({ state: 'failure', message: 'cannot authenticate' });
});
Now I am able to successfully authenticate a user. And the browser saves a session id cookie.
My problem is that every time the user refreshes the page passport won't deserialize the user using the session id, what makes the user unauthenticated.
While accessing the req.user object in the site's root I get undefined what make me realize passport doesn't deserialize the user properly:
app.use('/', function (req, res, next) {
console.log(req.user); // prints undefined.
next();
});
What is the proper way for restoring a user after refreshing the page?
The solution is to store the sessions in the DB. I used connect-mongo.
app.js:
var mongoose = require('mongoose');
var expressSession = require('express-session');
var MongoStore = require('connect-mongo')(expressSession);
mongoose.connect(db_url, function (err) {
if (err) {
console.log(err);
}
});
app.use(expressSession({
secret: process.env.SESSION_SECRET || 'keyboard cat',
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
after this, req.isAuthenticated() returns true on every request even after refreshing the page. (Thanks to app.use(passport.session()) which comes before any route handler)
app.use('/', function (req, res, next) {
if (req.isAuthenticated()) {
// returns true if a user already logged in.
}
next();
});
Looking at your post i can guess where problem is happening. In question you have pasted your back end expressjs code, but the problem is happening in the front end.
Think of a situation when you are working on normal javascript files, you make some edit in the variable values using Chrome dev tools of firebug n all. Let's say you refresh the page, do you still see the same edited value in the view? No right. Same is the case with your view, you are temporary holding user data. Although your backend is holding the values in req.user but your front end loses it when you refresh.
So you have to do either of below two :
Store your value in cookie on successful login and erase it when
logged out. So that cookie is never going to loose data even if you
refresh the page. Access the user data from that cookie whenever you need it
Call the backend API which returns value of req.user on your every
page refresh
I am not sure what frontend framework you are using for SPA, if you are using AngularJS then you can make use of cookieStore service it does the job beautifully.

Categories

Resources