Hi Jared, I stumbled upon a little problem. I have this code here:
@app.post "/sessions", @passport.authenticate("local", failureRedirect: "/sessions/login"), (req, res) ->
res.redirect @sessionRoutesPathHelper.profile(req.user.username)
which, upon successful authentication should redirect to the users profile page. This does not work though as the user object in req is only set in the next request, after the redirect.
I think it would be a good idea to set req.user to the user in authenticate before invoking the callback
From the looks of your code, you're not passing a callback to authenticate(), so Passport should fall into default behavior: login the user, set req.user, and call next(). At that point, your route handler with the helper-based redirect should be invoked with req.user available.
Once the holidays have slowed down, I'll look into this to find out what may be going wrong.
I ran into this, too. It'd be helpful if the authenticate documentation mentioned this. I worked around it by just calling req.login(...) manually so I still can take advantage of passport serializing the user etc. Not sure if that's correct, but it's working. :-)
Do you have any code (preferably JavaScript) that I could use to reproduce?
You mean a code example using the callback and req.login? If so it's below.
We didn't want a redirect as the failure (why redirect just to show the same page) -- and this was a cleaner way to keep the username and show an error, I think. I think it'd make sense to have authenticate() take functions as options for failure and success in addition to the current redirect options. "Call this if authenticate fails", "Call that if it succeeds".
app.post('/login',
function(req, res, next) {
passport.authenticate('local', function(err, user) {
if (err) { return next(err) }
if (!user) {
res.local("username", req.param('username'));
return res.render('login', { error: true });
}
// make passportjs setup the user object, serialize the user, ...
req.login(user, {}, function(err) {
if (err) { return next(err) };
return res.redirect("/dashboard/");
});
})(req, res, next);
return;
}
);
Just a quick comment that this piece of example code from the comment above:
// make passportjs setup the user object, serialize the user, ...
req.login(user, {}, function(err) {
if (err) { return next(err) };
return res.redirect("/dashboard/");
});
Would be super handy to have included in the "Custom Callback" example in the user doc here:
http://passportjs.org/guide/authenticate.html
That example mentions "when using a custom callback, it becomes the application's responsibility to establish a session (if necessary)", but I was stumped on how to do that until I ran across the comment in this issue. Thanks abh!
I'm closing down tickets. There's a couple separate issues in this one, so I'll tackle them in separate comments.
First, on @mwawrusch report.
Here's the roughly equivalent code in JavaScript:
app.post('/login', passport.authenticate('local', { failureRedirect: '/login' }), function(req, res) {
res.redirect('/users/' + req.user.username)
});
This middleware chain works as expected for me. After the passport.authenticate() function succeeds, the final route handler is invoked and has access to req.user and builds a URL to redirect to.
Since I can't reproduce this, I'm hoping that the root cause was something else and that you're not having this issue anymore. If that's not the case, feel free to reopen the issue and provide any additional details.
Second, on comments from @abh and @thehunmonkgroup
The documentation lacked details on how to login and establish a session from within a custom callback (also reported in #10). I've updated the docs to provide this in example code.
I'm going to be adding additional options to authenticate() for displaying flash messages on failure. (Track #12 if interested.) I'm going to stay away from separate success and failure functions as options, however. That can be accomplished as is with the single callback(err, user) form, which is more idiomatic Node.
This took me a while to figure out if this was the appropriate thread to comment on, because there are multiple problems, but they're all in the same vein. @jaredhanson and @mwawrusch , I think I'm having this issue, and I've done a lot of reading to try to narrow down my problem. The way I found it was: I found out that I couldn't pass my req.user variable into my successful redirect template. I thought it did, but that's because I was _rendering_ a view, and not redirecting, in Express.
req.user, or anything in Passport, doesn't survive redirects, but it can survive routes.
I haven't figured out a solution yet, although I'm sure it's more simple than I think.
coryarmbrecht i have kinda the same problem, i thought the object req.user survive the redirect but it doesnt, i want to do the same as you, witch was to fill my template after redirect with user data, and i cant do it, if you have any clue about that let me know
@Luisreino , unfortunately, I didn't, and I haven't revisited passport since this problem. If you do find a solution (and I haven't implemented everyauth instead), please let me know.
@coryarmbrecht Can you explain a bit more about what you mean when you say "req.user, or anything in Passport, doesn't survive redirects".
Redirects end a request/response pair, and because HTTP is stateless, no variables on req or res will survive. Anything you need access to on the next request/response will need to be saved in the session. That's what the serializationUser function is intended to do.
Gotcha. I'll be looking back into this soon with a fresh version.
router.post("/signup",function(req, res, next) {
var username = req.body.username;
var phoneNumber = req.body.phoneNumber;
var email = req.body.email;
User.findOne({ $or: [{ username: username }, { email: email }, { phone_Number: phoneNumber }] }, function (err, user) {
if (err) { return next(err); }
if (user) {
return res.send({message:"User already exists"});
}
var password = req.body.password;
var newUser = new User({
username: username,
email: email,
phone_Number: phoneNumber,
password: password,
});
newUser.save(next);
var token = new Token({
_userId: newUser._id,
token: crypto.randomBytes(16).toString('hex')
});
token.save(next);
ejs.renderFile(__dirname + '/templates/template.ejs', (err, data) => {
if (err) {
console.log(err.name + " " + err.message);
console.log('if')
}
else {
console.log('else')
var options = {
auth: {
api_user: '',
api_key: ''
}
}
var client = nodemailer.createTransport(sgTransport(options));
var email = {
from: '<no-reply>@com',
to: '@hotmail.com',
subject: 'Email confirmation for tutorns account',
html: data
};
client.sendMail(email, function(err, info){
if (err){
return res.send({message:err.message})
}
else{
return res.send({success:true})
}
})
}
})
}),passport.authenticate('local-login',function (err, user, message){
console.log('here');
if (err) {
console.log('here1');
return res.send(err.message)
}
if (user) {
req.logIn(user, loginErr => {
if(loginErr) {
console.log(loginErr)
return res.json({ success: false, message: loginErr })
}
console.log(user)
return res.send({user:user});
})
}
})(req, res, next)
})
else
[1] here
[1] _http_outgoing.js:494
[1] throw new Error('Can\'t set headers after they are sent.');
[1] ^
[1]
[1] Error: Can't set headers after they are sent.
[1] at validateHeader (_http_outgoing.js:494:11)
[1] at ServerResponse.setHeader (_http_outgoing.js:501:3)
[1] at ServerResponse.header (D:\Codes\13-5-18node_modules\express\libresponse.js:767:10)
[1] at ServerResponse.send (D:\Codes\13-5-18node_modules\express\libresponse.js:170:12)
[1] at ServerResponse.json (D:\Codes\13-5-18node_modules\express\libresponse.js:267:15)
[1] at ServerResponse.send (D:\Codes\13-5-18node_modules\express\libresponse.js:158:21)
could please someone tell me what``s the reason of getting this error
Most helpful comment
You mean a code example using the callback and req.login? If so it's below.
We didn't want a redirect as the failure (why redirect just to show the same page) -- and this was a cleaner way to keep the username and show an error, I think. I think it'd make sense to have authenticate() take functions as options for failure and success in addition to the current redirect options. "Call this if authenticate fails", "Call that if it succeeds".