More Passport.js woes - hangs on form submission - node.js

I am setting up what I thought would be the simplest auth possibly - a site loads on a log in screen, user enters credentials in a form, on submission I am using Passport.JS and Sequelize to check the credentials. Most all of this is copied from various tutorials, or the Passport website itself.
No matter what I do, change or attempt though, the site just hangs as soon as I click the form submit button. In the dev tools network tab I just shows the post request to /login as pending.
To eliminate all possible added problems, I stripped out Sequelize, and used hard coded users, ala the local use example on the passport github page. Still, no change, to Sequelize was not the problem.
I'm sure it's something dumb. I've tried the obvious solutions, like making sure my form was sending 'username' and 'password'.
My form looks like this:
form(method="post", action="/login")
fieldset
legend Please Log In
label(for="username") Username:
input#username(type='text', name="username", autocomplete="off")
label(for="password") Password:
input#password(type='password', name="password")
button#login(type="submit") Log In
In node my app.js uses a router, which includes this:
var login = require('../app/controllers/login');
app.get('/', login.index);
app.post('/login', login.auth);
The page load ok on the get request. On post it directs to this:
exports.auth = function (req, res, next) {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/',
failureFlash: true
});
};
The console.log(req.body) comes up fine, showing the form values, so we are ok to that point. But nothing after. This is the last part:
passport.use(new LocalStrategy(
function(username, password, done) {
console.log('local strat invoked');
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);
});
}
));
I swapped out the code that used Sequelize to check the DB for the user with this findByUsername function (copied and pasted straight from the above mentioned post on the passport github page), but as the console.log('local strat invoked') is never coming up, I'm guessing nothing after that point even matters.
Also includes are the passport.serializeUser and passport.deserializeUser, but I cant imagine those are the problem at the stage where I am stuck.
Any idea what step I am missing to get this working?

Passport is middleware, if you embed it in a route handler function, you need to invoke it:
exports.auth = function (req, res, next) {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/',
failureFlash: true
})(req, res, next); // <-- NEED TO INVOKE WITH req, res, next
};
or, more simply use it as middleware (which is the recommended approach:
exports.auth = passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/',
failureFlash: true
});

Related

Prevent User from logging again in passportjs if they are authenticated once?

I am using PassportJS, and signup and login functions are working quite smooth.
The only problem I am facing with PassportJS (I am using sessions too), that even when the user had logged in, they can again go back to the signup/login url and do signup and/or login back!
This is tempting me. If anyone has a fix/suggestion, please put it down.
UPDATE - 1
Glimpse of myroutes.js: (Using PassportJS along with connet-ensure-login.
app.get('*', function(req, res, next) {
if (req.url.indexOf('/users/login') < 0 &&
req.url.indexOf('/users/signup') < 0) {
req.session.returnTo = null;
}
next();
});
// =====================================
// HOME PAGE (with login links) ========
// =====================================
app.get('/', sabSettings, function(req, res) {
Setting.findOne(function(err, setting) {
if (err)
throw err;
// console.log(setting);
res.render('index', { title: 'eduBird | Reach the glory', setting: req.setting }); // load the index file
});
});
// =====================================
// LOGIN ===============================
// =====================================
// show the login form
app.get('/login', sabSettings, function(req, res) {
// render the page and pass in any flash data if it exists
res.render('login', {
message: req.flash('loginMessage'),
errors: req.flash('error'),
title: 'Login | eduBird',
setting: req.setting
});
});
// process the login form
app.post('/login', passport.authenticate('local-login', {
successReturnToOrRedirect: '/loggedin',
failureRedirect: '/login',
failureFlash: true
}));
// =====================================
// SIGNUP ==============================
// =====================================
// show the signup form
app.get('/signup', sabSettings, function(req, res) {
// render the page and pass in any flash data if it exists
process.nextTick(function() {
res.render('signup', {
message: req.flash('signupMessage'),
errors: req.flash('error'),
title: 'Register | eduBird',
setting: req.setting
});
});
});
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successReturnToOrRedirect: '/profile/welcome',
failureRedirect: '/signup',
failureFlash: true
}));
You have not created any sort of access control, but don't worry we will first go through how Passport works and use this to address the problem.
When the user submits a login form, a POST request to our specified path is made resulting in the execution of the passport.authenticate.
The authenticate middleware for that route is configured to handle the local strategy, passport will invoke your implementation of the local strategy.
If an error occurs while interacting with our database, we invoke done(err). Otherwise if the user is not found or the passwords do not match, we invoke done(null, false). If successful we invoke done(null, user).
Calling done will return us to the passport.authenticate and the corresponding redirect will be executed.
At this point, if the sign-in was successful, the user object (from done(null, user)) is attached to the request and you can access the user object through req.user.
The main idea is if the user object is not attached to the request it means the user is not logged in, so we can control our application behaviour for logged in
users with req.user. For example:
// If the user object does not exist it means the user is not logged in
if (!req.user) {
res.render('signin');
} else {
// If the user object exists, the user is logged in and if they try to log in we redirect them to the home page
return res.redirect('/');
}
I hope this helps.

Send data back with the Passport js failureRedirect method

I have a Passport js local signup strategy that makes use of the successRedirect and failureRedirect methods. The problem is, if there is an error with the signup process, Passport will simply redirect back to the signup form, with no data in the form.
app.post('/signup', passport.authenticate('local-signup', {
successRedirect: '/signup/avatar', // redirect to the secure profile section
failureRedirect: '/signup', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
I have the following condition inside my local signup strategy that will send a failure message to the user if the two provided passwords do not match.
if(req.body.password != req.body.confirm_password){
console.log('Passwords do not match');
return done(null, false, req.flash('signupMessage', 'Your passwords do not match'));
}
Along with the flash message, I want to send data back to the /signup route and re-populate the form with the data the user has provided.
Is there a way to send the form data back to the browser on failure?
The answer was much simpler than I expected.
The validation should happen before the data is sent to Passport js, then if the data is not valid, the /signup template can be re-rendered and the data passed back.
The solution was in this question: NodeJS express-validator with passport
Sorry, after our comments I understand what you're asking a little better now. You need to make use of a custom callback in your passport code. The passport site gives this example:
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);
});
Through a closure here, your callback has access to both the request and the response, so when you deal with your error state, you can invoke code to update the response from the originating request (e.g. pulling in the specified username or whatever it is you wish to re-populate the sign-up screen with).
http://passportjs.org/docs/authenticate

node.js+passport local authentication

I am developing a web app where after login according to type of user the page should redirect. type of user is defined by user at the time of sign-up. passport local authentication is working fine but what I have to do for redirecting the page according to usertype.. please guide
If the passport is working you could probably get that information off of the request and then use res.redirect('/whereYouWant'). Post some code? check out this answer for info on looking at the cookie: How to access Cookie set with Passport.js
Why not redirect to a page which takes of any redirections for you? Example:
router.get('/login', function(req, res) {
res.render('login', { user : req.user, status: req.param('status') });
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/redirect',
failureRedirect: '/login?status=fail',
}));
router.get('/redirect', function(req, res) {
if (req.isAuthenticated()) {
if (req.user.usertype === "admin") {
res.redirect('/adminpage');
}
if (req.user.usertype === "marketing") {
res.redirect('/marketingpage');
}
......
}
});

Correct Authentication Pattern using Passport.js

It seems the Passport docs suggest using a pattern such as:
app.get("/my/protected/resource", passport.authenticate("local"), function(req, res){
res.json({my:"protected resource"});
});
as the standard way to protect resources after authentication. However, as I have tried this strategy repeatedly, it has repeatedly given me 401 - Unauthorized. Rather, a middleware solution such as the following worked:
exports.loggedIn = function(req, res, next) {
console.log('Checking credentials...');
if(req.isAuthenticated()){
next();
} else {
res.redirect("/");
}
};
This, however, is either not found, or is not made prominent in the docs. Which of these two are the standard and correct way of protecting resources using passport.js?
If you are using sessions, I generally use this type of pattern:
app.get('/resource', passport.authenticate('local', {
successRedirect: '/success',
failureRedirect: '/login',
failureFlash: 'Invalid username or password'
}))
as shown http://passportjs.org/guide/authenticate/
If you want the response in the same function:
app.get('/resource', passport.authenticate('local', {
failureRedirect: '/login',
failureFlash: 'Invalid username or password'
}), function(req, res){
res.json({my:"protected resource"});
})
If you want to be redirected to "/" (as your second example does) rather than getting a 401 (the default behavior) when trying to access a protected resource unauthenticated, using passport.authenticate, you can use the redirect options, as specified in the second section of the page in the docs that you linked:
app.get("/my/protected/resource",
passport.authenticate("local", {failureRedirect: '/' }),
function(req, res){
res.json({my:"protected resource"});
});

Custom returnUrl on Node.js Passport's Google strategy

I'm using Express and Passport OpenID Google strategy and I would like to set returnURL on each auth request to be able to return to the page that initiated that auth.
The situation is that I have HTML5 slides application with Node.js backend (and with social stuff and editor and Portal and extensions... https://github.com/bubersson/humla) and I want be able to log in user on some slide (via slide menu...) but then I want him to get back to same slide easily.
So I would need something like this?
app.get('/auth/google', function(req,res) {
var cust = "http://localhost:1338/"+req.params.xxx;
passport.authenticate('google', returnURL:cust, function ...
}
I've read Passport's guide, but still don't know how to do that. I know this wouldn't be safe, but how else could I do it?
Or how can I make the application to return to the page from where the login has been initiated? Or is there a way to make OpenID authentication using AJAX (and still be able to use passport as well)?
I've figured this out for my apps Twitter authentication, I am sure that the GoogleStrategy is quite similar. Try a variant of this:
Assuming you have defined the route for the callback from the authentication service like so (from the passport guide):
app.get('/auth/twitter/callback',
passport.authenticate('twitter', {
successRedirect: authenticationRedirect(req, '/account')
, failureRedirect: '/'
})
);
Just change that block to this:
app.get('/auth/twitter/callback', function(req, res, next){
passport.authenticate('twitter', function(err, user, info){
// This is the default destination upon successful login.
var redirectUrl = '/account';
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
// If we have previously stored a redirectUrl, use that,
// otherwise, use the default.
if (req.session.redirectUrl) {
redirectUrl = req.session.redirectUrl;
req.session.redirectUrl = null;
}
req.logIn(user, function(err){
if (err) { return next(err); }
});
res.redirect(redirectUrl);
})(req, res, next);
});
Now, define your middleware for authenticated routes to store the original URL in the session like this:
ensureAuthenticated = function (req, res, next) {
if (req.isAuthenticated()) { return next(); }
// If the user is not authenticated, then we will start the authentication
// process. Before we do, let's store this originally requested URL in the
// session so we know where to return the user later.
req.session.redirectUrl = req.url;
// Resume normal authentication...
logger.info('User is not authenticated.');
req.flash("warn", "You must be logged-in to do that.");
res.redirect('/');
}
Works!
Wherever you have your login button, append the request's current URL as a
query parameter (adjust for whatever templating system you use):
<a href='/auth/google?redirect=<%= req.url %>'>Log In</a>
Then, add middleware to your GET /auth/google handler that stores this value in
req.session:
app.get('/auth/google', function(req, res, next) {
req.session.redirect = req.query.redirect;
next();
}, passport.authenticate('google'));
Finally, in your callback handler, redirect to the URL stored in the session:
app.get('/auth/google/callback', passport.authenticate('google',
failureRedirect: '/'
), function (req, res) {
res.redirect(req.session.redirect || '/');
delete req.session.redirect;
});
Try res.redirect('back'); in the callback for passport.authenticate
According to the author this isn't possible with OpenID strategies. We managed to update these dynamically by directly accessing the variables:
app.get('/auth/google', function(req, res, next) {
passport._strategies['google']._relyingParty.returnUrl = 'http://localhost:3000/test';
passport._strategies['google']._relyingParty.realm = 'http://localhost:3000';
passport.authenticate('google')(req, res, next);
});

Resources