I am using passport.js for authentication in my web app. I am following the example given here, that is the following code:
app.get('/api/me',
passport.authenticate('bearer', { session: false }),
function(req, res) {
res.json(req.user);
});
now, the thing is that the code inside function(req, res) is called only with the correct access_token. If the login is wrong the function is never called and it just returns "Unauthorized". Is there a way to execute the function anyway? For example something like this:
app.get('/api/me',
passport.authenticate('bearer', { session: false }),
function(req, res) {
if(req.user){
res.json(req.user);
} else {
res.json({ error: 'whatever' })
}
});
which obviously does not work as the function is never called!
here is a pastie from my code. I would basically like my router to render the page anyway, even with wrong credentials.
From Passport's authenticate page, the authenticate method accepts an optional custom callback function as its third argument.
I haven't tried out with a Bearer strategy, but I don't think it will be any different.
BUT bear in mind that if you this callback function, you have to handle the login process yourself, using res.logIn()
So
app.get('/api/me',
passport.authenticate('bearer', { session: false },
function(err, user,info) {
if (err) { return next(err); } // If there's an error the process
if (!user) { return res.json({ error: 'user not found' }) } // If the user's not found
req.logIn(user, function(err) { // User exists !
if (err) { return next(err); }
return res.json(req.user);
});
});
);
Does that help you ?
Related
Working on Passport.js authentication and want to display messages to client when login is not successful.
My current logic:
app.route('/login').post(passport.authenticate('local', { failureRedirect: '/' }),
(req, res) => {
res.redirect('/profile')
});
Also using similar thing in registeration which is working as I intended using connect-flash to display my message.
app.route('/register')
.post((req, res, next) => {
myDataBase.findOne({ username: req.body.username }, function (err, user) {
if (err) {
next(err);
} else if (user) {
req.flash('message', 'Username is already registered, please try different username');
res.redirect('/');
} else {........
How can I apply similar solution to my login logic? I suppose I need to create a custom callback function but could not figure it out.
I'm using express + passport to handle authentication for a react app. Because I'm using react, I don't want to automatically redirect the login request via express, instead I want to return JSON.
However, I've noticed that simply returning JSON and then calling next(null, user) seems to prevent passport from running the serializeUser method. By calling next passport should automatically call the req.login function, which doesn't appear to happen when I've sent json.
It appears that if I manually call req.login and then send the JSON, it works (the serialize method is called, the user has a session).
Is there a way to send a json response and call next without having to manually invoke req.login? Feels like I'm just not doing things the correct way.
module.exports = function(app) {
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user) => {
if (err) {
res.json({ errorMessage: err.message });
return next(err);
}
if (!user) {
res.json({ errorMessage: 'Invalid user.' });
return next(err);
}
// This fails to invoke the session serialization:
// res.json({
// success: true,
// username: user.username
// });
// return next(null, user);
// This works:
req.logIn(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
res.json({
success: true,
username: user.username
});
return;
});
})(req, res, next);
});
};
I am using passport-jwt and there is an endpoint, which should return some data from database if jwt token is not present in header.
Is it possible to apply some logic instead of just sending Unauthorized 401?
router.get(
'/get/items',
passport.authenticate('jwt', {session: false}),
(req, res) => {
// reaches this point only after validating token
...
}
);
So if jwt token is present, based on it endpoint should return some data. If not, some other data from db should be returned
I think the custom callback is an option. It is passed as last parameter to authenticate(strategy, options, callback) method and it will allow you to set the behavior that you wish.
Your code will look like:
app.get('/get/items', (req, res, next) => {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (!user) {
/*
Unauthorized accees.
Handle here the request as u wish
*/
/* Do some custom logic and return your desired result */
return res.status(401).json({ success: false, message: 'Unauthorized access!' });
}
/* User is authorized. Do other stuff here and return your desired result*/
return res.status(200).json({ success: true, message: 'Congratulations!' });
})(req, res, next);
});
In this example, note that authenticate() is called from within the
route handler, rather than being used as route middleware. This gives
the callback access to the req and res objects through closure.
If authentication failed, user will be set to false. If an exception
occurred, err will be set. An optional info argument will be passed,
containing additional details provided by the strategy's verify
callback.
The callback can use the arguments supplied to handle the
authentication result as desired. 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
Wrap your middleware and handle the errors how you want:
function authenticate(req, res, next) {
passport.authenticate('jwt', { session: false }, (err, user) => {
if (err) {
res.status(err.statusCode || 401).json({ error: err.toString() });
return;
}
if (!user) {
res.status(404).json({ ... });
return;
}
req.user = user;
next();
})(req, res, next);
}
Use that instead:
router.get(
'/get/items',
authenticate,
(req, res) => {
// reaches this point only after validating token
...
}
);
The answers from #codtex and #Dominic solves the problem.
I found out that following solution also works:
router.get(
'/get/items',
passport.authenticate('jwt', {session: false}),
(req, res) => {
// reaches this point only after validating token
...
},
(err, req, res, next) => {
console.log('error handling');
// reaches this point if validation fails
}
);
I have a simple passport setup with a local strategy for my login route. I'm not sure how to properly use the authentication function as a middleware though. Here is my Middleware that I call on the /login route before the controller:
exports.requireLogin = (req, res, next) => {
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
next(err);
} else if (!user) {
res.status(401).json(info.message);
} else {
req.login(user, (loginError) => {
if (loginError) {
console.log('error here');
next(new Error('test'));
} else {
next();
}
});
}
})(req, res, next);
};
As you can see, the passport function is immediately invoked with the middleware parameters. That is what I found here on Stackoverflow as how to use next in that function.
Now that doesn't work for some reason. The error is not really handled as error. I checked this by making a none-error middleware as last possible endpoint in express and console-logged it out. I can see that it logs out the error here right before the line where the error is passed to next but it also logs out the check that the normal middleware is called after that.
As far as I know Express shouldn't call any normal middleware when you pass an error to next. Why doesn't this example work?
Okay, so using passport.js works, and works well, from what I've seen. However, I'm not sure how to properly exclude certain users. If the application is intended to have restricted access, rather than just providing the user a method for logging in, how can I restrict the login through passport.js? As it stands, users can just visit /login and log in with their Google account, thereby getting access to the internals.
Here is one way to do this, with comments throughout. The main thing is understanding this page from the author: http://passportjs.org/guide/authenticate/, which I explain a little more in this example ...
It might be easier to read bottom to top:
var authenticate = function(req, success, failure) {
// Use the Google strategy with passport.js, but with a custom callback.
// passport.authenticate returns Connect middleware that we will use below.
//
// For reference: http://passportjs.org/guide/authenticate/
return passport.authenticate('google',
// This is the 'custom callback' part
function (err, user, info) {
if (err) {
failure(err);
}
else if (!user) {
failure("Invalid login data");
}
else {
// Here, you can do what you want to control
// access. For example, you asked to deny users
// with a specific email address:
if (user.emails[0].value === "no#emails.com") {
failure("User not allowed");
}
else {
// req.login is added by the passport.initialize()
// middleware to manage login state. We need
// to call it directly, as we're overriding
// the default passport behavior.
req.login(user, function(err) {
if (err) {
failure(err);
}
success();
});
}
}
}
);
};
One idea is to wrap the above code in some more middleware, to make it easier to read:
// This defines what we send back to clients that want to authenticate
// with the system.
var authMiddleware = function(req, res, next) {
var success = function() {
res.send(200, "Login successul");
};
var failure = function(error) {
console.log(error);
res.send(401, "Unauthorized");
};
var middleware = authenticate(req, success, failure);
middleware(req, res, next);
};
// GET /auth/google/return
// Use custom middleware to handle the return from Google.
// The first /auth/google call can remain the same.
app.get('/auth/google/return', authMiddleware);
(This all assumes we're using Express.)
Try this.
googleLogin: function(req, res) {
passport.authenticate('google', { failureRedirect: '/login', scope: ['https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'] }, function(err, user) {
req.logIn(user, function(err) {
if (err) {
console.log(err);
res.view('500');
return;
}
var usrEmail = user['email'];
if(usrEmail.indexOf("#something.com") !== -1)
{
console.log('successful');
res.redirect('/');
return;
}
else
{
console.log('Invalid access');
req.logout();
res.view('403');
return;
}
});
})(req, res);
}
*