passport js deserialize user, variable undefined - node.js

I'm trying to build a backend for a web app, but I'm stuck on passport. Right now I can register a user and log that user in, but I can not log in twice in a row. Whenever deserialize user is called I get "TypeError: Cannot read property 'findOne' of undefined" I see that user is undefined but it is defined when I call it on login() and signup()
var login = require('./login');
var signup = require('./signup');
module.exports = function (passport: any, user: any) {
// Passport needs to be able to serialize and deserialize users to support persistent login sessions
passport.serializeUser(function (user: any, done: any) {
done(undefined, user.username);
});
// deserialize user
passport.deserializeUser(function (id: any, done: any) {
user.findOne(id).then(function (user: any) {
if (user) {
done(null, user.get());
} else {
done("error", undefined);
}
});
});
// Setting up Passport Strategies for Login and SignUp/Registration
login(passport, user);
signup(passport, user);
}
in my app.ts file i implement the strategies like this:
var models = require("./config/models/User");
require('./config/passport/init')(passport, models.user);
signup.js
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport : any, user : any) {
var User = user
passport.use('signup', new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
}, function(req: Request, username: string, password: string, done: any) {
var User = user
console.log("signup"+User)
var generateHash = function(password: string) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
username: username
}
}).then(function(user: any) {
if (user)
{
console.log('already taken')
return done(null, false, {
message: 'That email is already taken'
});
} else {
var userPassword = generateHash(password);
var data =
{
username: username,
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname
};
User.create(data).then(function(newUser: any, created: boolean) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
}
));
}
login.js
import { passport } from '../../app'
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport : any, user : any) {
//LOCAL SIGNIN
passport.use('login', new LocalStrategy(
{
// by default, local strategy uses username and password, we will override with email
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req : Request, username : string, password : string, done : any) {
var User = user;
console.log("login: "+User)
var isValidPassword = function(userpass : string, password : string) {
return bCrypt.compareSync(password, userpass);
}
User.findOne({
where: {
username: username
}
}).then(function(user : any) {
if (!user) {
return done(null, false, {
message: 'User does not exist'
});
}
if (!isValidPassword(user.password, password)) {
return done(null, false, {
message: 'Incorrect password.'
});
}
var userinfo = user.get();
return done(null, userinfo);
}).catch(function(err : Error) {
console.log("Error:", err);
return done(null, false, {
message: 'Something went wrong with your Signin'
});
});
}
));
}

I solved it myself,
I imported the model like so:
var models = require("../models/User");
then I changed the findOne call like so:
models.user.findOne({ where: {username: id} }).then(function (user: any) {
if (user) {
done(null, user.get());
} else {
done("error", undefined);
}
});

Related

How can I add a simple middleware that will verify JWT and be ensure that is authorized?

I have the following middleware that works for authentication with JWT and passport.js. The thing is that I also need somehow verify for all controllers if the user is admin or not. I am using this passport.js middleware for authentication:
if (typeof app == "function") {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function (user, done) {
done(null, JSON.parse(user));
});
var opts = {};
opts.jwtFromRequest = passportJwtExctract.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.JWT_SECRET;
passport.use(
new passportJwtStrategy(opts, async (jwt_payload, done) => {
var user = await User.findByPk(jwt_payload.id);
if (user === null) {
return done(null, false, {
message: "Sorry, we couldn't find an account.",
});
}
done(null, user);
await User.update(
{ last_signin_date: "now()" },
{
where: {
id: user.id,
},
}
);
return;
})
);
passport.use(
new passportLocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
function (username, password, done) {
process.nextTick(async function () {
var valid =
validator.isEmail(username) && validator.isLength(password, 8);
if (!valid) {
return done(null, false, {
message: "Incorrect username or password",
});
}
username = username.toLowerCase();
let user = await User.findOne({ where: { email: username } });
user = user.toJSON();
if (user === undefined) {
return done(null, false, {
message: "Sorry, we couldn't find an account with that email.",
});
}
var hashed_password = await bcrypt.hash(password, user.salt);
if (hashed_password == user.password) {
delete user.password;
delete user.salt;
user.user_mode = process.env.USER_MODE;
user.token = jwtLib.sign(user, process.env.JWT_SECRET);
//l('done user', user)
done(null, user);
await User.update(
{ last_signin_date: "now()" },
{
where: {
id: user.id,
},
}
);
return;
}
return done(null, false, {
message: "Sorry, that password isn't right.",
});
});
}
)
);
}
How can I verify JWT correctly for all related requests and be sure that the user is admin? Something like the bellow option.
Common.ensureAuthenticated("Administrator"),
you can investigate about Outh2 authentication where in JWT token you can claim number of parameter according to need and at time of verification you can validate it and extract and use it everywhere you want !
For admin and different different role you can define "actor" as key and its role in respect to priority as value and create a check actor flag at run time !
I'm assuming your application starts from index.js In index.js, you can use a middleware before initiating your routes.
For example:
const express = require('express');
const app = express();
const router = require('./src/router'); // Your router file (router.js)
app.use(AuthMiddleware); // This is how you use your middleware
app.use('/', router);

passport local not working for multiple model

I have used passport local for authenticate purpose but not working.It is working only one from these two(user and admin).I do not know why it is working like this.If anyone know please help to find solution.
passportconfig.js:
const passportuser = require('passport');
const localStrategyuser = require('passport-local').Strategy;
const mongooseuser = require('mongoose');
const passportAdmin = require('passport');
const localStrategyAdmin = require('passport-local').Strategy;
const mongooseadmin = require('mongoose');
var User = mongooseuser.model('User');
var Admin = mongooseadmin .model('Admin');
passportuser.use(
new localStrategyuser({ usernameField: 'email' },
(username, password, done) => {
User.findOne({ email: username },
(err, user) => {
if (err) { return done(err); } else if (!user) {
return done(null, false, { message: 'Email is not registered for User' });
} else if (!user.verifyPassword(password)) {
return done(null, false, { message: 'Wrong password.' });
} else {
return done(null, user);
}
});
})
);
passportAdmin.use(
new localStrategyAdmin({ usernameField: 'email' },
(username, password, done) => {
Admin.findOne({ email: username },
(err, admin) => {
if (err)
return done(err);
// unknown user
else if (!admin)
return done(null, false, { message: 'Email is not registered for Admin' });
// wrong password
else if (!admin.verifyPassword(password))
return done(null, false, { message: 'Wrong password.' });
// authentication succeeded
else
return done(null, admin);
});
})
);
Like #Panther mentioned in the comment, passportuser and passportAdmin is just the same module, you have to create separate Passport instances instead of using the default one
const { Passport } = require('passport');
const passportuser = new Passport();
const passportAdmin = new Passport();
And also as #Panther mentioned, there is no need to require('mongoose') multiple times. This would work equally well:
const mongoose = require('mongoose');
const User = mongoose.model('User');
const Admin = mongoose.model('Admin');

Is there a way of defining what collection to use when using passport.authenticate

Within my project I have both hcpuser and regular user. I have got the registration working for HCP but when i go to do my login function it still only reads from my users collection and not from the hcpuser I want it to. Is there a simple line of code I can declare before my function that allows this.
Hcp model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcryptjs');
var User = require('../model/user.model').schema
var schema = new Schema({
email : {type:String, require:true},
username: {type:String, require:true},
password:{type:String, require:true},
creation_dt:{type:Date, require:true},
hcp : {type:Boolean, require : true},
clinic:{type:String, require:true},
patients: [User],
});
schema.statics.hashPassword = function hashPassword(password){
return bcrypt.hashSync(password,10);
}
schema.methods.isValid = function(hashedpassword){
return bcrypt.compareSync(hashedpassword, this.password);
}
schema.set('collection', 'hcpuser');
module.exports = mongoose.model('Hcpuser',schema);
Hcp controller with first register function working as expected.
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const router = express.Router();
const Hcpusermodel = mongoose.model("Hcpuser")
const {ObjectId} = require("mongodb");
var Hcpuser = require('../model/hcp.model')
var passport = require('passport');
router.post('/register', function (req, res, next) {
addToDB(req, res);
});
async function addToDB(req, res) {
var hcpuser = new Hcpuser({
email: req.body.email,
hcp : true,
username: req.body.username,
password: Hcpuser.hashPassword(req.body.password),
clinic: req.body.clinic,
creation_dt: Date.now()
});
try {
doc = await hcpuser.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
//login
router.post('/login', function(req,res,next){
passport.authenticate('local', function(err, hcpuser, info) {
if (err) { return res.status(501).json(err); }
if (!hcpuser) { return res.status(501).json(info); }
req.logIn(hcpuser, function(err) {
if (err) { return res.status(501).json(err); }
return res.status(200).json({message:'Login Success'});
});
})(req, res, next);
});
From your question, you either want to auth one OR the other, or check both - I think you're asking for how to auth separately (one OR the other, not both)?
please note, this specific code has been untested, but the principles are there and still stand.
One OR the Other
You need to define the name of each strategy in your passport code.
For example:
passport.use('users', new LocalStrategy({
usernameField: 'user[email]',
passwordField: 'user[password]',
},(email, password, done) => {
Users.findOne({ email })
.then((user) => {
if(!user || !user.validatePassword(password)) {
return done(null, false, { errors: { 'email or password' : 'is valid' } });
}
return done(null, user);
}).catch(done);
}));
passport.use('hcpusers', new LocalStrategy({
usernameField: 'user[email]',
passwordField: 'user[password]',
},(email, password, done) => {
HCPUser.findOne({ email })
.then((user) => {
if(!user || !user.validatePassword(password)) {
return done(null, false, { errors: { 'email or password' : 'is valid' } });
}
return done(null, user);
}).catch(done);
}));
And then in your passport.authenticate method, specify the strategy name:
passport.authenticate('users', function(err, user, info) { ...
and
passport.authenticate('hcpusers', function(err, user, info) { ...
In this case you'll need two separate endpoints for each login method, or just an extra parameter specifying which one to check from an if statement.
Update
For your comment of not knowing where the passport code should be, this is up to you. However, I like to keep passport code in an 'auth' folder and add the following code to a passport.js file:
const mongose = require('mongoose');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const Users = mongose.model('Users');
passport.use('...', new LocalStrategy({
...
...
}));
Include this in your server/index/app.js (whatever yours is) app.use(passport.initialize());
You can then just use the passport code as normal in your user controllers.
My passport.authenticate code looks like:
return passport.authenticate('local', function(err, passUser, info) {
if (err) {
return next(err);
}
if (!passUser) {
return res.status(503).send('error');
}
const user = passUser;
user.token = user.generateJWT();
return res.json({ token: user.token });
})(req, res, next);
But this can be different for you (i.e. you may not be using sessions?) Either way, if authenticated, just send the response to client so it can proceed.
Hi so to solve this issue i followed what was mentioned. I needed to define the name of the collection within the hcp model using this:
module.exports = mongoose.model('Hcpuser', Hcpuser, 'Hcpuser');
I then created a local strategy ensuring that i was searching using the right model which would then point to the right collection within my DB.
Solution:
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use('hcplocal', new LocalStrategy(
function(uemail, password, done) {
Hcpuser.findOne({ "email" : uemail }, function(err, user) { console.log(user)
if (err) { return done(err); }
if (!user) {
console.log(user);
console.log(err);
console.log(uemail)
return done(null, false, { message: 'Incorrect email.' });
}
if (!user.isValid(password)) {
console.log(user);
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
router.post('/login',function(req,res,next){
passport.authenticate('hcplocal', function(err, user, info) {
if (err) { return res.status(501).json(err); }
if (!user) { return res.status(501).json(info); }
req.logIn(user, function(err) {
if (err) { return res.status(501).json(err); }
console.log(user);
return res.status(200).json({message:'Login Success'});
});
})(req, res, next);
});

convert passport usage from mongoose to sequelize

I am building an app that requires authentication during login and for that I have used passport(passport-local). The app does not run for the login part and the last option available after removing all syntax errors is that the way in which I am using passport(as given in their docs) is for mongoose while I am using sequelize. Can someone please tell how to rectify my passport.js file so that it works fine for sequelize as well?
(using mysql through sequelize; database is already populated)
here is my passport.js file
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const Users = require('./db').Users
module.exports = function (passport) {
console.log("passport is working");
passport.serializeUser(function (users, done) {
console.log("Serialize");
done(null, users.id)
})
function findById(id, fn) {
User.findOne(id).exec(function (err, user) {
if (err) {
return fn(null, null);
} else {
return fn(null, user);
}
});
}
passport.deserializeUser(function (id, done) {
console.log("DeSerialize");
findById(id, function (err, user) {
console.log(user);
done(err, user);
});
})
passport.use(new LocalStrategy(
function (username, password, done) {
Users.findOne({ where: { username: username } },
function (err, users) {
if (err) { return done(err); }
if (!users) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!users.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, users);
});
}
));
}
and here is my db.js file
const Sequelize = require('sequelize');
const db = new Sequelize(
'mydb',
'myuser',
'mypass',
{
dialect: 'mysql',
host: 'localhost',
pool: {
max: 8,
min: 0,
aquire: 30000,
idle: 10000
},
}
);
const Users = db.define('users', {
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
password: {
type: Sequelize.STRING,
allowNull: false,
}
})
db.sync().then(() => console.log("Database is ready"))
exports = module.exports = {
db,
Users
}
also,as a side note,can I do a res.send after post request using passport.authenticate?
app.post('/login', passport.authenticate('local'),
function(req, res) {
res.send({
url:'/profile',
username:req.body.username
});
});
thanks in advance!!
findById has been replaced by findByPk method in Sequelize 4.
http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-findByPk
Yes, you need to change the functions where you fetch your user. This:
function findById(id, fn)
Should be something like:
function findById(id, fn) {
User.findById(id).then((user) {
return fn(null, user);
});
}
And you also need to change this a bit:
passport.use(new LocalStrategy(
function (username, password, done) {
Users.findOne({ where: { username: username } })
.then((user) => {
if (!user) {
return done(null, false, { message: '' });
}
// validate your password**
return done(null, user);
});
}
));
If you want to use a function like user.validatePassword() you will need to define an instance method (check sequelize docs) or here
.

Node JS Authentications with passport-jwt unauthorized

Im trying to setup my Node JS API.
I have a User model :
// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
var bcrypt = require('bcrypt');
// Schema
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true},
},
{
timestamps: true
});
// Saves the user's password hashed
userSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
// Use bcrypt to compare passwords
userSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = restful.model('Users', userSchema);
I want to use passport with jwt for authentication :
// Dependencies
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var config = require('../config/database');
// Load models
var User = require('../models/user');
// Logique d'authentification JWT
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
opts.secretOrKey = config.secret;
opts.audience = 'localhost';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
Company.findById(jwt_payload._id, function(err, company) {
if (err) {
return done(err, false);
}
if (company) {
done(null, company);
} else {
done(null, false)
}
});
}));
};
And my route for authentication :
// User
router.post('/users/login', (req, res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else {
// Check if passwords matches
user.comparePassword(req.body.password, (err, isMatch) => {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({success: true, token: 'JWT ' + token, user: {
id: user._id,
username: user.username,
email: user.email
}});
} else {
res.json({success: false, message: 'Authentication failed. Passwords did not match.'});
}
});
}
});
});
Everything work great on postman.
The token is correctly generated and signed with user's informations.
But i have a problem with the authentication on a protected route :
router.get('/users/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
Everytime, it gives me an "Unauthorized 401" Error.
I really dont know where is the problem, i think the problem is around jwtFromRequest, i also tried with Bearer but it also doesn't work...
I think a good option to avoid this kind of problems is to start from a base project that uses this authentication strategy, and after you have that working, modify it with your functionality.
Here you have an example with jwt authentication strategy and Refresh token implementation: https://solidgeargroup.com/refresh-token-autenticacion-jwt-implementacion-nodejs?lang=es

Resources