I am using passport.js and I'd like to flash a message if the fields of my form are empty. But I don't know how to do it since passport doesn't trigger the strategy callback if those are missing. I really want this use case to be more clear, and I don't want to modify passport. I feel like there is a way to do so but I don't know where! I've tried to use the callback of the route (app.post
) but it doesn't seem to work the way I tried.
Here is the authenticate function prototype:
Strategy.prototype.authenticate = function(req, options) {
options = options || {};
var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);
// here is my problem
if (!username || !password) {
return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
}
var self = this;
function verified(err, user, info) {
if (err) { return self.error(err); }
if (!user) { return self.fail(info); }
self.success(user, info);
}
try {
if (self._passReqToCallback) {
this._verify(req, username, password, verified);
} else {
this._verify(username, password, verified);
}
} catch (ex) {
return self.error(ex);
}
};
Here is my strategy:
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
// ...
console.log("Hello");
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'Pas d\'utilisateur avec ce login.')); // 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))
return done(null, false, req.flash('loginMessage', 'Oops! Mauvais password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
And finally my route:
app.get('/login', function(req, res) {
// render the page and pass in any flash data if it exists
res.render('login', { title: "Connexion", message: req.flash('loginMessage') });
});
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}, function(err, user, info) {
// Was trying this callback, does'nt work, post callback maybe ?
console.log("Hello");
}));
Note: Using flash messages requires a req. Use of connect-flash middleware is recommended to provide this functionality when using Express 3. x. So you need to install connect-flash express middleware as it is recommended. var flash = require('connect-flash'); var app = express(); app.
Passport is a popular, modular authentication middleware for Node. js applications. With it, authentication can be easily integrated into any Node- and Express-based app. The Passport library provides more than 500 authentication mechanisms, including OAuth, JWT, and simple username and password based authentication.
In this route, passport. authenticate() is middleware which will authenticate the request. By default, when authentication succeeds, the req. user property is set to the authenticated user, a login session is established, and the next function in the stack is called.
You should not call req.flash
in your verify callback. Instead you should return a message as shown in the documentation. Passport will put the message returned to flash message when failureFlash: true
:
Setting the
failureFlash
option totrue
instructs Passport to flash an error message using the message given by the strategy's verify callback, if any.
Your revised verify callback:
passport.use('local-login', new LocalStrategy({...},
function(email, password, done) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, {message: 'Pas d\'utilisateur avec ce login.'});
if (!user.validPassword(password))
return done(null, false, {message: 'Oops! Mauvais password.'});
return done(null, user);
});
}));
And routes:
app.get('/login', function(req, res) {
console.log(req.flash('error'));
res.send();
});
app.post('/login', passport.authenticate('local-login', {
successRedirect : '/profile',
failureRedirect : '/login',
failureFlash : true
}));
Edit:
Here's a fully working example: https://gist.github.com/vesse/9e23ff1810089bed4426
Edit:
This does not indeed answer the original question which was I am using passport.js and I'd like to flash a message if the fields of my form are empty. passport-local
strategy does just execute fail
if the form fields are empty, so they should be checked before the authentication middleware and set the flash message outside passport.
It's an old question, but I had trouble finding an answer. Hopefully this helps others.
I think the documentation is a little incomplete when it comes to using connect-flash
. They say:
Note: Using flash messages requires a req.flash() function. Express 2.x provided this functionality, however it was removed from Express 3.x. Use of connect-flash middleware is recommended to provide this functionality when using Express 3.x.
Yet, there's no mention of using req.flash in the done()
callback. Based on the scotch.io tutorial, you actually should call req.flash()
right there in the callback. It works for me.
// In your strategy
...
if (user) {
return done( null, false, req.flash('loginMessage','Pas d\'utilisateur avec ce login.') );
...
You will need to use passReqToCallback
of course. Also be sure failureFlash
is set to true
. OP is already doing these correctly.
Now you can check the flash message in the route. Note that connect-flash
sends an array of messages. That could be OP's problem, if his template is expecting a string.
// In your routes
app.get('/login', function(req, res) {
// Test flash messages in the console
console.log( req.flash('loginMessage') ); // This returns an array
console.log( req.flash('loginMessage')[0] ); // This returns a string
// render the page and pass in any flash data if it exists
res.render('login', {
title: "Connexion",
message: req.flash('loginMessage')[0] // Don't forget the index!
});
});
If there's a chance of having multiple login messages on a page, pass the whole req.flash('loginMessage')
array and iterate through it in your template. Below is an example using nunjucks.
Protip:
If you have many routes with flash messages, you can always set them to res.locals
in a middleware route. This will not interfere with other locals, like title
. Here is my implementation, using bootstrap alerts.
In my strategy:
...
if (!user){
return done( null, false, req.flash('danger','No account exists for that email.') );
}
...
In my routes.js:
// Set flash messages
router.get('*', function(req,res,next){
res.locals.successes = req.flash('success');
res.locals.dangers = req.flash('danger');
res.locals.warnings = req.flash('warning');
next();
});
// Login route
app.get('/login', function(req, res) {
res.render('login', { title: 'Login'});
});
In my nunjucks base template:
<!--Messages-->
{% for danger in dangers %}
<div class='header alert alert-danger alert-dismissible'>
<strong><i class="fa fa-exclamation-circle"></i> ERROR:</strong> {{ danger | safe }}
<a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a>
</div>
{% endfor %}
{% for warning in warnings %}
<div class='header alert alert-warning alert-dismissible'>
<strong><i class="fa fa-check-circle"></i> Warning:</strong> {{ warning | safe }}
<a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a>
</div>
{% endfor %}
{% for success in successes %}
<div class='header alert alert-success alert-dismissible'>
<strong><i class="fa fa-check-circle"></i> Success!</strong> {{ success | safe }}
<a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a>
</div>
{% endfor %}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With