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.
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!')
}
})
I have a simple nodeJs server with express and I have an external module that handles sending the response to the requests. In order for this file to work, it needs to access express res variable. Before, I used to send this variable to the module functions to use it, but then I tried to use a middleware to store this variable in the module and not send it every time I'm calling a function, and it worked!
This is the code:
// app.js
var http = require('http');
var express = require('express');
var app = express();
var response = require('./response.js');
// store the value of 'res' in the response module
app.use(function(req, res, next) {
response.setRes(res);
next();
});
app.get("/", function(req, res){
response.success("Welcome"); // works fine
});
app.get("/images", function(req, res){
response.forbidded("Forbidded"); // works fine
});
var server = http.createServer(app);
server.listen(3000, function(){
console.log("listening... ");
});
and this is the other module
// response.js
exports.setRes = function(_res){
res = _res;
};
exports.success = function(msg){
res.status(200).send(msg);
};
exports.forbidded = function(msg){
res.status(403).send(msg);
};
My question is: Is this a good approach? and will this affect my application if the number of users increases (I'm worried that one user might get the res of another user)
I have a node express app , using express-stormpath for authentication/authorization
I have a GET route which is called with certain jquery parameters.
If the user is logged in everything is working as expected.
If not the user login screen is shown.
After stormpath authentication and authorization is done my query params are lost.
Is there any way to retain those?
app.get('/myRoute', stormpath.groupsRequired(['admin']), function(req, res){
console.log('req.query ',req.query);
//do somehting with the query data
res.sendStatus(200);
});
after authentication req.query is {}.
Any ideas?
Thank you for the question, I work at Stormpath and I'm more than happy to help. Our express-stormpath library is open source, and we're always happy to fix bugs and review pull requests.
Can you tell me which version of our library you are using? At the moment I'm not able to reproduce the problem you are seeing. Here is a quick example that I put together with the latest version, 3.0.1:
'use strict';
var express = require('express');
var stormpath = require('express-stormpath');
var app = express();
var port = process.env.PORT || 3000;
app.use(stormpath.init(app));
app.get('/admins', stormpath.groupsRequired(['admins']), function(req, res){
res.json(req.query);
});
app.on('stormpath.ready',function () {
console.log('Stormpath Ready');
});
app.listen(port, function () {
console.log('Server listening on http://localhost:' + port);
});
With this example, I do the following:
1.) Assert that I'm not logged in, by deleting all my cookies for localhost.
2.) Type /admin?foo=bar into the URL bar.
3.) I am redirected to the login page.
4.) I login with valid credentials.
5.) I am redirected to /admins?foo=bar, as expected, and I see the req.query object in the body of the page that is rendered. This is only true if the user is in the admins group, if they are not I will see the "Unauthorized" error message page.
Can you compare my steps and my example to your application, and let us know if there are any differences? Thanks!
I don't think that stormpath is removing query from request.
But we can check it by adding middlewhare before stormpath initialization:
var express = require('express');
var stormpath = require('express-stormpath');
var app = express();
// binding middleware to assign req.query to req.q param
app.use(function(req, res, next) {
req.QUERY = req.query;
next();
});
function restoreQuery(req, res, next) {
req.query = req.QUERY;
next();
}
app.use(stormpath.init(app, {
// Optional configuration options.
}));
app.get('/myRoute',
stormpath.groupsRequired(['admin']),
restoreQuery,
function(req, res){
console.log('req.query ',req.query);
//do somehting with the query data
res.sendStatus(200);
});
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
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);