Passport serializeUser() is not called with this authenticate() callback - node.js

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

Related

Getting Information from Req.User() into Req.params() in ExpressJS

After people log into my Express app, I'd like to redirect them to a url that reads:
www.mysite.com/:user
What I'm having trouble with is that my req.params object is coming up as an empty object literal when people get re-directed after logging in.
Ideally what I'd like to do is to somehow take the information stored in req.user and pass some of that information into the URL.
So basically I'd like to get from here:
router.post('/login',
passport.authenticate('local', {successRedirect:'/', failureRedirect: '/login', failureFlash: true}));
To here:
router.get('/:user', function(req, res) {
//stuff
}
After logging in the req.user object reads like this:
{
username: joesmith#mail.com,
name: Joe Smith,
password: dkei348#$kDL^583L,
formulas: []
}
And the code I'm attempting to use is as follows:
router.get('/:user', function(req, res) {
var user = req.user.name;
req.params = {
"user": user
}
But this doesn't work because initially req.user.name is undefined if someone isn't already logged in, so I can't get past the console error.
Apparently I don't understand how req.params is generated in enough detail to get around this problem.
Thank you.
If you want to have username in parameters you shoud redirect to '/' + req.user.name); because '/' doesn't have any params. That's why it is undefined. (additionaly you shoud check if parameter is defined and handle the error, so instead of console error you get error 404 or get to next, proper routing path).
Passport Documentation privides examples of doiung it:
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('/' + req.username);
});
or more complex 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('/' + user.name);
});
})(req, res, next);
});
But I think it is not a good practice and safer would be to redirect to the proper page/content by req.user value without putting it to URL, for example setting req.auth. and then using next(); instead of res.redirect('/' + user.name); to get to the next middleware. There username would be taken from req.auth.username, not from /:username.
How does the structure of your routing looks? You shoud make sure router.get('/:user' .. is not before router.get('/login'...
Looks like you are wanting to access whatever /:user value is. e.g. www.mysite.com/stretch0. Therefore you would access it like var user = req.params.user;
You can provide a custom handler, instead of letting Passport do all the redirecting:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
req.flash('error', 'Unable to authenticate user');
return res.redirect('/login');
}
if (! user) {
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) {
req.flash('error', 'Unable to log in user');
return res.redirect('/login');
}
return res.redirect('/' + user.username);
});
})(req, res, next);
});
This does mean that each logged-in user gets their "personal" URL, because you're redirecting to /USERNAME. I'm not sure from your question if that's actually your intention.

Express middleware Passport not responding unauthorized

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.

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

Use request and passport npm login account

I am using nodejs for a project,now I want login my account with passport npm,but not from webpage,from request post method,can it be done?
main code like this:
router.post('/login',function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.json(null); }
req.logIn(user, function(err) {
if (err) { return next(err); }
//return res.redirect('/'); redirect not work
});
})(req, res, next);
});
router.get('/check',function(req, res, next) {
request.post({
url:'http://localhost/login',
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
},
form:{
username:'myname',
password:'mypassword'
}},function(err,httpRes,body){
//do here...?
return res.redirect('/');
});
});
When I call "check" with get method and use the correct username/password,I can print out the user data from database in "login" method,but lost the user session when it redirect home page.Any suggestion?
It's not redirecting the user when they GET /check because the POST request to /login in /check is getting redirected itself, not the actual user. Also making internal requests to internal webpages isn't the best solution for logging in. I suggest creating login() middleware like so:
// Don't forget to set req.body.username and req.body.password when calling login().
var login = function login(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return next(); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return next(null);
});
})(req, res, next);
};
And then calling it appropriately:
router.post('/login', login, function(req, res, next) {
if (req.user) {
console.log('We logged in successfully!');
res.redirect('/');
} else {
res.json(null);
}
});
router.get('/check', function(req, res, next) {
if (!req.user) {
login(req, res, function(err) {
if (err) {
return next(err);
}
if (!req.user) {
// No user, do some error handling.
} else {
// We have the user, do some custom stuff...
}
res.redirect('/');
});
} else {
// User is logged in already, do some other custom stuff...
}
});
You can check if a user is logged in by checking if req.user exists.

Resources