Reputation: 1074
I'm setting up a module to grant authentification by checking cookies. This module is called on each routes and if there is no authentificated session it will compare the cookies with the database and grant session. After each successful compare, the module updates both database and cookie informations and that's where I get the error "Error: Can't set headers after they are sent."
So basicly I have my route "Index" :
var check_auth = require('./middleware/check_auth');
module.exports = function(app){
app.get('/', check_auth, function(req, res){
if(req.session.userid){
res.render('index', { title: 'AUTH'});
}else{
res.render('index', { title: 'NOT AUTH'});
}
});
};
and now my module check_auth :
var mysql = require('mysql');
var crypto = require('crypto');
var bcrypt = require('bcrypt');
var pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '****',
database: 'nodejs'
});
function check_auth(req, res, next){
if(!req.session.userid){
var cookie_auth = req.signedCookies.auth;
var cookie_db_id = req.signedCookies.db_id;
if(cookie_auth && cookie_db_id){
pool.getConnection(function(err, conn) {
var db_id = false;
query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
query.on('error', function(err){
throw err;
});
query.on('result', function(row){
db_id = row.id;
db_cookie = row.cookie;
db_username = row.username;
});
query.on('end', function(result){
if(db_id && cookie_auth == db_cookie){
console.log("OK");
req.session.username = db_username;
req.session.userid = db_id;
var salt = bcrypt.genSaltSync(10);
var crypt = crypto.randomBytes(64).toString();
var hash = bcrypt.hashSync(crypt, salt);
res.cookie('auth', hash, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
pool.getConnection(function(err, conn) {
conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info){
if(err) throw err;
});
});
conn.release();
}
});
});
}
}
next();
}
module.exports = check_auth;
The error appears at the line :
res.cookie('auth', hash, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
Is this error coming because I'm writing my cookie into an asynchronous function from mysql or is it something to do with the module/routes ? Anyway, any solution are welcome.
Upvotes: 1
Views: 1993
Reputation: 470
Yes, you are correct about the asynchronous nature of the problem. A simple workaround would be to call the rendering functionality at the end of your query:
function check_auth(req, res, next){
if(!req.session.userid){
var cookie_auth = req.signedCookies.auth;
var cookie_db_id = req.signedCookies.db_id;
if(cookie_auth && cookie_db_id){
pool.getConnection(function(err, conn) {
var db_id = false;
query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
query.on('error', function(err){
throw err;
});
query.on('result', function(row){
db_id = row.id;
db_cookie = row.cookie;
db_username = row.username;
});
query.on('end', function(result){
if(db_id && cookie_auth == db_cookie){
console.log("OK");
req.session.username = db_username;
req.session.userid = db_id;
var salt = bcrypt.genSaltSync(10);
var crypt = crypto.randomBytes(64).toString();
var hash = bcrypt.hashSync(crypt, salt);
res.cookie('auth', cookie_auth, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
res.cookie('db_id', db_id, { expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)), signed: true, httpOnly: true, secure: true });
pool.getConnection(function(err, conn) {
conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info){
if(err) throw err;
});
});
conn.release();
if(req.session.userid){
res.render('index', { title: 'AUTH'});
}else{
res.render('index', { title: 'NOT AUTH'});
}
}
});
});
}
}
next();}`
Upvotes: 0
Reputation: 32118
Due to the asynchronous nature, next
will most likely be called immediately. Move your callback inside your end
handler.
function check_auth(req, res, next) {
if (!req.session.userid) {
var cookie_auth = req.signedCookies.auth;
var cookie_db_id = req.signedCookies.db_id;
if (cookie_auth && cookie_db_id) {
pool.getConnection(function(err, conn) {
if(err) return next(err);
var db_id = false;
query = conn.query('SELECT * FROM users WHERE id = ?', [cookie_db_id]);
query.on('error', function(err) {
return next(err);
});
query.on('result', function(row) {
db_id = row.id;
db_cookie = row.cookie;
db_username = row.username;
});
query.on('end', function(result) {
if (db_id && cookie_auth == db_cookie) {
console.log("OK");
req.session.username = db_username;
req.session.userid = db_id;
var salt = bcrypt.genSaltSync(10);
var crypt = crypto.randomBytes(64).toString();
var hash = bcrypt.hashSync(crypt, salt);
res.cookie('auth', cookie_auth, {
expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)),
signed: true,
httpOnly: true,
secure: true
});
res.cookie('db_id', db_id, {
expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365)),
signed: true,
httpOnly: true,
secure: true
});
pool.getConnection(function(err, conn) {
conn.query('UPDATE users SET cookie = (?) WHERE id = ?', [hash, db_id], function(err, info) {
if (err) throw err;
});
});
conn.release();
}
return next();
});
});
}
} else {
next();
}
}
Upvotes: 3