I am trying for days to setup Passport on SPA page. On Client Side I am using AngularJS $http and generally it's working.
angular.module('Factory', []).factory('MainFactory', ['$http',function($http) {
return {
RegisterNewAccount : function(AccountInformation) {
return $http.post('/api/PageSignup', AccountInformation);
}
}
}]);
angular.module('Controllers', [])
.controller('MainPage', ['$scope','MainFactory', function($scope, MainFactory) {
$scope.Signup = function(){
var account = {
email: $scope.RegisterEmail,
password: $scope.RegisterPassword
};
MainFactory.RegisterNewAccount(account)
.success(function(data) {
console.log(data);
});
};
}]);
My problem is on server side code, i am new to passport and still cant figure it out how to make it work without redirect thing. My passport setup is below:
var passport = require('passport');
var LocalStrategy= require('passport-local').Strategy;
passport.use('local-signup', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
if (email) email = email.toLowerCase();
process.nextTick(function() {
if (!req.user) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (user) return done(null, false, { message: 'That email is already taken.' });
else {
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.save(function(err) {
if (err) return done(err);
return done(null, newUser);
});
}
});
} else if ( !req.user.local.email ) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (user) {
return done(null, false, { message: 'That email is already taken.'});
} else {
var user = req.user;
user.local.email = email;
user.local.password = user.generateHash(password);
user.save(function (err) {
if (err)
return done(err);
return done(null,user);
});
}
});
} else {
return done(null, req.user);
}
});
}));
var app = express();
app.post('/api/PageSignup', function(req, res, next) {
passport.authenticate('local-signup', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
return res.send(info.message);
}
req.logIn(user, function(err) {
return res.send(user);
});
})(req, res, next);
});
If there are clear example on SPA Authentication with passport please show me.
Overwise can you show me where I am mistaking in my server side code?
Related
I implemented a signup route below. It gets to "User saved..." but the request returns 404.
It doesn't seem to be executing the login strategy:
router.post("/signup", function(req, res, next) {
var email = req.body.email;
var password = req.body.password;
User.findOne({ email: email }, function(err, user) {
if (err) { return next(err); }
if (user) {
return res.status(409).send({message: "Duplicate user - already registered."});
}
var newUser = new User({
email: email,
password: password
});
newUser.save(next);
console.log("User saved...");
});
},
passport.authenticate("login"),
function(req, res) {
return res.status(200).send({
message: "Signup successful",
user: req.user
});
}
);
My Passport login strategy looks like this:
passport.use("login", new LocalStrategy(async (email, password, done) => {
console.log("login...");
User.findOne({ email: email }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: "No user has that email!" });
}
user.checkPassword(password, function(err, isMatch) {
console.log("Checked password...");
console.log("Error? Match?");
console.log(err);
console.log(isMatch);
if (err) { return done(err); }
if (isMatch) {
console.log("Returning done...");
return done(null, user, { message: 'Logged in Successfully' });
} else {
return done(null, false, { message: "Invalid password." });
}
});
});
}));
Here's what I see in the logs:
User saved...
POST /signup 400 181.122 ms - -
Passport is likely throwing the 400 error because the username/password fields are not set.
Passport expects username and password and what you are passing are the email and password. So you can modify the code and let passport's LocalStrategy use the email as the username.
You can set the username and password as follows:
passport.use("login", new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
},async (usernameField, passwordField, done) => {
console.log("login...");
User.findOne({ email: usernameField }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: "No user has that email!" });
}
user.checkPassword(passwordField, function (err, isMatch) {
console.log("Checked password...");
console.log("Error? Match?");
console.log(err);
console.log(isMatch);
if (err) { return done(err); }
if (isMatch) {
console.log("Returning done...");
return done(null, user, { message: 'Logged in Successfully' });
} else {
return done(null, false, { message: "Invalid password." });
}
});
});
}));
You can check the line which was throwing the error from passport's source code here
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.
I am building app using MEAN STACK. I want to use passport-local authentication for my login form. But a the time of form submission i am getting POST http://localhost/login 404 (Not Found) please have a look of my code below This is my controller:
lyfee.controller('loginCtrl', ['$scope', '$http', function($scope, $http) {
$scope.user = {};
$scope.login = function() {
// var data = {User: $scope.user }
//console.log($scope.user);
console.log("login function call");
$http.post('/login', $scope.user);
console.log("login request send");
}
}]);
and this is my server.js :
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByusername(username, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
User.comparePassword(password, user.password, function(err, isMatch) {
if () throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));
app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}),
function(req, res) {
res.redirect('/');
});
in which file should i write getUserByusername and comparePassword function ? and what is mistake i am doing please correct it and give me some suggestion.
In your model suppose that User.js write functions like this:
/**
* Check the user's password
*/
dbSchema.methods.comparePassword = function(candidatePassword, cb) {
var status = this.password.localeCompare(candidatePassword.trim());
if (status != 0) {
return cb(err);
}
cb(null, true);
};
then use the function like this
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
/**
* Check the user's password
*/
User.comparePassword(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));
I'm new to node express and passport so I'm not sure what I'm missing. I'm trying to use a custom callback as all I want is a json response from my registration strategy. Currently I keep getting the following error "Missing credentials". I've been at it for a while now and I'm stuck.
Here is my controller:
app.post('/services/authentication/registration', function(req, res, next) {
console.log('before authentication')
passport.authenticate('local-registration', function(err, user, info) {
console.log('authentication callback');
if (err) { return res.send({'status':'err','message':err.message});}
if (!user) { return res.send({'status':'err','message':info.message});}
if (user!=false) { return res.send({'message':'registration successful'});}
})(req, res, next);
},
function(err, req, res, next) {
return res.send({'status':'err','message':err.message});
});
And my passport strategy:
passport.use('local-registration', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log('credentials passed to passport' + email + '' + password)
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, {message: 'User already exists'});
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
I dont think you should send "req" as a parameter to the authentication function. take a look at this example from the passport docs:
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
I have two model in my node/express app :
User model (simple user authenticated)
Manager model (user with different rights and different fields)
I want to use passport to authenticated them :
User with couple : Username / Password
Manager with couple : Email / Password
I correctly implemented PassportJS for the User model but now I try to do the same for Manager model.
exports.postlogin = function(req, res, next) {
passport.authenticate('user', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
};
exports.postlogin = function(req, res, next) {
passport.authenticate('manager', function(err, manager, info) {
if (err) { return next(err) }
if (!manager) {
req.session.messages = [info.message];
return res.redirect('/manager_signup')
}
req.logIn(manager, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
};
The two strategies :
passport.use('user', new LocalStrategy(function(username, password, done) {
UserModel.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
passport.use('manager', new LocalStrategy({usernameField: 'manager_signin_email', passwordField: 'manager_signin_password'},function(manager_signin_email, manager_signin_password, done) {
ManagerModel.findOne({ email: manager_signin_email }, function(err, manager) {
if (err) { return done(err); }
if (!manager) { return done(null, false, { message: 'Unknown manager ' + manager_signin_email }); }
manager.comparePassword(manager_signin_password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
console.log('Manager login OK : ' + manager_signin_email);
return done(null, manager);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
The problem is for Serialize/Deserialize.
For User I have this :
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
UserModel.findById(id, function (err, user) {
done(err, user);
});
});
But I don't know how to do for Manager model.
You could maybe do something like this when serializing:
if (isUser(user)) {
// serialize user
done(null, "user_"+user.id );
} else if (isManager(user)) {
// serialize manager
done(null, "manager_"+user.id );
}
And then check the prefix when deserializing.
I think there is an open issue for what you want (https://github.com/jaredhanson/passport/issues/148) .
Alternatively you could change you serialize method to include information if it's user or manager not only user id and when deserializing read that info and load user/manager from proper model.
In case anyone is still stumbling upon this. You can check the type of the object you are serializing. Note all there of by objects are generalized in to a PrincipleInfo object.
function PrincipleInfo(principleId, principleType, details) {
this.principleId = principleId;
this.principleType = principleType;
this.details = details;
}
passport.serializeUser(function (userObjectThing, done) {
//userObjectThing could be a User or a Sponsor
var principleType = "user";
var userPrototype = Object.getPrototypeOf(userObjectThing);
if (userPrototype === User.prototype) {
principleType = "user";
} else if (userPrototype === Sponsor.prototype) {
principleType = "sponsor";
} else if (userPrototype === Admin.prototype) {
principleType = "admin";
}
var principleInfo = new PrincipleInfo(userObjectThing.id, principleType, '');
done(null,principleInfo);
});
passport.deserializeUser(function (principleInfo, done) {
if (principleInfo.principleType == 'user') {
User.findOne({
_id: principleInfo.principleId
}, '-salt -hashedPassword', function (err, user) { // don't ever give out the password or salt
done(err, user);
});
} else if (principleInfo.principleType == 'sponsor') {
Sponsor.findOne({
_id: principleInfo.principleId
}, '-salt -hashedPassword', function (err, user) { // don't ever give out the password or salt
done(err, user);
});
} else if (principleInfo.principleType == 'admin') {
Admin.findOne({
_id: principleInfo.principleId
}, '-salt -hashedPassword', function (err, user) { // don't ever give out the password or salt
done(err, user);
});
}
});