I'm building a website using Node.js, Express, and Redis for session management. For whatever reason, if I have a session variable (isLoggedIn in this example), and I refresh the page, the variable doesn't get saved, however, if I call req.session.save() after setting the variable, it does get saved to Redis (redis-cli monitor shows this - not calling save() shows that the variable isn't there, whereas calling save() shows it).
I'm using this to set up and start the server:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var swig = require('swig');
var session = require('express-session')
var RedisStore = require('connect-redis')(session);
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// Configure the favicon first. This avoids other middleware from processing the request if we know the request is for the favicon.
app.use(favicon(__dirname + '/public/images/favicon.ico'));
// All other requests will require everything else.
// Set up the view engine.
app.set('view engine', 'html');
app.set('views', path.join(__dirname, '/views'));
app.engine('html', swig.renderFile);
// Set up our logger.
app.use(logger('dev'));
// Set up JSON parsing.
app.use(bodyParser.json());
// Set up encoded URL parsing.
app.use(bodyParser.urlencoded());
// Set up the cookie parser.
app.use(cookieParser('thedogsleepsatnight'));
// Set up our session store. This will use Redis as our session management.
app.use(session({
resave: true,
saveUninitialized: true,
secret: "thedogsleepsatnight",
store: new RedisStore()
}));
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
And then, in that route, I have:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
console.log(req.session.isLoggedIn);
if (req.session.isLoggedIn) {
console.log("Logged in!");
} else {
console.log("Not logged in");
}
res.render('index');
});
router.post('/login', function(req, res) {
console.log("Going to set isLoggedIn. Currently: " + req.session.isLoggedIn);
req.session.isLoggedIn = true;
console.log("Set isLoggedIn. Currently: " + req.session.isLoggedIn);
});
module.exports = router;
From that, I should be able to navigate to /login, have the session set isLoggedIn to true, and that should save automatically to Redis. After that, heading to / should tell me that I'm logged in. Loading /login does set the variable, the second log shows that, but loading / says that I'm not logged in. redis-cli monitor shows
1414076171.241836 "setex" "sess:FIDJ9qDbX_0u9pzlC6VZEW76zZcyiPME" "86400" "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"}}"
upon saving, which doesn't include the isLoggedIn variable, but adding in req.session.save() shows:
1414076475.601644 "setex" "sess:FIDJ9qDbX_0u9pzlC6VZEW76zZcyiPME" "86400" "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"isLoggedIn\":true}"
Any idea on why I have to call req.session.save() when all of the examples I've seen don't use it?
Okay, so, I've figured it out. I'll put the answer here for anyone else that happens to get caught on this.
For GET requests, the server assumes that you're going to be sending data back, and will automatically save session data once the route is fully processed.
For POST requests (what I'm using), however, the same assumption isn't made. Session states are only saved in one of two conditions - either when data is being sent out (through res.send, res.redirect, etc.), or if you manually call req.session.save(). I was already calling /login from an AJAX request, I just wasn't returning anything if certain conditions were met. Having the server respond to the client with some data after setting the session variable fixed this.
If the brilliant answer from #thatWiseGuy is not solving the problem ... here is a hint.
I called a axios POST request from document loading:
document.addEventListener('DOMContentLoaded', function () {
axios.post('/do-magic', {
data: data
}, {withCredentials: true, credentials: 'include'}).then(res => {
// even more magic
}).catch(err => console.error(err));
}, false);
On server side, in the "do-magic" route, whenever I set a session and hit save, was not saved with the code below.
req.session.isMagic = true;
req.session.save();
I am not sure what is the reason behind, but when I invoked this call delayed, it was working properly and saving my session.
document.addEventListener('DOMContentLoaded', function () {
window.setTimeout(() => {
axios.post('/do-magic', {data: data}, {withCredentials: true, credentials: 'include'}).then(res => {
// even more magic
}).catch(err => console.error(err));
}, false);
}, 1000);
Related
Let say that I have "home" route and "food" route. In "home" route I collected user data from database, I want to pass username to "food" route. I want to use session to do that, but I didn`t find solution for that. In PHP is very easy by using
$_SESSION['username'] = username;
From google I learned that in node.js I can do something like this
const express = require('express');
const session = require('express-session');
const app = express();
var sess;
app.get('/',function(req,res){
sess=req.session;
sess.email; // equivalent to $_SESSION['email'] in PHP.
sess.username; // equivalent to $_SESSION['username'] in PHP.
});
But I learned as well that using a global variable for the session won’t work for multiple users. You will receive the same session information for all of the users.
This person recommend to use Redis, but it require to install another software. Is there any better way to save data to session variables in node.js and pass it to another route similar to what I can do in PHP?
You have to create a session object and pass it to expressjs app in order to access the sessions.
This is the example from the documentation:
var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
And then you can access the session from the request object in handlers:
app.get('/', function(req, res, next) {
if (req.session.views) {
req.session.views++
res.setHeader('Content-Type', 'text/html')
res.write('<p>views: ' + req.session.views + '</p>')
res.write('<p>expires in: ' + (req.session.cookie.maxAge / 1000) + 's</p>')
res.end()
} else {
req.session.views = 1
res.end('welcome to the session demo. refresh!')
}
})
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I am developing an app in node.js and using expressJs, but I am a PHP developer, I know that in PHP we can use session to implement login and I thought in node.js case is the same, so as I searched I found the express-session module, and I tried to implement this way.
This app.js file
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs');
app.set('views', 'app/views');
app.use(require('./routes/login_api'));
app.use(require('./routes/login'));
app.use(require('./routes/Index_api'));
app.use(require('./routes/index'));
var server = app.listen(app.get('port'), () => {
console.log('Server is listening on ' + app.get('port'));
});
So as you can see I have four different routes, /routes/login renders the login.ejs template, /routes/login_api handles the requests and response of the login page,
/routes/Index_api, /routes/Index_api (which is my dashboard), is the same way. This is `login_api
var express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
var session = require('express-session');
var mysql = require('mysql');
var conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'fmis'
});
conn.connect();
router.use(bodyParser.urlencoded({
extended: false
}));
router.use(bodyParser.json());
router.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true
}));
router.post('/login_api', function(req, res) {
var Username = req.body.Username;
var Password = req.body.Password;
var query = "SELECT `User_Id`, `User_Username`, `User_Password`, `User_Name`, `User_Last_Name`, `User_Type`, `User_Photo` FROM `users` WHERE `User_Deleted` = 0 AND `User_Username`='" + Username + "' AND `User_Password` = '" + Password + "'";
conn.query(query, function(err, result) {
if (err) {
console.error(err);
res.end();
} else {
if (result.length == 1) {
req.session.Username = result[0].User_Username;
req.session.Password = result[0].User_Password;
req.session.UserType = result[0].User_Type;
}
}
console.log(req.session.Username); // gives the right result
});
console.log(req.session.Username); // gives the undefined
});
As far as I know the session.Username is a global variable (as it is in PHP)
so it should be accessible in everywhere but when I access it in routes/index route it gives the undefined result, not even that if write the console.log(req.session.Username); inside query callback function it gives the right result which is returned from the database but when I place it out side query callback function it gives undefined result.
So any suggesstion please help me
You need to understand the async and non-blocking nature of Node.js. When you call conn.query the process does not block and moves on to the next statement which is console.log (outside the callback). Most probably the callback hasn't been called yet. Callback will be called at an arbitrary time when either the result or error is available. That's why you see that "weird" behaviour. The order of execution is not necessarily the order of statements.
A Quick Solution:
I recommend Passport. It is popular and easy to use for handling authentication.
You can use it as a middleware which is cleaner and get access to the current user simply by req.user which is available in the request context. It has different authentication strategies, including OAuth, and easy to work with.
NOTE: It requires the session and cookie-parser.
In your particular application see:
LocalStrategy to load user details from database;
de/serializeUser for reading and writing user data from session;
Also you need to use passport.initialize() and passport.session() middlewares. See the project's page for details on how to use it.
I am new to Node and Express.
I've got a static html page where the users posts his username via ajax to my server. Then I want to redirect him to another html file.
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + "/public/arena.html"));
app.get('/',function(req,res){
res.sendFile(__dirname + "/public/index.html");
});
app.post('/login',function(req,res){
var username=req.body.username;
console.log("User name = "+username);
res.redirect(__dirname + "/public/arena.html");
});
var server = app.listen(3000);
I get the username and also the respond in the browser but the server does not redirect me to arena.html. I also don't get any errors.
Why are these "easy" things so difficult in Node?
Thank you guys so much for your help.
The problem in this case is that it looks like you had some test (debugging?) code inserted into your POST route that is stopping the redirect call from running.
Here's the modified (corrected) version of your program, which will redirect the user in the way you want:
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(__dirname + "/public/arena.html"));
app.get('/', function(req, res) {
res.sendFile(__dirname + "/public/index.html");
});
app.get('/arena', function(req, res) {
res.sendFile(__dirname + "/public/arena.html");
});
app.post('/login', function(req, res) {
var username = req.body.username;
console.log("User name = " + username);
// Note how I'm redirecting the user to the /arena URL.
// When you issue a redirect, you MUST redirect the user
// to a webpage on your site. You can't redirect them to
// a file you have on your disk.
res.redirect("/arena");
});
app.listen(3000);
I had to do a couple of things to get this working:
Get rid of your call to res.end. Whenever you call res.end, it will END the request, so any code that happens AFTER that call in the route will not run.
I had to create a new route for /arena. This just renders the arena.html file that you've created. This is required if you want to 'redirect' the user to an arena page.
I had to update your redirect code to actually redirect the user to /arena (the new route I created in step 2), so that the user will then hit your /arena route, and finally get back the template you are trying to show them.
Your res.redirect function is never executed because you are returning from function right before that statement.
You pass a URL to res.redirect(). That URL should be a URL that you have an appropriate route for that will serve the desired file.
Instead you are doing:
res.redirect(__dirname + "/public/arena.html");
But, that isn't a URL at all. That's a path name on your local hard disk. res.redirect() sends a URL back to the browser and, if the browser is following redirects, it will then request that URL from scratch as a branch new request. So, you need to send a URL (not a path) and you need to send a URL that you have a route configured for that will serve the desired file.
It also looks like your express.static() statements might be incorrect. For us to help more specifically with those, we need to know where the static HTML files are on your hard drive relative to __dirname and we need to know exactly how you want the URLs for them to work. For example, do you want a request for /arena.html to serve __dirname + /public/arena.html? Is that what you are trying to do? Please explain that part so we can advise more specifically on your express.static() statements.
If that is the case, then you can change your redirect to:
res.redirect("/arena.html");
I'm trying to get sessions variables working in my Express Node.js project, using the express-session module. I'm not getting any errors, and setting session variables seems to work, however they don't persist across requests. Here's a simplified bit of my code:
server.js
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var app = express();
app.use(bodyParser.json());
// Sessions
app.use(session({
secret: config.secret
}));
app.use('/api/projects', require('./controllers/api/projects'));
var server = app.listen(3000, function() {
console.log('Server listening on', 3000)
});
api/projects.js router
var router = require('express').Router()
router.get('/set', function(req, res, next) {
req.session.test = "test";
res.status(200).json({"test":req.session.test});
});
router.get('/current', function(req, res, next) {
res.status(200).json({"test":req.session.test});
})
Setting the variable seems to works...
The /set API call should set a session variable called test. This API call appears to work when I try it with curl:
curl -X GET localhost:3000/api/projects/set --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{"test":"test"}
This is also confirmed in the console log:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
test: 'test' }
...however next time I check it it's not set
When I try to check the variable with my second API call, it appears to be undeclared:
curl -X GET localhost:3000/api/projects/current --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{}
This is confirmed in the console log, the test variable is no longer set:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
How can I ensure my sessions variables persist across requests?
(PS: I've been stuck on this for a while and any small comment on hints or resolution is very welcome, even if you're not sure you've got the answer)
I can see two possible reasons for this.
First, you could've messed up with cookies in curl.
You could ensure that you've set your cookies correctly by checking http response. If it contains Set-Cookie header with a new connect.sid then you did something wrong.
Alternatively, you could use a web browser with native cookies support to guard yourself against such mistakes.
Second, you may've restarted your sever between two requests.
Since you didn't specify any persistent store for your sessions, no data will persist between node.js server restarts.
If you want session data to persist after node.js server stops or restarts, you should consider using some persistent session store (i.e. redis-store).
I am working on a website with the help of express.js and mongodb.
To use this website persistently I am using express-session v1.11.3. And, to check if a user is logged in or not, I have created a middleware function. In this function I am trying to get a session but it always returns Undefined.
When login to the router, I have an initialized session variable with user obj. as below:
//users router
router.all('/login', function(req, res) {
var db = req.db;
.....
........
//on successfully login
req.session.user = user;
res.redirect('/dashboard');
});
After I created this middleware function in the users.js router file to authenticate. Here is the code of this function:
function requiredAuthentication(req, res, next) {
if (req.session.user) {
next();
} else {
res.redirect("/users/login");
}
}
Now, I am trying to get users listing and only authenicated users get this:
router.get('/index',requiredAuthentication, function(req, res, next) {
var db = req.db;
db.collection('users').find().toArray(function(err, users) {
if (err) {
res.render('error', {error: err});
}else {
res.render('users/index', {users: users});
}
});
});
But it's not working properly.
The code parts that you've posted seems ok, the problem might be in other code that is not visible here.
First of all make sure that you've assigned the req.session.user correctly, inside the asynchronous callback where user is fetched from the db.
Also check whether the express-session middleware is included properly, don't forget to use(session) middleware, like:
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
Also, try to include and use(express-cookies) middleware, this was an issue in previous express versions, but now it's used automatically by express-session.
a good practice would be to also check if a session exists before you check if a user is added to a session var.
if (req.session && req.session.user) {
//do something
}
that could also indicate more on where the problem lays.
also . try putting the function in the controller and see if it still doesn't work for you. (instead of passing it in the route)
check to see if you correctly applied all the modules needed (express-session - and is up to date and not corrupted. (update to latest version if no)
make sure your middleware is set correctly
app.use(session({
secret: '',
saveUninitialized: true/false,
resave: true/false
}));
if you did checked all of the above, take a look at the console of your server. does it output any error regarding the session var or decleration.? if so please post
hope this guide you somehow. cheers