Hi i am doing login application using node js (express framework) and mysql, for this using express-session. my session getting expires soon and also when there is change in code. I am using nodmon for automatic server response. I have attached my code here,
var session = require('express-session');
app.use(session({
secret: 'page builder',
resave: true,
saveUninitialized: false,
cookie: {secure: false, expires: new Date(Date.now() +
config.sessionTime)}
}));
setting session after user login:
app.post('/loginuser', function (req, res){
var datajsonEle = req.body;
con.query('SELECT * FROM users where userId=?', datajsonEle.userID, function (error, results, fields){
if (error){
throw error;
}else {
if(results.length > 0){
if(results[0].password === datajsonEle.userPassword){
sess = req.session;
sess.userInfo = results[0];
req.session.save();
res.end("success");
}else {
res.end("password");
}
}else {
res.end("userId");
}
// res.end(JSON.stringify(results));
}
});
});
When redirect to home.
var sess;
app.get('/', function (req, res, next){
sess = req.session;
console.log(sess);
if(sess.userInfo !== undefined){
console.log(sess.userInfo.initStage);
if(sess.userInfo.initStage === 0){
res.render('index', { title: sess.userInfo });
}else {
res.redirect('/home');
}
}else {
res.redirect('/login');
}
});
For every time when i reload after 5 min or changed my code and reload session getting empty. Please help me i am new to node js
The default store for express-session is the MemoryStore, which disappears when express restarts!
You need to use the store variable when initializing express-session to set the initialized store. The package has a list of options. Most require a persistent database somewhere (MongoDB, redis, memcache), but there is session-file-store if you're just trying things out locally.
Related
I have created a login module using express session. One module is for authenticating the request which acts as middleware, the code for which is:
//authenticate.js
const session = require('express-session');
function isAuthenticate(req, res, next){
if(req.session.userId===undefined || req.session.userId===null){
res.send(false) //not authorized
}
else{
next(); //authorized
}
}
module.exports = isAuthenticate;
When we log in, we add a userId property to the req.session object.
//login.js
let username = req.body.username;
let password = req.body.password;
let sql =`select user_id, name, email, username from users where username=${username} and hash_password=${password}`;
con.query(sql, (err, result, fields)=>{
if(err) throw err;
else{
if(result.length!=0){
result = JSON.parse(JSON.stringify(result));
req.session.userId = result[0].user_id; //authenticated user
res.send(result);
}
else{
res.send({
status: "invalid credentials"
})
}
}
})
The setup of the express session is done like this:
//app.js
app.use(session({
secret: process.env.SECRET_KEY,
resave: false,
saveUninitialized: true,
cookie:{ secure: false},
}))
When I test the login using ThunderClient, it works perfectly. But when I use the same on the browser, authentication always returns false even after successfully logging in.
I believe that the value of req.session.userId property is always undefined, even after storing some value in it after logging in.
What could be the reason for this?
I'm using SQL Server as database and I couldn't get the session store npm package listed for SQL Server to work. Are there any concerns with inserting the sessionID into the database directly and then verifying what the client sends as sessionID matches the database sessionID? Do I need to tell it somehow to not save the session in memory?
const uuidv1 = require('uuid/v1');
const bcrypt = require('bcrypt');
app.use(session({
genid: uuidv1,
name: 'session',
secret: 'thesecretcode',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, /*no https*/
httpOnly: true,
sameSite: true,
maxAge: 600
}
}));
app.all('*', requireAuthentication);
function requireAuthentication(req, res, next) {
console.log(req.sessionID);
if (req.session.user) {
req.session.views++;
console.log(req.session.user)
res.render('home'); /*when logged in*/
} else {
req.session.views = 1;
req.session.user = 'MrUser';
next();
}
}
app.get('/', (req, res) => {
const saltRounds = 10;
bcrypt.hash(req.sessionID, saltRounds, function(err, hash) {
let promise = insert(req.session.user, hash);
});
res.render('landing'); /*when not logged in*/
})
function insert(theuser, sessionID) {
var thequery = "insert into session (sessiondata,sessionid,lasttouchedutc) values(#theuser,#sessionID,getdate())";
return new Promise(function(resolve, reject) {
var con = new msSqlConnecter.msSqlConnecter(config);
con.connect().then(function() {
new con.Request(thequery)
.addParam("theuser", TYPES.VarChar, theuser)
.addParam("sessionID", TYPES.VarChar, sessionID)
.onComplate(function(count, datas) {
resolve(datas);
// res.end()
}).onError(function(err) {
console.log(err);
}).Run();
}).catch(function(ex) {
console.log(ex);
});
});
}
It's more performant to use Redis but what you're doing is fine. Sessions have always been managed by matching a server-side cookie value to what is typically stored in an RDBMS.
Summary:
What you're doing is fine.
Use Redis to management sessions as it is much faster than hitting SQL server or any database
I've made a basic website with nodejs and express that needs a session system so i used express-session to do that. The issue is that my session is not kept through the different pages of my website, it seems like the req.session is directly deleted after the login has been done.
Here is my code (just the important parts):
var express = require('express');
var consolidate = require('consolidate');
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/mydb";
var bodyparser = require('body-parser');
var session = require('express-session');
//var MongoStore = require('connect-mongo')(session);
var app = express();
app.engine('html', consolidate.swig);
app.set('view engine', 'html');
app.use(express.static(__dirname + '/views'));
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({extended:true}));
app.use(session({
secret: "stfkjvctygf",
resave : false,
saveUninitialized : true,
cookie: { secure: false, maxAge: 1800000 }
}));
app.post('/login',function(req,res,next){
MongoClient.connect(url, function(err, db){
if(err) throw err;
var dbo=db.db("testdb");
var user=dbo.collection("login").findOne({userid : req.body.userid, pwd : req.body.pwd}, function(err, user){
if(err) throw err;
if (user) {
req.session.user = user;
req.session.save(function() {
console.log("current user session: "+req.session.user.userid);
res.render('mainpage.html', {userid: req.session.user.userid});
});
}
else {
console.log("connection failed");
res.render('login.html', {error: 'Invalid username or password'});
}
db.close();
});
});
});
app.get('/mainpage', function(req,res,next) {
if (req.session.user) {
res.render('mainpage.html', {userid: req.session.user.userid});
}
else {
return res.status(401).send();
}
});
app.get('/profilpage', function(req,res,next){
res.render('profilpage.html', {userid: req.session.user.userid});
});
app.listen(8080);
So when the log in of an user succeeds, mainpage.html is displayed properly (the field userid is correctly field with req.session.user.userid), but when I click on another page like the profilpage or if I come back to the mainpage, the session is not saved and the userid field is incorrect.
I also want to mention that the if (req.session.user) clause
in /mainpage does not work, the else seems to be never used.
(The mongodb database and html files are correct)
Thank you.
Delete 'var user =' because you dont need to assign the query to a variable since it is using a callback for returned data.
dbo.collection("login").findOne({userid : req.body.userid, pwd : req.body.pwd}, function(err, user){
if(err) throw err;
console.log(user) //It is most likely an array. Then use index to point the session to the username as such req.session.user = user[0].username
if (user) {
req.session.user = user;
req.session.save(function() {
console.log("current user session: "+req.session.user.userid);
res.render('mainpage.html', {userid: req.session.user.userid});
});
}
else {
console.log("connection failed");
res.render('login.html', {error: 'Invalid username or password'});
}
In addition, 'if(user)' is a bad practice, because if mongodb returns an empty array, that condition is still true, so that causes error in your logic. Its better to do something like
if(data.length > 0){
//your code here. data is what is returned from your mongo Query
}
In Node.js / MongoDB I want to create an easy session and give it to another page so I can know if a user is logged in or not. I want to know if he/she can access the page or not. How can I do this?
How do I set a session and how do I get the value to check if I have to redirect the user?
Use Express.js session.
It's also worth taking a look at Passport.js which makes implementing authentication in Node.js really easy.
Express.js session example:
var express = require('express');
var session = require('express-session');
var app = express();
// Make sure this is defined before any of your routes
// that make use of the session.
app.use(session({
secret: 'keyboard cat',
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: false
}));
// Access the session as req.session
app.get('/login', function(req, res) {
req.session.user = 1;
res.end('User 1 is logged in, please go to the /discussion page.');
});
app.get('/discussion', function(req, res) {
var sess = req.session;
if (typeof sess.user === 'undefined') {
res.redirect('/login');
} else {
res.setHeader('Content-Type', 'text/html');
res.write('<p>user: ' + sess.user + '</p>');
res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
res.end();
}
});
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.