Reputation: 177
I'm looking at the passport.js documentation and I noticed this code:
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);
});
What's going on with the (req, res, next) on the second to last line there?
From what I understand, this isn't the right syntax or context for an IIFE, and req, res and next should already be available to the authenticate function.
Thanks,
Zakiir
Upvotes: 0
Views: 61
Reputation: 9454
passport.authenticate()
returns a middleware function.
i.e. it has the signature fn(req, res, next)
.
When you want to run it within a route, you have to invoke it yourself and pass it those arguments so that it can, for example, access data from the request, manipulate the response, etc.
app.get('/login', function(req, res, next) {
passport.authenticate('local')(req, res, next);
});
That runs your local strategy and does whatever it is that it's configured to do.
Had you just written this:
app.get('/login', function(req, res, next) {
passport.authenticate('local')();
});
Then req
, res
, and next
are not available to the authenticate
function.
Simply invoking a function does not give it access to variables that are in scope at the call site. For example, this doesn't work:
function addOne() {
x++;
}
app.get('/', function() {
var x = 99;
addOne();
})
It's when you define a function that the function can access variables in scope at the definition site whenever/wherever you invoke it.
Your snippet passes in (defines) a callback that's run after the local strategy has run which lets you run arbitrary logic after the local strategy succeeded or failed to authenticate the user.
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);
});
This callback can access req
, res
, and next
since they are in scope when the callback function is defined.
However, you still need to pass req
, res
, and next
when invoking passport's authenticate
function so that it can access them. That function is already defined in the library so, unlike the defined callback function, it did not capture the res
, res
, and next
variables inside the route.
By the time the defined callback function is run, the req/res variables in scope have possibly been mutated by whatever is happening in the local strategy that authenticate
runs. The callback function gets to read the state of the variables and altogether has the final word — it's essentially post-processing the result of the authenticate
method, allowing you complete freedom to write your own logic if Passport's built-in options aren't enough.
The difference in scoping behavior between the invocation site vs the definition site of a function is what results in the confusing snippet.
Upvotes: 1