Understanding Callbacks in Express - node.js

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.

Related

How works next() in express

I think that I know why we use next() in expressjs in theory: :)
In fact, the routing methods can have more than one callback function as arguments. With multiple callback functions, it is important to provide next as an argument to the callback function and then call next() within the body of the function to hand off control to the next callback.
But in practice I can't get it.
app.get('/project', (req, res, next) => {
con.query("SELECT * FROM issues", (err, result) => {
if (err) throw err;
res.send(result);
})
next()
console.log("NEVER HERE");
});
This is situation where this code do not work.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
But if I put next() right bellow res.send(result); and remove this one above console.log('NEVER HERE'); statement it works perfectly.
Also if I remove next() on both places it works well.
And what I noticed is if I use console.log(result) instead of res.send(result) it works no metter where next() callback is.
My main question is why one next() "makes troubles" to other one, when they are used together with res.send().
I'm new to express and also with mysql, thank you in advance. :)
You are not supposed to use both res.send and next
The purpose of next is to pass control to another middleware but when you respond to the request using res.send(...) there's no reason to that.
Simple example when you would use next:
app.get('/admin', function authMiddleware(req, res, next){
// user must be logged in and be an admin to access this route
if (req.user && req.user.isAdmin)
next(); // this will pass control to the handler function bellow
else // respond with 401 error
res.status(401).send('unauthorized');
}, function handler(req, res){
// only get here when user is admin
res.render('admin');
});
Your example should look like this, you don't need to care about next at all:
app.get('/project', (req, res) => {
con.query("SELECT * FROM issues", (err, result) => {
// don't throw, just respond appropriately
//if (err) throw err;
if (err)
res.status(500).send('error occured');
else
res.send(result);
});
// calling `next` here doesn't make any sense unless you pass an error
});
You could use the next function but you should have setup an error handler and you need to pass the error (Docs):
app.get('/project', (req, res, next) => {
con.query("SELECT * FROM issues", (err, result) => {
// don't throw, just respond appropriately
//if (err) throw err;
if (err)
next('error occured');
else
res.send(result);
});
});
// Error handler
app.use(function (err, req, res, next) {
console.error(err)
res.status(500).send('error occured')
});

Passport Js custom callback function not calling next middleware

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.

Ajax call to passport does not run the authenticate function

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

How can I use next() inside passport.authenticate(...) in Express middleware?

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?

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.

Resources