Express middleware Passport not responding unauthorized - node.js

Passport
passport.use('jwt', new JwtStrategy(opts, function(jwt_payload, done) {
User.where({id: jwt_payload.id}).fetch().then(function(user) {
if(user) {
return done(null, user);
} else {
return done(null, false);
}
}).catch(function(err) {
return done(err, false);
});
}));
Example 2
This works but when the JWT is not set, I get res = null when I think I should be getting an 401 response.
app.get('/user', getProfile);
getProfile = function(req, res, next) {
passport.authenticate('jwt', {session: false}, function(err, user, info) {
if(user) {
res.json(user);
} else {
res.json(err);
}
})(res, req, next);
};
Example 2
When the JWT is not set then I get the correct 401 response but if it is set I can't get user returned because res doesn't exist.
app.get('/user', passport.authenticate('jwt', {session: false}, getProfile);
getProfile = function(err, user) {
if(user) {
res.json(user);
} else {
res.json(err);
}
};
So how do I pass res into this function?

Example 1
In your first example, it looks like you've just mixed up the order of req and res in your function call. It should be
})(req, res, next);
not
})(res, req, next);
Example 2
In your second example, I think you're using the callback to passport.authenticate incorrectly.
The passport.authenticate method is just middleware to be called before your actual route gets hit. Its callback does not replace the regular route callback function you would define to handle sending a response - you still need to provide a route callback after the middleware.
app.get('/user',
passport.authenticate('jwt', { session: false }),
function(req, res, next) {
res.json(req.user);
});
The authenticate method should handle responding with an appropriate status code if the user was not authenticated, so you can safely call req.user in your route callback and know the user is authenticated.

Related

How to perform some logic if JWT token is not present in header?

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
}
);

Passport serializeUser() is not called with this authenticate() callback

Using passport.js, I write the route this way so I have access to the MongoDb document userDoc. But, when doing it this way... passport.serializeUser() will never be called and the req object will be missing user.
auth.route('/auth/facebook/callback')
.get(function(req, res, next) {
passport.authenticate('facebook', function(err, userDoc, info) {
if (err) { return next(err); }
// I don't think !userDoc will ever happen because of mongo upsert
if (!userDoc) { return res.redirect('/login'); }
res.cookie('facebookPicUrl', userDoc.value.facebook.picture, {maxAge : 9999999,
httpOnly: false,
secure: false,
signed: false
});
res.redirect('http://localhost:9000/users')
})(req, res, next);
});
But if I write it this way, the req.user is there as it should be:
auth.route('/auth/facebook/callback')
.get(passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('http://localhost:9000/users')
});
How can I make this to where passport.serializeUser is called and user exists on req and I also have access to the mongoDb object?
Since you are using the custom authentication callback you are responsible for establishing the session.
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.
req.login() assigns the user object to the request object req as req.user once the login operation completes.
You can see for example that in the documentation req.login() is explicitly called in the custom callback:
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);
});

Node.js passport custom callback

I am using passport for my node.js app.
When I want to authenticate users local, I can simply do it
function local(req, res) {
req._passport.instance.authenticate('local', function(err, user, info) {
if(err) {
return workflow.emit('exception', err);
}
// and so on
res.end('some data');
}
}
But when I want to use facebook strategy, I must use redirectUrls like this.
function signinFacebook(req, res, next) {
req._passport.instance.authenticate('facebook')(req, res, next);
}
function facebookCallback(req, res, next) {
req._passport.instance.authenticate('facebook', {
successRedirect: '/',
failureRedirect: '/'
})(req, res, next);
}
This way I cant send with response data, that I am sending on local strategy.
Can anyone help me to fix it. I want not give success and failure Redirects, I want to call some function if all goes well like on local strategy.
I've found this in Passport's documentation, it may help.
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);
});
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.

Send info message from verify callback to client

I am trying to send the info message that gets set in my verify callback:
Here is the example from passport docs:
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);
});
}
));
However if I write my route as:
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
That function does not get called with the info message. At least not that I know of. I know passport shoves the user into the req, as I can access it from req.user. Is there a way to access the info message like this. Or do I need to specify a custom callback?
Which they outline as:
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);
});
The part that is confusing is that they outline using a 3rd parameter in the done callback (from verify) that is a message, yet you can only access that if you write a custom callback. Is that true.
The adopted convention in the Passport framework is that the optional info object will be available through req.authInfo.
This however depends on the used strategy which is responsible for passing it to the Passport framework. For instance, the passport-http strategy does not forward the info object to Passport while the passport-local does.
Here is how you can then access it in a controller protected by the local strategy :
app.post('/login',
passport.authenticate('local'),
function (req, res) {
if (req.authInfo) {
// req.authInfo.message will contain your actual message.
}
});

req object in passport.authenticate

I am using passportjs for authentication on my server. I am using the following code:
exports.auth = function(req, res, next){
passport.authenticate('bearer', { session: false })(req, res, next);
};
passport.use(new BearerStrategy(
function(token, done) {
User.findOne({ token: token }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false);
}
return done(null, user, { scope: 'read' });
});
}
));
Is there a way to access the req object in passport.use? This was I can get the user ip address and check for eventual attacks.
The comments in the example suggest that you can pass an object { "passReqToCallback": true } to make the req callback available in the callback function. Which can be accessed as
function(req, token, done){//rest of the function body}
So initialize passport.use as
passport.use(new BearerStrategy({ "passReqToCallback": true },
function(req, token, done) {
});
and you should have req in the callback.

Resources