I am trying to use two different basic authentication conditions for two different apis. But the moment I add the second authentication condition in my project, Passport.Js doesn't let me authenticate at all. It keeps saying wrong password. Below is the code: Can anybody suggest what's wrong with it? I am sorry if this is a noob question. I have just begun working with node so the entire framework is new to me.
// Load required packages
var passport = require('passport');
var passport2= require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var BasicStrategy2 = require('passport-http').BasicStrategy;
var User = require('../models/useridentity');
var User2 = require('../models/useridentity');
passport.use(new BasicStrategy(
function(username, password, callback) {
User.findOne({ username: username }, function (err, user) {
if (err) { return callback(err); }
// No user found with that username
if (!user) { return callback(null, false); }
// Make sure the password is correct
user.verifyPassword(password, function(err, isMatch) {
if (err) { return callback(err); }
// Password did not match
if (!isMatch) { return callback(null, false); }
// Success
return callback(null, user);
});
});
}
));
passport2.use(new BasicStrategy2(
function(user, pass, callback) {
User2.findOne({ useremail: user }, function (err, user) {
if (err) { return callback(err); }
// No user found with that username
if (!user) { return callback(null, false); }
// Make sure the password is correct
user.verifyPassword(pass, function(err, isMatch) {
if (err) { return callback(err); }
// Password did not match
if (!isMatch) { return callback(null, false); }
// Success
return callback(null, user);
});
});
}
));
exports.isAuthenticated = passport.authenticate('basic', { session : false });
exports.isAuthenticated2 = passport2.authenticate('basic',{session:false});
Requiring same modules multiple times makes no sense - due to module caching you are likely receiving the same object. You can name the strategies in use which enables using same strategy with different configuration:
var passport = require('passport'),
BasicStrategy = require('passport-http').BasicStrategy;
passport.use('auth1', new BasicStrategy(...));
passport.use('auth2', new BasicStrategy(...));
app.post('/api1', passport.authenticate('auth1'), function(req, res) { ... });
app.post('/api2', passport.authenticate('auth2'), function(req, res) { ... });
Related
Hi All,
I am authenticating my user using bcrypt module.
I am able to do perform the Registration process, but facing problem during Login process.
User Model:
var userSchema = new Schema({
email: {type: String, required: true},
password: {type: String,
});
Hashing methods:
userSchema.methods.encryptPassword = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(5), null)
};
userSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
Sign in:
module.exports.login = function (user, callback) {
User.findOne({'email': user.email, 'password': user.validPassword(this.password)}, callback);
};
Login Route
router.post('/login', function (req, res) {
var user = req.body;
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});
While executing am getting this error: TypeError:user.validPassword is not a function
Please Help.
Your mistake is that the user being provided to your login method is not a Mongoose DB object. Instead, your login function should look something like this:
module.exports.login = function (request, callback) {
User.findOne({'email': request.email }, function(err, user) {
if (err) return callback(err);
if(!user || !user.validPassword(request.password)) return callback();
return callback(null, user);
});
};
This will ensure that user is a valid Mongoose object before you attempt to verify the password.
One other possible solution, if you'd prefer to avoid checking that the password is valid in your data layer, is to simply fetch the user document based on its email and then check the password in the login route.
router.post('/login', function (req, res) {
var user = req.body;
User.findOne(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
if (!user.validPassword(req.body.password)) {
res.sendStatus(401);
return;
}
res.json(user.id);
});
});
In Login Route, you need to instantiate the Schema:
router.post('/login', function (req, res) {
var user = new User(req.body);
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});
I need some help with setting up Passport Consumer Strategy and integrating it into "locals" (Right now, we have the local strategy working just fine). We have tried several approaches but with no luck on it working 100%. The below code is not the complete code, we have taken out some of it so this post doesn't get too long. Any help with this would greatly be appreciate. There could be compensation as well if someone can get us over this hurdle.
So one question is, if the user is authenticated by the consumer key and secret, how does Passport store the session variables so they are used throughout the site?
Second question, how do we handle the user after it passes the authentication process?
Both local and consumer need to be working.
Consumer key and secret using a POST by a Provider <- I can show some of the post if needed.
This needs to be OAuth1 Only, as of right now, OAuth2 isn't an option.
This is for a single sign-on authentication.
I can supply a consumer session output if needed.
Ultimately, we would like the local strategy and the consumer strategy working with the same "locals" global variables. As far as I can tell, we can authenticate the consumer, retrieve the user from our DB, create a session and can tell if user is "ensureAuthenticated".
Here is what we have working right now.
Local strategy is authenticating correctly.
We render the pages with these local variables:
"Omitted most of the code to save time."
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
...
function renderPage(req, res, pageName, pageTitle){
res.render(pageName, {
pageName: pageTitle,
username: req.user ? req.user.username : '',
...
Consumer strategy is authenticating by a POST request from a "Provider"
We have tried adding the Consumer strategy to the authentication.
server.js
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
ADDED -> authentication.initConsumerStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
ADDED -> passport.authenticate('consumer', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
authentication.js (omitted code)
module.exports = function(siteConfig, defaultRedirectPage, server, sessionStore, log) {
var passport = require('passport')
...
, ConsumerStrategy = require('passport-http-oauth').ConsumerStrategy
, TokenStrategy = require('passport-http-oauth').TokenStrategy
, LocalStrategy = require('passport-local').Strategy;
var auth = {};
var authenticationRedirects = { successRedirect: '/', failureRedirect: '/login' };
passport.serializeUser(function(user, done) {done(null, user);});
passport.deserializeUser(function(obj, done) {done(null, obj);});
auth.authenticate = function(email, password, callback) {
email = email.toLowerCase();
userController.findUserByUsernameWithPermissions(email,
function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, null, 'Incorrect username.');
bcrypt.compare(password, user.password_hash, function(err, res) {
if(err){return callback(err);
} else if (!res) {return callback(null, null, 'Incorrect password.');
} else {if (user.account_state>0) {callback(null, user);} else {return callback(null, null, '/reset?rand='+user._id);}}
});
}
);
}
auth.initLocalStrategyRoutes = function(app){
passport.use(new LocalStrategy(auth.authenticate));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) return res.send({success: false, message: info});
req.logIn(user, function(err) {
if (err) { return next(err); }
res.send(req.user);
});
}) (req, res, next);
});
}
auth.initConsumerStrategyRoutes = function(app){
// passport.use(new LocalStrategy(auth.authenticate));
console.log('app: ', app)
passport.use('consumer', new ConsumerStrategy(
function(key, done) { console.log('starting ConsumerStrategy');
dbConsumerKey.findByConsumerKey({consumerKey: key}, function(err, consumerKey) {
if (err) { return done(err); }
if (!consumerKey) {
var errCode = dbError.find({name:'no_resource_link_id'}, function(err, errorCodes) {
console.log('statusText: ', errorCodes[0]["statusText"]);
return errorCodes[0]["statusText"];
});
return done(null, errCode);
} else {
if (!consumerKey[0]["consumerKey"]) { return done(err); }
if (!consumerKey[0]["consumerSecret"]) { return done(err); }
// return done(null, consumerKey[0]["consumerKey"], consumerKey[0]["consumerSecret"]);
return done(null, consumerKey[0], consumerKey[0]["consumerSecret"]);
}
});
},
function(requestToken, done) {
dbRequestTokens.find(requestToken, function(err, token) {
console.log('inside requestToken');
if (err) { return done(err); }
var info = { verifier: token.verifier,
clientID: token.clientID,
userID: token.userID,
approved: token.approved
}
done(null, token.secret, info);
});
},
function(timestamp, nonce, done) {
done(null, true)
}
));
};
auth.initTokenStrategyRoutes = function(app){}
auth.addUser = function(username, email, password, callback){auth.authenticate(username, "pass", callback);}
return auth;
};
The authentication.js strategy does validate the consumer key and secret. but it doesn't create the session variable we are wanting. We would like the consumer strategy code to be in the authentication.js file.
Now here is another approach, we created a separate files called consumerkey.js
This direction works to a point. We can output the passport session either on the screen or on the command line.
var passport = require('passport')
exports.launchLti = [
passport.authenticate('consumer', { session: false/true [tried both] }),
function(req, res) {
db.findByStudentUserId({lis_person_contact_email_primary:
req.body.lis_person_contact_email_primary}, function(err, user) {
req.logIn(user, function(err) {
req.user.username = user[0].lis_person_contact_email_primary;
...
// req.session.save(function(){
// res.redirect('/classes');
res.redirect(200,'/');
// });
});
})
// res.render('launch', {launch: launch});
}
}]
I solved this issue by changing some of my code structure.
app.pst('/launch/lti/:id', function(req, res, next) {
passport.authenticate('consumer', {failureRedirect: '/login'}),
dbConsumerKey.findByStudentUserId({},
function(err, user) {
if (err) console.log(err, user);
req.logIn(user, function(err) {
if (err) return err;
ADDED -> req.session.valid = true;
ADDED -> res.redirect('/');
});
}
});
});
and modifying the render page function to adapt to the incoming information.
function renderPage(req, res, pageName, pageTitle){
...
this is where the locals are created
...
this allowed me to use my current local strategy as is and adding a totally different strategy route but making the session correctly.
So, I have everything working but it is not showing it is an authenticate user even though it arrives at the proper places...
javascript code from the page to validate login
var UserManager = {
validateLogin : function (username, password) {
var userData = {
username: username,
password: password
}
return new Promise(function(resolve, reject) {
$.ajax({
url: "/musicplayer/users/api/login",
dataType: "json",
data: userData,
type: "POST",
success: function loginSuccess(result, status, xhr) {
resolve(null);
},
error: function loginError(xhr, status, result) {
reject(new Error(result));
},
});
});
}
}
function userLogin(){
UserManager.validateLogin($('#loginEmail').val(), $('#loginPassword').val()).then(function(response) {
window.location = '/musicplayer/library'
},
function(error){
$("#msgBox").messageBox({"messages" : error.message, "title" : "Warning", boxtype: 4 });
$("#msgBox").messageBox("show");
});
return false;
}
local.strategy.js
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var userLibrary = require('../../classes/music/userlibrary.js');
module.exports = function () {
passport.use(new localStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
//validating user here
var userManager = new userLibrary.UserManager();
userManager.login(username, password).then(
function (user){
done(null, user);
},
function (reason){
if (reason.err) {
done(err, false, info);
}
else {
done(null, false, {message: reason.message});
}
}
);
})
);
};
Router
/******* validate the user login ********/
usersRouter.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
console.log("Login Failed", err.message + " - " + err.stack);
if (req.xhr){
res.status(500).send({ error: 'Internal Error' });
}
else {
next(err);
}
}
else if (!err && !user){
err = new Error();
err.message = info.message;
err.status = 401;
console.log("Invalid Data", err.message);
if (req.xhr){
res.status(401).send({ error: err.message });
}
else {
next(err);
}
}
else if (user){
console.log("Successful Login:", user);
res.status(200).send({message: "successful"});
}
}
)(req, res, next);
});
passport.js file which has my Middleware...
var passport = require("passport");
module.exports = function (app) {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
require('./strategies/local.strategy')();
app.all('/musicplayer/*', function (req, res, next){
// logged in
//need function for exceptions
if (req.user || req.url === '/musicplayer/users/api/login' || req.url === '/musicplayer/users/signin') {
next();
}
// not logged in
else {
// 401 Not Authorized
var err = new Error("Not Authorized");
err.status = 401;
next(err);
}
});
}
Userlibrary/UserManager
I am using promises to be able to utilize the creation of a library and to deal with sync versus async issues that I ran into early on...
var sqlite3 = require('sqlite3').verbose();
function User() {
this.email = "";
this.password = "";
this.userid = "";
};
function UserManager () {
this.user = new User();
};
UserManager.prototype.login = function (email, password) {
var db = new sqlite3.Database('./data/MusicPlayer.db');
params = {
$email: email,
$password: password
}
var self = this;
return new Promise(function(resolve, reject){
db.serialize(function () {
db.get("SELECT * FROM users WHERE email = $email and password = $password", params, function (err, row) {
db.close();
if (!err && row) {
//log in passed
self.user.userid = row.userid;
self.user.email = row.email;
self.user.password = row.password;
resolve(self.user);
}
else if (!err) {
//log in failed log event
reject({
err: err,
message: null
});
}
else {
//error happened through out an event to log the error
reject({
message : "Email and/or Password combination was not found",
err : null
});
}
});
});
});
};
module.exports = {
User : User,
UserManager : UserManager
}
Now, I have debugged this and it is for sure getting to "successful Login"
Returns to the browser with success, the browser says okay let me redirect you to the library page (which is really just a blank page). When it goes to my library page I get a 401 unauthorized.
So if I debug inside the middleware to ensure authentication. I look at req.user and it is undefined and I try req.isAuthenticated() it returns a false.
I think I must be missing something...
What I want is a global authentication saying hey is this person logged in. And then I will set up the route/route basis say okay do they have permission for this page or web service call.
Right now I am sticking with session for everything as it is not useful to me to learn web tokens at this point and time.
Any help would be appreciated... I been around and around on this looking at examples out there. But the examples I find are the "basic" examples no one calling a library to validate from database or they are not trying to set up the authorization globally but rather on a route by route basis.
Upon searching I found this article
https://github.com/jaredhanson/passport/issues/255
then I found this in documentation
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
and that worked for me... I basically forgot to do the req.logIn method itself when using the custom callback.... I knew it was something simple... Hope this helps someone in the future.
My model looks like this, but when I try use verifyPassword, it says TypeError: user.verifyPassword is not a function
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var User = require('../models/user');
passport.use(new BasicStrategy(
function(username, password, callback) {
User.findOne({ username: username }, function (err, user) {
if (err) { return callback(err); }
// No user found with that username
if (!user) { return callback(null, false); }
// Make sure the password is correct
// Error comind at this point
user.verifyPassword(password, function(err, isMatch) {
if (err) { return callback(err); }
// Password did not match
if (!isMatch) { return callback(null, false); }
// Success scenario
return callback(null, user);
});
});
}
));
Try to use User.verifyPassword function
But for more correct answer show your User model
You should have in your User schema something like:
// I am use bctypt, but you need your comparer function
userSchema.methods.verifyPassword = function(password, callback) {
callback(err, bcrypt.compareSync(password, this.password));
};
I know i am late but you can do something like this,
passport.use(
'local',
new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
})
);
Reference : https://github.com/sikandar114/node-auth-pass-local/blob/master/config/passport.js
Since the data is sent by post method, express is not able to parse data because body-parser middleware has been omitted from express due to some issues. Hence you must add it manually.Import body-parser:
npm install body-parser
Add these lines to your code.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Also define these 2 functions.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
Try using passport.use(new LocalStrategy(User.authenticate()));
I'm using passport local strategy like this:
Config:
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(email, password, done) {
UserPersistenceModel.findOne({ email: email }, function (err, user) {
if (err) return done(err);
if (!user) return done(null, false);
user.comparePassword(password, function(err, isMatch) {
if(err) return done(err);
if(!isMatch) return done(null, false);
return done(null, user);
});
});
}
));
My router definition looks like this:
var TokenRouter = function(app, passport, tokenSecret) {
//Create
app.post('/', function(req, res, next) {
console.log(req.body);
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) {
console.log('Unsuccessful login!');
return res.status(401).json({ error: 'Login failed!' });
}
req.logIn(user, function(err) {
if (err) return next(err);
console.log('Successful login!');
//user has authenticated correctly thus we create a JWT token
var token = jwt.encode(user, tokenSecret);
res.json({ output : token });
});
})(req, res, next);
});
};
For some reasons I don't fully understand the passport "mechanism". The documentation doesn't look very detailled to me. One question is where the request object (this contains the body which contains the email and the password) is passed to my strategy. How does the strategy gets the email and the password (what's the "source object")?
The reason asking this is that I'd like to use the following inputModel instead of the request being passed to the strategy:
var TokenCreateInputModel = function(req) {
this.email = req.body.email;
this.password = req.body.password;
this.validate();
};
TokenCreateInputModel.prototype = Object.create(InputModel);
TokenCreateInputModel.prototype.validate = function() {
if(!this.email) throw new Error('Email is required!');
if(this.email.indexOf('#') == -1) throw new Error('Emailsyntax is wrong!');
if(!this.password) throw new Error('Password is required!');
};
module.exports = TokenCreateInputModel;
This inputmodel is converting the request and validating the data. I want to use this input model because it fits better to my architecture (I'm using such inputModels in all other cases ... just not with passport (because of the lack of my understanding), which seems to be inconsistent to me.