Passport.authenticate() Error handling inside a route? - node.js

If I want to handle errors from "incorrect" logins on this route how to collect (err) and where?
// POST LOGIN
usersRouter
.route('/login')
.post(passport.authenticate('local'), (req, res, next) => {
// for example, to declare an if here to catch err and call next with err...
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json({ success: true, status: 'You are successfully logged in!' });
});

You can use like this
usersRouter.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) {
return res.send(401,{ success : false, message : 'authentication failed' });
}
return res.send({ success : true, message : 'authentication succeeded' });
})(req, res, next);
});

Related

Why does my MERN / Passport User Login not redirect on success?

When entering the correct login info, it does nothing. No error logs or redirect.
router.post("/login", (req, res, next) => {
passport.authenticate(
"local",
{ successRedirect: "/dashboard" },
(err, user, done) => {
if (!user) {
return res.json(done); //sends error msg ("email not registered", "password incorrect", etc.)
}
}
)(req, res, next);
});
What could be the issue for this?
passport.authenticate accept 2 parameters. If you choose custom callback, then removing { successRedirect: "/dashboard" }. Code 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);
});

How to pass parameter from Express to Angular 6?

How can I pass parameter from Express route to A
`router.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
res.locals.username=req.user.username;
res.redirect('/home');
});`
This is what I have in the route for Express. I want to pass the username from here to the frontend that I have done in Angular and echo it.
You should return a JSON versus redirects and in the front-end side catch the response to render it.
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return res.json({ error: err });
}
if (!user) {
return res.json({ error: 'Authentication failed' });
}
req.logIn(user, function(err) {
if (err) {
return res.json(err);
}
return res.json({ username: user.username });
});
})(req, res, next);
});
I based in the Passport docs in the Authentication / Custom Callback section:
http://www.passportjs.org/docs/

Handle rejection of passport.authenticate() function

I have a router like this:
/* get user info */
router.get('/info', authenticate(), function (req, res) {
models.User.getUserById(req.user.id, function (result) {
if (result) {
var user = result.dataValues;
res.json({
success: true,
data: user,
message: 'Get user info success!'
})
} else {
res.json({
success: false,
message: 'User does not exist!'
})
}
})
});
the authenticate() function is like this:
/* ensure authentication */
var authenticate = function () {
return passport.authenticate('jwt', {
session: false
});
}
If user logged in, the router would go to function(req, res). But what if user has not logged in yet ? How can I return to client a message if user has not logged in ? Can I put a callback to passport.authenticate() ? If I can, what is the parameters of this callback ?
According to Docs, you can add a failure redirect URL and a failure flash message like this :
var authenticate = function () {
return passport.authenticate('jwt', {
session: false,
failureRedirect: '/login',
failureFlash: 'Invalid username or password.'
});
}
Or if you want a custom callback :
var authenticate = function (req, res, next) {
return passport.authenticate('jwt', function (err, user, info) {
if (err) { return next(err); }
if (!user) {
// code if failed here
return res.redirect('/login')
}
req.logIn(user, function (err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username)
})
})
}

Node/Express: Notify User that they have been redirected due to unauthorized access

Node newbie here
So I'm using Node(0.12.x) Express(4.x) & Passport(0.3.x) for authentication, and if the user accesses pages without the proper authentication a res.redirect('/login') will be preformed(by way of ensureLoggedIn('/login')). After a user is redirected to the login page I want to give the user some feedback to tell them they have been redirected ie. "You have been redirected due to unauthorized access, please login". How can I do this? My code below
app.get('/', require('connect-ensure-login').ensureLoggedIn('/login'), function(req, res) {
res.sendFile(path.join(html_dir + 'form.html'), {
user: req.user
});
});
app.get('/login', function(req, res) {
res.sendFile(path.join(html_dir + 'login.html'));
});
app.post('/login', urlencodedParser, function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return res.status(500).send({
success: false,
message: err
});
}
if (!user) {
res.statusCode = 200;
return res.status(200).send({
success: false,
message: "Incorrect username or password"
});
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
return res.status(500).send({
success: false,
message: err
});
}
console.log("verified");
res.statusCode = 200;
res.setHeader("Location", "/");
return res.status(200).send({
success: true
});
});
})(req, res, next);
});

Express Passport (node.js) error handling

I've looked at how error handling should work in node via this question Error handling principles for Node.js + Express.js applications?, but I'm not sure what passport's doing when it fails authentication. I have the following LocalStrategy:
passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' },
function(email, password, next) {
User.find({email: UemOrUnm}, function(err, user){
if (err) { console.log('Error > some err'); return next(err); }
if (!user) { console.log('Error > no user'); return next('Incorrect login or password'); }
if (password != user.password) {
return next(Incorrect login or password);
}
return next(null, user);
});
}
));
After I see 'Error > some err' console printout, nothing else happens. I would think it should continue on the the next path with an error parameter, but it doesn't seem to do that. What's going on?
The strategy-implementation works in conjunction with passport.authenticate to both authenticate a request, and handle success/failure.
Say you're using this route (which is passed an e-mail address and a password):
app.post('/login', passport.authenticate('local', {
successRedirect: '/loggedin',
failureRedirect: '/login', // see text
failureFlash: true // optional, see text as well
});
This will call the code in the strategy, where one of three conditions can happen:
An internal error occurred trying to fetch the users' information (say the database connection is gone); this error would be passed on: next(err); this will be handled by Express and generate an HTTP 500 response;
The provided credentials are invalid (there is no user with the supplied e-mail address, or the password is a mismatch); in that case, you don't generate an error, but you pass a false as the user object: next(null, false); this will trigger the failureRedirect (if you don't define one, a HTTP 401 Unauthorized response will be generated);
Everything checks out, you have a valid user object, so you pass it along: next(null, user); this will trigger the successRedirect;
In case of an invalid authentication (but not an internal error), you can pass an extra message along with the callback:
next(null, false, { message : 'invalid e-mail address or password' });
If you have used failureFlash and installed the connect-flash middleware, the supplied message is stored in the session and can be accessed easily to, for example, be used in a template.
EDIT: it's also possible to completely handle the result of the authentication process yourself (instead of Passport sending a redirect or 401):
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (! user) {
return res.send({ success : false, message : 'authentication failed' });
}
// ***********************************************************************
// "Note that when using a custom callback, it becomes the application's
// responsibility to establish a session (by calling req.login()) and send
// a response."
// Source: http://passportjs.org/docs
// ***********************************************************************
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'authentication succeeded' });
});
})(req, res, next);
});
What Christian was saying was you need to add the function
req.login(user, function(err){
if(err){
return next(err);
}
return res.send({success:true});
});
So the whole route would be:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (! user) {
return res.send(401,{ success : false, message : 'authentication failed' });
}
req.login(user, function(err){
if(err){
return next(err);
}
return res.send({ success : true, message : 'authentication succeeded' });
});
})(req, res, next);
});
source: http://passportjs.org/guide/login/
You need to add req.logIn(function (err) { }); and do the success redirect inside the callback function
Some time has passed and now the most right code will be:
passport.authenticate('local', (err, user, info) => {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (!user) {
return res.status(401).send({ error: 'Authentication failed' });
}
req.login(user, (err) => {
if (err) {
return next(err);
}
return res.status(202).send({ error: 'Authentication succeeded' });
});
});
I found this thread very useful!
https://github.com/jaredhanson/passport-local/issues/2
You could use this to return error and render it in form.
app.post('/login',
passport.authenticate('local', { successRedirect: '/home', failWithError: true }),
function(err, req, res, next) {
// handle error
return res.render('login-form');
}
);
This is what I got after console.log(req) at the failler route.
const localStrategy = new LocalStrategy({ usernameField: "email" }, verifyUser);
passport.use(localStrategy);
const authenticateWithCredentials = passport.authenticate("local", {
failureRedirect: "/api/auth/login-fail",
failureMessage: true,
});
validation method find your user from db and throw error to the cb if there is any
const verifyUser = async (email, password, cb) => {
const user = await User.findOne({ email });
if (!user) return cb(null, false, { message: "email/password incorrect!" });
const isMatched = await user.comparePassword(password);
if (!isMatched)
return cb(null, false, { message: "email/password incorrect!" });
cb(null, {
id: user._id,
email,
name: user.name,
});
};
now setup your route
router.post("/sign-in", authenticateWithCredentials,(req, res) => {
res.json({user: req.user})
});
router.get("/login-fail", (req, res) => {
let message = "Invalid login request!";
// if you are using typescript cast the sessionStore to any
const sessions = req.sessionStore.sessions || {};
for (let key in sessions) {
const messages = JSON.parse(sessions[key])?.messages;
if (messages.length) {
message = messages[0];
break;
}
}
res.status(401).json({ error: message });
});

Resources