Passport-imap callback? - passport.js

I'm new to node.js, and am trying to implement imap authentication for a simple test app using passport-imap.
I configured my ImapStrategy and login functions as stated in https://github.com/nettantra/passport-imap, but when I login to my app I get the message "Success fallback is required to generate the user!". Here's my code:
passport.use(new ImapStrategy({host: 'my-imap-server', port : 993, tls : true}, function(req,res){
console.log('AUTH HERE ' );
return done(null, user);
}));
app.post('/login', passport.authenticate('imap', {
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}),function(req, res) {
console.log('OK AUTHENTICATED');
res.redirect('/app');
});
Adding a "successRedirect" param did not help... can anybody point me to the right direction?
I added a verification callback, but cannot understand what params does it need.
I tried doing something similar to localstrategy, but my "username" param is not what I expected (the user login), it's some other object. Also, I'm not sure this is the way to go: I actually don't want to keep a local userlist, I just want to accept any user that can authenticate to my imap server.
Also I don't understand whether I should use "nextTick" like localstrategy does: if I do it appears my verification is bypassed, and I'm always returned to the login page.
passport.use(new ImapStrategy({host: 'my-imap-server', port : 993, tls : true,
success_callback: function(username, password, done){
// process.nextTick(function () {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
//});
} }
));

Related

passport.js hangs on some posts

I've been searching stackoverflow for reasons why my node.js (express) app hangs on some posts with passport.js.
I've been looking at theese two questions:
More Passport.js woes - hangs on form submission
Passport (local) is hanging on Authentication
My code for creating a user works fine, and looks like this:
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching
// asynchronous
process.nextTick(function() {
// if the user is not already logged in:
if (!req.user) {
User.findOne({
where: {
local_email: email
}
}).then(function(user) {
if (user){
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
}else{
// create the user
User
.build({
local_email: email,
local_password: generateHash(password),
id: 1 //Normal activated user
})
.save()
.then(newUser => {
//Our newly crated user
return done(null, newUser);
})
.catch(error =>{
//Woops something went wrong
return done(error);
})
//var newUser = new User();
//newUser.localemail = email;
//newUser.localpassword = generateHash(password);
//User.create(newUser).then(function(newUser, created) {
// if (!newUser) {
// return done(err);
// }
// if (newUser) {
// return done(null, newUser);
// }
//});
}
});
// if the user is logged in but has no local account...
} else if ( !req.user.local.email ) {
// ...presumably they're trying to connect a local account
// BUT let's check if the email used to connect a local account is being used by another user
User.findOne({
where: {
localemail: email
}
}).then(function(user) {
if (err)
return done(err);
if (user){
return done(null, false, req.flash('loginMessage', 'That email is already taken.'));
// Using 'loginMessage instead of signupMessage because it's used by /connect/local'
} else {
// create the user
var newUser = new User();
newUser.local.email = email;
newUser.local.password = generateHash(password);
User.create(newUser).then(function(newUser, created) {
if (!newUser) {
return done(err);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
} else {
// user is logged in and already has a local account. Ignore signup. (You should log out before trying to create a new account, user!)
return done(null, req.user);
}
});
}));
In my routs file the post to that function looks like this:
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
My code for signing in however dosen't work that well. The function in passport.js looks like this:
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
if (email)
email = email.toLowerCase(); // Use lower-case e-mails to avoid case-sensitive e-mail matching
// asynchronous
process.nextTick(function() {
User.findOne({
where: {
local_email: email
}
}).then(function(user){
console.log("TEST: "+user);
// if no user is found, return the message
if (!user){
console.log("no user with the following email: " +email);
return done(null, false, req.flash('loginMessage', 'No user found.'));
}else if(!validPassword(password,user.local_password)){
console.log("wrong password");
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
// all is well, return user
}else{
return done(null, user);
return;
}
}).catch(function(err){
console.log("woops der skete en fejl: "+err);
console.log("email is: "+email);
console.log("password is: "+password);
return done(err);
});
});
}));
The corresponding route looks like this:
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
The above code hangs when the form is being submitted. If I changes the code to:
app.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) { return next(err); }
if (!user) {
return res.render('/login', {
pageTitle: 'Sign in',
form: req.body
});
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
I receive an 500.
Any ideas why this isen't working?
Updated with console output:
Executing (default): SELECT `id`, `role`, `local_email`, `local_password`, `facebook_id`, `facebook_token`, `facebook_email`, `facebook_name`, `twitter_id`, `twitter_token`, `twitter_displayname`, `twitter_username`, `google_id`, `google_token`, `google_email`, `google_name`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`local_email` = 'test#local' LIMIT 1;
TEST: [object SequelizeInstance:users]
POST /login 302 87.021 ms - 46
Executing (default): SELECT `id`, `role`, `local_email`, `local_password`, `facebook_id`, `facebook_token`, `facebook_email`, `facebook_name`, `twitter_id`, `twitter_token`, `twitter_displayname`, `twitter_username`, `google_id`, `google_token`, `google_email`, `google_name`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`id` = 1;
Updated with changed passport.deserializeUser
passport.deserializeUser(function(id, done) {
/*User.findById(id, function(err, user) {
done(err, user);
});*/
User.findById(id).then(user => {
if (!user){
done(null);
}else{
done(user);
}
})
});
Your passport.deserializeUser isn't correct. As with all Node.js callbacks, the first argument is reserved for errors, and should be null (or another falsy value) if there aren't any errors. In your case, you're passing user as first argument, which makes Passport think there was an error.
Try this:
passport.deserializeUser(function(id, done) {
User.findById(id)
.then(user => done(null, user))
.catch(done);
});

Understanding done function in PassportJS

I am trying to implement a normal login and signup module and I am facing difficulty in understanding the done function.here is the line I feel is responsible for the done function. Now what I wanted to do was return the corresponding messages like if there was some server error.
app.post('/user/auth/login', passport.authenticate('local-login'), function(req, res) {
console.log("Login Request Successful");
if(req.user){
res.status(200).send(JSON.stringify({
'msg' : "Successfully logged in"
}));
}
else{
res.status(400)
}
//console.log(req.user);
});
This is for the case when after logging in Passport attaches a user object to the request module.
But how do I differentiate between a server error and authentication failed error
Here is my authenticate middleware.
passport.use('local-login', new LocalStrategy({
passReqToCallback: true
}, function(req, username, password, done) {
Users.findOne({
'emailId': username
}, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false);
else if (passwordHash.verify(password, user.password)) {
console.log("User is verified");
req.session.save();
return done(null, user);
} else
return done(null, false);
});
}));
Basically, I need to access the messages in done() function. How do I do that?
How does function, like if I type the wrong password, the message I get in my browser in Unauthorized. It means somewhere it is setting the response.data field to Unauthorized. Instead of that I want to know when there is an error and want to send my custom message.
I don't know what do you mean by
access the messages in done() function
, however You can very well do with supplying addition object with message in done callback
if (!user) {
return done(null, false, {
message: 'Unknown user or invalid password'
});
}
if (!user.authenticate(password)) {
return done(null, false, {
message: 'Unknown user or invalid password'
});
}
Another method.
I don't know how I missed it. There is an option of custom callbacks in the docs. Here is the implementation of the same.
app.post('/user/auth/login', function(req, res) {
passport.authenticate('local-login', function(err, user, info) {
if (err) {
res.status(500).send(JSON.stringify({
'msg': "Internal Server Error"
}));
}
if (!user) {
res.status(401).send(JSON.stringify({
'msg': "Username or Password is incorrect"
}));
}
if (user) {
res.status(200).send(JSON.stringify({
'msg': "Successfully logged in"
}));
}
})(req, res, next);
});
The only issue with this solution is that you will have to manually login and create session and all that.
An optional callback can be supplied to allow the application to overrride the default manner in which authentication attempts are
handled. The callback has the following signature, where user
will be set to the authenticated user on a successful
authentication attempt, or false otherwise. An optional info
argument will be passed, containing additional details provided by
the strategy's verify callback.
app.get('/protected', function(req, res, next) {
passport.authenticate ('local',function(err, user, info) {
if (err) { return next(err) }
if (!user) { return res.redirect('/signin') }
res. redirect('/account');
})(req, res, next);
});
Note that if a callback is supplied, it becomes the application's responsibility to log-in the user, establish a session, and otherwise
perform the desired operations.

Node.js passport pass postdata on to failureRedirect

This is driving me crazy!
I'm using Express 4, passport and local-passport to authenticate login and signup.
I'm using this example:
https://github.com/tutsplus/passport-mongo
Problem:
When the signup form does not validate (say you forgot one of the fields) and we redirect to the failureRedirect (which is the same signup page), all the entered values are gone. Not a very good user experience that you have to fill out the entire form because you messed up a single field.
How do I pass the already entered data on the the form?
I got these two routes handing the GET and POST of the form:
app.get('/signup', function(req, res){
res.render('signup', {
message: req.flash('message'),
});
});
app.post('/signup', passport.authenticate('signup', {
successRedirect: '/signup-complete',
failureRedirect: '/signup', // need to pass the entered values (if any) back to the signup page
failureFlash : true
}));
I have a nagging suspicion that the values are already there - I just don't know how to grab them.
I'm using Swig for the views btw.
Did you add connect-flash middleware to your app?
var flash = require('connect-flash');
app.use(flash());
Update:
When you define your local strategy, you should return the flash messages in the third parameter of the done function. Example:
passport.use(new LocalStrategy(
function(username, password, done) {
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));

Nodejs and PassportJs: Redirect middleware after passport.authenticate not being called if authentication fails

I have no login page but rather I have a login form that appears on every page. I want to redirect user back to the same page they were on regardless of whether authentication was successful (with appropriate flash messages)
Take the following code:
app.post('/login', validateLogin, passport.authenticate('local-login'), function(req, res) {
var redirectUrl = '/';
if(req.body.to.length > 0){
redirectUrl = req.body.to;
}
console.log("THIS IS ONLY CALLED IF passport.authenticate() IS SUCCESSFUL");
res.redirect(redirectUrl);
});
I only see the final middleware above being called if authentication is passed. If it fails then passport appears to be redirecting me to /login in the form of a get request. In my app this page doesn't exist.
If I pass an additional options object as a parameter in the passport authenticate function then this works:
app.post('/login', validateLogin, passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page. THIS IS JUST FOR TESTING TO SEE IF THE REDIRECT ON FAIL WORKS.
failureFlash : true, // allow flash messages
}
));
But in doing this I lose the ability to choose where to redirect the user to. It seems that passport takes control over where the user is redirected to if authentication fails. How can I fix this? Or is it a bug? Must passport authenticate be the last middleware in the chain if authentication fails?
This is my local strategy function call:
//LOCAL LOGIN
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
console.log("IN PASSPORT");
if(email.length == 0 || password.length == 0){
console.log("FIELDS ARE EMPTY");
return done(null, false, req.flash('loginMessage', 'Fill in all values.'));
}
// 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 before anything else
if (err){
return done(err);
console.log("db err");
}
// if no user is found, return the message
if (!user){
console.log("not user");
return done(null, false, req.flash('loginMessage', 'Incorrect details.')); // req.flash is the way to set flashdata using connect-flash
}
// if the user is found but the password is wrong
if (!user.validPassword(password)){
console.log("invalid pw");
return done(null, false, req.flash('loginMessage', 'Incorrect details.')); // create the loginMessage and save it to session as flashdata
}
// all is well, return successful user
console.log("All OK");
return done(null, user);
});
}));
You could use a custom authentication callback as described in the last paragraph there http://passportjs.org/guide/authenticate/.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
// Redirect if it fails
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
// Redirect if it succeeds
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
I was running into the same issue where the redirect-calls , that follow successful Facebook Auth
passport.authenticate('facebook', ..)
.. were not being honored.
Based on 'local' passportJS Strategy - and a nice reminder of that from #ploutch's answer here .. I realized the key to getting it to work seems to be in this call:
req.logIn(user, function(err) {
...
}
For Facebook, this route setup worked for me:
app.get(
'/auth/facebook/callback',
passport.authenticate
(
'facebook',
{ failureRedirect: '/fbFailed' }
),
function(req, res)
{
var user = myGetUserFunc(); // Get user object from DB or etc
req.logIn(user, function(err) {
if (err) {
req.flash('error', 'SOMETHING BAD HAPPEND');
return res.redirect('/login');
}
req.session.user = user;
// Redirect if it succeeds
req.flash('success', 'Fb Auth successful');
return res.redirect('/user/home');
});
}
);
Full answer, including:
Middleware to set redirectUrl
Flash messages
Not returning values that won't be used
Just create a redirectTo value in your loginRequired middleware:
var loginRequired = function(req, res, next) {
if ( req.isAuthenticated() ) {
next();
return
}
// Redirect here if logged in successfully
req.session.redirectTo = req.path;
res.redirect('/login')
}
And then in your login POST:
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if ( err ) {
next(err);
return
}
// User does not exist
if ( ! user ) {
req.flash('error', 'Invalid email or password');
res.redirect('/login');
return
}
req.logIn(user, function(err) {
// Invalid password
if ( err ) {
req.flash('error', 'Invalid email or password');
next(err);
return
}
res.redirect(req.session.redirectTo || '/orders');
return
});
})(req, res, next);
});
I know this might still be a problem for some people like me who tried all the suggested options without any success.
In my case, as it turned out, I was getting the error because my req.body object was always empty. I had my body parsing middleware set up correctly so it didn't make sense why this was happening.
After more research I found out that the enctype I was using for my forms(multipart/form-data) isn't supported by body-parser - see their read me - after switching to a different middleware, multer, everything worked smoothly.

passport-local with node-jwt-simple

How can I combine passport-local to return a JWT token on successful authentication?
I want to use node-jwt-simple and looking at passport.js I am not sure how to go about.
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
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);
});
}
));
Is it possible to return the token when calling done() ?
Something like this... (just pseudo code)
if(User.validCredentials(username, password)) {
var token = jwt.encode({username: username}, tokenSecret);
done(null, {token : token}); //is this possible?
}
If not, how can I return the token?
I figured it out!
First of all you need to implement the correct strategy. In my case LocalStrategy, and you need to provide your validation logic. For example sake let's use the one in passport-local.
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
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);
});
}
));
the verify call back you provide function(username, password, done) will take care of finding your user and checking if the password matches (beyond the scope of the question and my answer)
passport.js expects several pieces for it to work, one is that you return the user in the strategy. I was trying to change that part of the code, and that was wrong. The callback expects false if the validation fails and an object (the validated user) if you are successful.
Now.... how to integrate JWT?
In your login route you will have to handle a successful auth or an unsuccessful one. And it is here that you need to add the JWT token creation. Like so:
(remember to disable the session, otherwise you will have to implement the serialize and deserialize functions. And you don't need those if you are not persisting the session, which you are not if you are using a token based auth)
From passport-local examples: (with the JWT token added)
// POST /login
// This is an alternative implementation that uses a custom callback to
// achieve the same functionality.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
return res.json(401, { error: 'message' });
}
//user has authenticated correctly thus we create a JWT token
var token = jwt.encode({ username: 'somedata'}, tokenSecret);
res.json({ token : token });
})(req, res, next);
});
And that is it! Now when you call /login and POST username and password (which should always be over SSL) the first code snippet above will try to find a user based on the username you provided and then check that the password matches (Of course you will need to change that to suit your needs).
After that your login route will be called and there you can take care of returning an error or a valid token.
Hope this will help someone. And if I have made any mistakes or forgot something let me know.
This is a great solution, I just want to add this:
var expressJwt = require('express-jwt');
app.use('/api', expressJwt({secret: secret}));
I like to use "express-jwt" to validate the token.
btw: this article is great to learn how to handle the token in the client side, using Angular, in order to send it back with every request
https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Here's a boiler-plate I'm working on for specifically using api tokens only (no sessions...not that session are bad of course; just we're using token approach):
https://github.com/roblevintennis/passport-api-tokens

Resources