User coming as null while authenticating with passport js - node.js

I am new to Node/mongodb/passport, trying to authenticate using passport.
here is my auth.js
var User = require('../models/userModel'),
passport = require('passport'),
BasicStrategy = require('passport-http').BasicStrategy,
jwt = require('jsonwebtoken');
passport.use(new BasicStrategy(function(userid, password, done){
User.findOne({ 'userid': userid }, function(err, user){
if(err){
return done(err);
}
if(!user){
return done(null, false);
}
if(!user.verifyPassword(password)){
return done(null, false);
}
return done(null, user);
})
}));
exports.generateToken = function(req, res, next){
req.token = jwt.sign({ id:req.user.userid },'secret key', { expiresInMinutes:120 });
next();
};
exports.respond = function(req, res){
req.status(200).json({
user: req.user,
token:req.token
});
};
exports.isAuthenticated = passport.authenticate('basic', {session:false});
For database, I simply created a user to test in the following way:
db.UserSchema.insert({"userid": "hm", "password":"mm123"})
here is my userModel.js
var mongoose = require('mongoose'),
bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
userid: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
access_token: String
});
UserSchema.methods.verifyPassword = function(password, cb) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
when I send a request using postman and debug using node-inspector, user come as null in auth.js and in console
Runtime.getProperties failed.
Error: No scopes
here is the server.js
var express = require('express'),
mongoose = require('mongoose'),
bodyParser = require('body-parser'),
path = require('path'),
passport =require('passport'),
authentication = require('./auth/auth');
var db;
if (process.env.ENV == 'Test') {
db = mongoose.connect('mongodb://localhost/fxtest');
}else{
db = mongoose.connect('mongodb://localhost/fx');
}
var app = express();
var port = process.env.PORT || 3000;
app.listen(port, function(){
console.log('Running on Port using gulp:', port);
});
app.use(passport.initialize());
app.post('/auth',authentication.isAuthenticated,
authentication.generateToken, authentication.respond);
module.exports = app;
Can anyone give me any leads as to what am I missing here??

You're responding with the request object which you can't do. You need to respond on the response object.
Its also worth pointing out, that unless you have a cleanup procedure that needs to happen after you respond, you should use a return with your res object.
exports.respond = function(req, res){
// This is wrong
req.status(200).json({
user: req.user,
token:req.token
});
// This is correct
return res.status(200).json({
user: req.user,
token: req.token,
});
};

I got it resolved. The problem was I had given the collection name as UserSchema, whereas my model name was User. As stated in mongoose docs here:
Mongoose automatically looks for the plural version of your model
name
and so it was looking for users as a collection.

Related

Passport JS using express, authentication on 'Local' strategy returns "Missing Credentials" every time

My issue is that on POST, I'm getting a 'Missing Credentials' error whenever I try and authenticate using 'local' strategy. I initially thought that this was an issue with body-parser, but even after some suggestions about forcing urlencoded: true/false, I'm still not getting any differing results. My console logs are also not returning anything substantial beyond irrelevant req.body's, and I'm really not sure what else to do at this point. I've read about this being a potential issue with specific versions of Express, but I've tried downgrading/upgrading and that hasn't changed anything.
With some breakpoints, it looks like my new localStrategy in PassportConfig.js is never being called, but I'm not entirely sure if that's my error or not? Would really appreciate some pointers here.
app.js
require("./config/config");
require("./models/db");
require("./config/passportConfig");
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const passport = require("passport");
// router
var rtsIndex = require("./routes/index.router");
var app = express();
// middleware config
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(passport.initialize());
app.use(passport.session());
app.use("/api", rtsIndex);
My PassportConfig.js:
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const mongoose = require("mongoose");
var User = mongoose.model("User");
passport.use(
new LocalStrategy({ usernameField: "email" }, (username, password, done) => {
User.findOne({ email: username }, (err, user) => {
if (err) return done(err);
// if user is unknown
else if (!user)
return done(null, false, {
message: "That email address has not been registered"
});
// or if password is wrong
else if (!user.verifyPassword(password))
return done(null, false, { message: "Wrong password" });
// successful authentication
else return done(null, user);
});
})
);
My controller for a basic user registration/login system:
const mongoose = require("mongoose");
const passport = require("passport");
const _ = require("lodash");
User = require("../models/user.model");
// snipped some registration code for brevity
module.exports.authenticate = (req, res, next) => {
console.log(req.body);
// call for passport authentication
// executed passport.use from passportConfig
passport.authenticate("local", (err, user, info) => {
// Error from passport middleware
if (err) return res.status(400).json(err);
// user is registered if there is a value in 'user'
else if (user) return res.status(200).json({ token: user.generateJwt() });
// user unkown or wrong password
else return res.status(401).json(info);
})(req, res);
};
Edit: Added my user model definitions as well:
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: "Full name cannot be empty"
},
email: {
type: String,
required: "Email cannot be empty",
unique: true
},
password: {
type: String,
required: "Password cannot be empty",
minlength: [4, "Password must be at least 4 characters long"]
},
// Encryption/Decryption of password
saltSecret: String
});
// Custom validation for email
userSchema.path("email").validate(val => {
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegex.test(val);
}, "Invalid email");
// Events
userSchema.pre("save", function(next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
// Methods
userSchema.methods.verifyPassword = function(password) {
// plain password, vs encrypted password,
// called from findOne in passportConfig
return bcrypt.compareSync(password, this.password);
};
userSchema.methods.generateJwt = function() {
return jwt.sign({ _id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXP
});
};
// Can pass custom name as third paramater
// Modified with a force export for debugging
module.exports = mongoose.model("User", userSchema);
And finally, some basic routing:
const express = require("express");
const router = express.Router();
const ctrlUser = require("../controllers/user.controller");
// foreword all links with api/
router.post("/register", ctrlUser.register);
router.post("/authenticate", ctrlUser.authenticate);
// Export router for middleware so app.js can access it
module.exports = router;
I've tried the following solutions to these questions:
Passport js local strategy custom callback "Missing credentials"
passport js missing credentials
Passport returning "missing credentials" error
Passportjs local strategy missing credentials error message
With it not being the body-parser issue, I thought it might be a usernameField issue, but that doesn't seem to be the case either.
I Have tried out your code which you mention above.
And i have made little change and remove some validation.
I mention all files below:
passportMiddleware.js
import passportLocal from 'passport-local';
const localStrategy = passportLocal.Strategy;
import userModel from '../model/userModel';
import passport from 'passport';
import bcrypt from 'bcrypt';
passport.use(new localStrategy({
usernameField: 'email',
}, async (email, password, done) => {
const user = await userModel.findOne({ "email": email });
// console.log(user)
if (!user) {
return done(null, false);
}
try {
if (password === user.password) {
return done(null, user)
} else {
return done(null, false, 'password Incorrect')
}
} catch (error) {
return done(error)
}
}));
userController.js
import userModel from '../model/userModel';
import bcrypt from 'bcrypt';
import passport from 'passport';
exports.authenticate = (req, res, next) => {
res.status(200).json({ messaage: 'Authenticated', data: req.user
});
};
userModel.js
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
var userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
}
});
// Events
userSchema.pre("save", function (next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
module.exports = mongoose.model("Users", userSchema);
routes.js
router.post('/authenticate', passport.authenticate('local', { session: false }), userController.authenticate);
Try this out. Import the dependency first.
once you get success to authenticate using passport-local. you can make changes according to your criteria.
I have import dependencies in ES6 format. You just change it to ES5.

How to make API call using API token in Node.js using express, passport-local

I'm a newbie in Node.js and trying to use API token to access Grafana.
And I created one API token by following instruction from Grafana page.
However, I don't know how to make API calls from my code of node.js to access my local server of grafana page. Also, I have a local login-page by using mongoDB to manage users.
How can I make Node.js API calls to access my local server of grafana page?
Please help me out here.. I'm having hard time on this..
If you want me to show code, I can edit here..
EDIT:
This is my whole code for app.js
var io = require('socket.io');
var express = require('express');
var app = express();
var redis = require('redis');
var sys = require('util');
var fs = require('fs');
//Added for connecting login session
var http = require('http');
var server = http.createServer(app);
var path = require('path');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var flash = require('connect-flash');
var async = require('async');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
//Adding grafana
var request = require('request');
//Connecting Database (MongoDB)
mongoose.connect("my mongoDB private address");
var db = mongoose.connection;
db.once("open",function () {
console.log("DB connected!");
});
db.on("error",function (err) {
console.log("DB ERROR :", err);
});
//Setting bcrypt for password.
var bcrypt = require("bcrypt-nodejs");
//Setting userSchema for MongoDB.
var userSchema = mongoose.Schema({
email: {type:String, required:true, unique:true},
password: {type:String, required:true},
createdAt: {type:Date, default:Date.now}
});
userSchema.pre("save", function (next){
var user = this;
if(!user.isModified("password")){
return next();
} else {
user.password = bcrypt.hashSync(user.password);
return next();
}
});
//setting bcrypt for password.
userSchema.methods.authenticate = function (password) {
var user = this;
return bcrypt.compareSync(password,user.password);
};
//Setting User as userSchema.
var User = mongoose.model('user',userSchema);
io = io.listen(server);
//Setting middleware for login format.
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(methodOverride("_method"));
app.use(flash());
app.use(session({secret:'MySecret', resave: true, saveUninitialized: true}));
app.use(passport.initialize());
app.use(passport.session());
//Initializing passport.
passport.serializeUser(function(user, done) {
//console.log('serializeUser()', user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
//console.log('deserializeUser()', user);
User.findById(id, function(err, user) {
done(err, user);
});
});
var username_tmp = '';
var global_username = ''; //Global variable for username to put in the address
var pass = '';
//Initializing passport-local strategy.
var LocalStrategy = require('passport-local').Strategy;
passport.use('local-login',
new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ 'email' : email }, function(err, user) {
if (err) return done(err);
if (!user){
req.flash("email", req.body.email);
return done(null, false, req.flash('loginError', 'No user found.'));
}
if (!user.authenticate(password)){
req.flash("email", req.body.email);
return done(null, false, req.flash('loginError', 'Password does not Match.'));
}
var email_address = req.body.email;
username_tmp = email_address;
var username = email_address.substring(0, email_address.lastIndexOf("#"));
global_username = username;
pass = req.body.password;
return done(null, user);
});
}
)
);
//Check whether it is logged in or not.
//If it is not logged in(Session is out), it goes to login page
//If it is logged in(Session is still on), it goes directly to status.html
app.get('/', loggedInCheck);
app.get('/login', function (req, res) {
res.render('login/login',{email:req.flash("email")[0], loginError:req.flash('loginError')});
});
//Accessing to MongoDB to check to login or not
app.post('/login',
function (req,res,next){
next();
}, passport.authenticate('local-login', {
successRedirect : '/status',
failureRedirect : '/login',
failureFlash : true
})
);
//Creating new account
app.get('/users/new', function(req,res){
res.render('users/new', {
formData: req.flash('formData')[0],
emailError: req.flash('emailError')[0],
passwordError: req.flash('passwordError')[0]
}
);
});
//Calling status.html
app.get('/status', isLoggedIn, function(req, res){
var user_temp = {user: ''};
user_temp.user = global_username;
res.render('status/status', user_temp);
//res.redirect('/status.html?channel=' + global_username);
});
app.get('/grafana', isLoggedIn, function(req, res){
console.log('Accessing to grafana');
res.redirect('http://localhost:8080');
});
request.get('http://localhost:8080',{
auth: {
bearer: 'TOKEN HERE'
}
});
server.listen(4000);
Edited more
app.get('/grafana', isLoggedIn, function(req, res){
console.log('Accessing to grafana');
var url = 'http://localhost:8080/api/dashboards/db/test';
request.get(url,{
auth: {
bearer: 'API token from Grafana page'
}
});
res.redirect(url);
});
Thank you..
The API calls are made with HTTP requests. You can use the request package from npm.
From the docs:
You use the token in all requests in the Authorization header, like this:
GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1
Accept: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
Example (I'm using request-promise but you can use whatever you want):
let request = require('request-promise');
let url = `http://your.grafana.com/api/dashboards/db/mydash`;
//Obviously replace this with your token
let myToken = `eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk`;
request.get(url).auth(null, null, true, myToken).then(res=> { ... });
// or
request.get(url, {
auth: {
bearer: myToken
}
}).then(res=> { ... });

How do I get data from Mongoose in Express?

I am trying to get some data from Mongoose using Express. I have created a username and password form to log in to my site. It is supposed to check the credentials and let them in if the credentials are the same as the ones stored on the database, but it is letting anything in (even if I put the wrong password). My source files seem fine, and the console is not throwing any errors. The Gists to the files are below:
login.ejs
register.ejs
And the app.js file (can't gist it due to my reputation):
var bodyParser = require('body-parser');
var express = require('express');
var mongoose = require('mongoose');
var app = express();
var options = {
server: {
socketOptions: {
keepAlive: 300000, connectTimeoutMS: 30000
}
},
replset: {
socketOptions: {
keepAlive: 300000,
connectTimeoutMS : 30000
}
}
};
var db = mongoose.connect('db_uri', options);
var Schema = mongoose.Schema;
var UserSchema = new Schema({
email: String,
password: String
});
var User = mongoose.model('Users', UserSchema);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname+'/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname+'/public/templates');
app.get('/', function (req, res) {
res.render('index');
});
app.get('/registerPage', function (req, res) {
res.render('register') ;
});
app.get('/loginPage', function (req, res) {
res.render('login');
});
app.post('/register', function (req, res) {
var email = req.body.email;
var password = req.body.password;
var user = new User({
email: email,
password: password
});
user.save(function (err) {
if (err) throw err;
console.dir(user);
res.send('Account created successfully (email): ' + email);
});
});
app.post('/login', function (req, res) {
var email = req.body.email;
var password = req.body.password;
User.findOne( {email: email, password: password}, function (err) {
if (err) throw err;
res.render('portal');
});
});
app.listen(3000, function () {
console.log('Express server listening on http://localhost:3000');
});
Thanks for your help!
In the login handler, you're not actually checking if the query matches a user.
Perhaps you assume that err will be set in that case, but it won't be, because the query itself was performed successfully (it just didn't return any records).
You need to add an additional check to see if the query returned a valid user:
User.findOne( {email: email, password: password}, function (err, user) {
if (err) throw err;
if (! user) return res.send(401);
res.render('portal');
});
This will return a 401 ("Unauthorized") when the query didn't match a user.

express and socket.io authentication -- how do I retrieve the cookie/session info?

Preface: I'm new to node.js, express, socket.io and all of that. I realize my code is kinda messy and needs to be separated out into modules, etc -- but I've not done that yet because I was trying to get the authentication part working first. I have searched all over stackoverflow and other sites. I've found some promising examples, but I've just not been able to make it work.
I followed a couple of tutorials to create my express app which allows a user to register, login, and view their details. I also followed a tutorial which helped me make a basic socket.io chat. What I'm trying to do is combine them and have the user log in and then be redirected to the chat app. The problem I have is that when I redirect them, I have no way to know "who they are" on the socket.io side of things. Currently I've got it set up so you have to enter your name to chat -- I would like it to grab the info from the session and use that instead.
Currently when a user logs in, it does set a cookie (I can view it in the console). So I know the cookie is there. It also sets the info into the MongoStore. I've verified that as well with db.collection.find().
Here's the code that I have so far. If any of the experts out there could help me find a way to pass the session info over to socket.io, I would very much appreciate it!
var mongo = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var bcrypt = require('bcryptjs');
var csrf = require('csurf');
var path = require ('path');
var express = require('express');
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var session = require('express-session');
var moment = require('moment');
var now = moment().format('L');
var http = require('http');
var MongoStore = require('connect-mongo')(session);
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
UserSchema = new Schema({
//id: ObjectId,
firstName: String,
lastName: String,
username: {
type: String,
unique: true,
uniqueCaseInsensitive:true
},
password: String,
email: {
type:String,
unique: true,
uniqueCaseInsensitive:true
},
accountType: String,
accountStatus: String,
acctActivation:{
type:String,
unique:true
},
joinDate: String
});
UserSchema.plugin(uniqueValidator,{ message: 'Error, {PATH} {VALUE} has already been registered.\r' });
var User = mongoose.model('User', UserSchema);
var app = express();
app.engine('ejs', require('ejs').renderFile);
app.locals.pretty = true;
//connect to mongo
mongoose.connect('mongodb://localhost/myUserDb');
//create server
var server = http.createServer(app).listen(3000);
var client = require('socket.io')(server);
console.log('listening on port 3000');
//middleware
app.use(express.static('public'));
app.use(bodyParser.urlencoded({extended:true}));
app.use(session({
secret: 'mysecret!',
resave:false,
saveUninitialized: false,
stringify:true,
store: new MongoStore({
url: 'mongodb://127.0.0.1/sid2'
})
}));
app.use(csrf());
app.use(function(req,res,next){ // check to see if user already has a session, if so, query mongodb and update the user object
if(req.session && req.session.user){
User.findOne({email: req.session.user.email}, function(err, user){
if(user){
req.user = user;
delete req.user.password; // remove password field from session
req.session.user = req.user;
res.locals.user = req.user;
}
next();
});
}else{
next();
}
});
function requireLogin(req,res,next){ // check to see if user is logged in, if not, boot em
if(!req.user){
res.redirect('/login');
}else{
next();
}
};
function requireAdmin(req,res,next){ // check to see if accountType = Developer (or admin later) - if not, send them to dashboard
if(req.user.accountType !== 'Developer'){
res.redirect('/dashboard');
}else{
next();
}
};
app.get('/', function(req, res){
if(req.user){
res.render('dashboard.ejs');
}else{
res.render('index.ejs');
}
});
app.get('/register', function(req,res){
res.render('register.ejs', {csrfToken: req.csrfToken(),
error:false});
});
app.post('/register', function(req,res){
var hash = bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10));
var user = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
username: req.body.username,
password: hash,
email: req.body.email,
accountType: 'Standard',
accountStatus: 'Active',
joinDate: now
});
user.save(function(err){
if(err){
console.log(err);
res.render('register.ejs', {csrfToken: req.csrfToken(),
error: err});
}else{
req.session.user = user;
res.redirect('/dashboard');
}
});
});
app.get('/login', function(req,res){
res.render('login.ejs', {
csrfToken: req.csrfToken(),error:false});
});
app.post('/login', function(req, res){
User.findOne({username: {$regex: new RegExp('^' + req.body.username, 'i')}}, function(err, user){
if(!user){
res.render('login.ejs', {error: 'Invalid username or password combination.',
csrfToken: req.csrfToken()});
}else{
if(bcrypt.compareSync(req.body.password, user.password)){
req.session.user = user;
res.redirect('/chat');
}else{
res.render('login.ejs', {error: 'Invalid username or password combination.',
csrfToken: req.csrfToken()});
}
}
});
});
app.get('/dashboard', requireLogin, function(req,res){
res.render('dashboard.ejs');
});
app.get('/chat', requireLogin, function(req,res){
res.render('chat.ejs');
});
app.get('/admin', requireLogin, requireAdmin, function(req,res){ //required logged in AND admin status
// var userlist = User.find({});
User.find({},{},function(err,docs){
res.render('admin.ejs',{ "userlist": docs
});
}) ;
// res.render('admin.ejs');
});
app.get('/logout', function(req,res){
req.session.reset();
res.redirect('/');
});
mongo.connect('mongodb://127.0.0.1/chat', function(err,db){
if(err) throw err;
client.on('connection', function(socket){
var col = db.collection('messages');
sendStatus = function(s){
socket.emit('status', s);
};
//emit all messages (shows old room data)
col.find().limit(100).sort({_id: 1}).toArray(function(err, res){
if(err) throw err;
socket.emit('output',res);
});
//wait for input
socket.on('input', function(data){
var name = data.name,
message = data.message,
whitespacePattern = /^\s*$/;
if(whitespacePattern.test(name) || whitespacePattern.test(message)){
sendStatus('Name and message is required.');
}else{
col.insert({name: name, message: message}, function(){
//emit latest message to all clients
client.emit('output', [data]);
sendStatus({
message: "Message sent",
clear: true
});
});
}
});
});
});
Ok, so I've finally figured it out. I used express-session to set the cookie, and then a module called express-socket.io-session to get it over to socket.io.
From there, I was able to use:
var data = socket.handshake.session;
console.log(data.user.username);
to retrieve the values I needed. All of these days searching and it was a very simple solution. I guess I just needed some sleep!

express-session won't log out

The code
app.js:
var express = require('express');
var session = require('express-session');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var passport = require('passport');
var config = require('./config');
var routes = require('./routes');
var mongodb = mongoose.connect(config.mongodb);
var app = express();
// view engine setup
app.set('views', config.root + '/views');
app.set('view engine', 'jade');
app.engine('html', require('ejs').renderFile);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(config.root + '/public'));
app.use(session({
name: 'myCookie',
secret: 'tehSecret',
resave: true,
saveUninitialized: true,
unset: 'destroy',
store: new mongoStore({
db: mongodb.connection.db,
collection: 'sessions'
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use('/', routes);
app.set('port', config.port);
var server = app.listen(app.get('port'), function() {
if (config.debug) {
debug('Express server listening on port ' + server.address().port);
}
});
routes.js:
var express = require('express');
var router = express.Router();
var config = require('../config');
var userController = require('../controllers/user');
var authController = require('../controllers/auth');
router.get('/', function(req, res) {
res.render('index', {
title: config.app.name
});
});
router.route('/users')
.post(userController.postUsers)
.get(authController.isAuthenticated, userController.getUsers);
router.get('/signout', userController.signout);
module.exports = router;
models/user.js:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
}
});
// Execute before each user.save() call
UserSchema.pre('save', function(callback) {
var user = this;
// Break out if the password hasn't changed
if (!user.isModified('password')) return callback();
// Password changed so we need to hash it
bcrypt.genSalt(5, function(err, salt) {
if (err) return callback(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return callback(err);
user.password = hash;
callback();
});
});
});
UserSchema.methods.verifyPassword = function(password, cb) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
// Export the Mongoose model
module.exports = mongoose.model('User', UserSchema);
controllers/user.js:
var config = require('../config');
var User = require('../models/user');
exports.postUsers = function(req, res) {
if (config.debug)
console.log("user.postUsers()");
var user = new User({
username: req.body.username,
password: req.body.password
});
user.save(function(err) {
if (err)
return res.send(err);
if (config.debug)
console.log("saved");
res.json({
message: 'New user created!'
});
});
};
exports.getUsers = function(req, res) {
if (config.debug)
console.log("user.getUsers()");
User.find(function(err, users) {
if (err)
return res.send(err);
if (config.debug)
console.log("users", users);
res.json(users);
});
};
exports.signout = function(req, res) {
if (config.debug)
console.log("user.signout()");
res.clearCookie('myCookie');
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
};
controllers/auth.js:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var config = require('../config');
var User = require('../models/user');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new BasicStrategy(
function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(err);
}
// No user found with that username
if (!user) {
return done(null, false);
}
// Make sure the password is correct
user.verifyPassword(password, function(err, isMatch) {
if (err) {
return done(err);
}
// Password did not match
if (!isMatch) {
return done(null, false);
}
// Success
return done(null, user);
});
});
}
));
exports.isAuthenticated = passport.authenticate('basic', {
session: false
});
The problem
/signout route does not end the current session. In the req.session.destroy callback the req.session is undefined, yet a new GET request to /users acts like the session is valid.
Can someone help clear this problem out?
If, like me, you came here as a result of question title rather than full details- the answer is req.session.destroy(). I think the logout function is particular to passport.js and will not work if you are using standard express-session.
Solution
controllers/user.js:
exports.signout = function(req, res) {
if (config.debug)
console.log("user.signout()");
req.logout();
res.send(401);
};
Btw. don't mind the session(s) still being in DB immediately after the logout. Mongod checks and clears those out after 60 s.
in sign out api without using req.session.destroy() try req.logout();. I hope it will work.
In my case the server-side code was fine. It was the client-side code where I wasn't including the withCredentials parameter when making the http request.
Below is the correct working code.
// server side (nodejs)
authRouter.post("/logout",
passport.session(),
checkAuthenticationHandler,
async (req, res, next) => {
req.logOut(err => {
if (err) next(err)
res.status(http.statusCodes.NO_CONTENT).end()
})
})
// client side (reactjs)
export const logout = async () => {
const _response = await axios({
method: 'post',
url: `${authApi}/auth/logout`,
withCredentials: true
})
}

Resources