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?
Related
I am trying to authenticate a user with custom callback in passport js. I have written my code based on the passport documentation.
router.post("/signin/email", function (req, res, next) {
passport.authenticate("email-local", function (err, user, info) {
if (err) {
return res.send("err");
}
if (!user) {
return res.send(info);
}
req.logIn(user, function (err) {
if (err) {
return res.send(err);
}
next(user);
});
})(req, res, next),
UsersController.getToken;
});
I want to pass the user to the next middleware i.e UsersController.getToken ,but it is not being passed. How can i solve this issue?
Passport will add the user data to req.user after login is called so you don't need to pass it with next.
I would recommend simplifying the req.logIn call to the following:
return req.logIn(user, next)
Then within you UsersController.getToken middleware you can access the user data through the req object.
I do not want a redirect in my sign-up route, it is a two stage process (the way I currently have it figured out), so I would like to submit the first form, save to db with ajax and return something, and then show the second form to complete sign up. The post route works, but the function does not run
router.route('/register')
.post((req, res, next) => {
console.log('this bit here works');
passport.authenticate('local-signup', function(error, user) {
console.log('it's here that nothing happens');
if(error) {
return res.status(500).json(error);
}
return res.json(user); //this is what I want to return;
})
})
Does passport only work with the one post call?
passport.authenticate() is an Express middleware, not a regular function.
As per the fine manual (search for "Custom Callback"), it should be used like this:
router.route('/register').post((req, res, next) => {
console.log('this bit here works');
passport.authenticate('local', function(error, user, info) {
console.log("it's here that something should happen now.");
if (error) {
return res.status(500).json(error);
}
return res.json(user);
})(req, res, next);
})
FWIW, user may not necessarily be a proper object (if authentication failed, for instance).
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.
I would like some help understanding the following example from the passport.js authenticate documentation:
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); //***UNSURE ABOUT THIS***
});
I understand what the code does - but I don't know what the (req, res, next)at the end of the callback function is for. Why is it necessary? Does it provide the values for (err, user, info)? If that's the case, why don't I see more function calls ending with arguments - Is it perhaps something to do with passing on the next object?
Would love for someone to help me improve my understanding of this concept.
Request handlers are Express middleware; they get a request, a response, and a way to pass on execution to the next layer of middleware. passport.authenticate returns middleware, but it hasn’t been attached with app.use(), so you have to pass the appropriate arguments manually.
The fact that the callback from passport.authenticate also has three arguments is just a coincidence. They won’t have the same values.
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 ?