-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's the use case for passing a callback to a strategy? #422
Comments
Ok, so it looks like this is intentional, given the example in the comments: app.get('/protected', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) { return res.redirect('/signin') }
res.redirect('/account');
})(req, res, next);
}); Although this is a little bit unintuitive since your code needs both a callback and pass req/res/next to middleware in order to cover all possible cases. Plus it relies on generating a new middleware function for every request, which feels like unnecessary overhead. If you'd entertain a PR to smooth this out a bit, I'd be happy to give it a go. |
What would the PR do differently? A custom callback should be used only in rare instances, so being unintuitive is not a big concern. |
Also, use of custom callbacks by an application developer should not impact how a custom strategy is implemented. The custom callback is handled by Passport core, independent of the strategy itself. |
My initial thought would be simply to pass a req/res/next as well, so your code could look like: app.get('/protected', passport.authenticate('local', function(err, user, info, status, req, res, next) {
if (err) { return next(err) }
if (!user) { return res.redirect('/signin') }
res.redirect('/account');
})); I think the main use case for this would be logging information about the request based on its success/failure for auditing, regardless of the other options you pass to the strategy. I also think using the API consistently will also help avoid user error; I had to dig around in the source code quite a bit before understanding how it works. If i was feeling ambitious, it'd want to have the if (typeof callback !== 'function') {
callback = function (err, user, info, status, req, res, next) {
if (err) {
return allTheStuffPassportDoesForErrors(err, user, info, status, req, res, next);
}
if (user) {
return allTheStuffPassportDoesForSuccess(err, user, info, status, req, res, next);
}
return alltheStuffPassportDoesForFailure(err, user, info, status, req, res, next);
}
}
// ...
strategy.success = function(user, info) {
callback(null, user, info, 200, req, res, next);
}; Additionally, the |
I like the idea of factoring all the stuff passport does into a separate function, and assigning that to the callback. I don't like the idea of passing |
Cool. I'll put some think together; functioning code is much easier to talk about. |
Add #423 -- I know you mentioned you don't like passing function (req,res,next) {
passport.authenticate('strategy', function () {
// stuff
})(req,res,next);
} structure. I hope my rationale in the PR makes sense. |
I don't think that structure needs avoiding Sent from my iPhone
|
Well, I'd love to understand why, but I'm not going to badger you if you're going to reject the PR. The way I see it, the existing structure:
While the optimizations are small, I don't see any drawbacks of using them. |
This one isn't a priority for me. Passport is written in JavaScript, and uses the middleware idioms established by Express/Connect. The learning curve is small, in my opinion, and worth climbing for any engineer who is serious about developing web applications in JavaScript. Further, once the middleware pattern is understood, it allows applications to be composed very easily. Passport itself takes advantage of this composibility. The other two points are valid, and I'm considering them. However, I think they can be accomplished while remaining standard middleware and without changing the callback's function signature. |
I think that's a fair point; I'm starting to try and think through what the callback can even give you that other passport options do not, and what I'm getting is:
Doesn't seem like much. You can get pretty much the same behavior with something like: var AuthenticationError = require('passport/lib/authenticationerror');
app.get('/protected',
passport.authenticate('local', {
authInfo: true,
failWithError: true,
session: false
}),
function(req, res) {
// req.user and req.authInfo are available
res.redirect('/account');
},
function(err, req, res, next) {
if (err instanceof AuthenticationError) {
// status is available on res.statusCode if it was truthy
// challenge is accessible with res.getHeader('WWW-Authenticate') if it was a string
// but has been cast into an array
return res.redirect('/signin');
}
next(err);
}
); ...which feels like better code than the callback version anyway. So maybe for my docs I'll just ignore the If you are interested in some PRs that avoid checking properties more than once, I'm happy to contribute. It would break passport's ability to support something like: var options = {};
setInterval(function () {
options.authInfo = !options.authInfo;
}, 100);
app.get('/protected', passport.authenticate('local', options); ...but that seems like an enhancement, not a regression. |
Welp, not sure how I found myself here, but anyhow. You're trying to re-define conventions here which are very well established, and your proposed approach doesn't actually help in many of the use cases in which developers would use this sort of approach in the first place. Conventions around error handling and middleware aren't changing anytime soon. In any case, learn lexical scope & take advantage of it. Creating functions in JS is super cheap. Here's a more realistic example of how these pieces would fit together:
As you can see, your approach kind of defeats the point considering most people would need to do a custom wrapper anyway I'm using express-error-funnel as my error middleware in order to do meaningful parsing, especially of errors created using node-restify-errors. By the way, your PR is dependent on express flash, which was deprecated |
Ah, that's my problem. Thanks. It doesn't matter, since the PR isn't going to get accepted. While I still think the API is unintuitive, the author doesn't believe that to be a problem, so it won't change. If performance ever gets to be a problem, I can submit my load test results with a PR, but for now it's fine. I just had some spare cycles and thought it would be fun to contribute to a good project, even if in a small way. Not interested in a flame war. |
I'm working on a custom Passport strategy, and documenting how to use it. I'm seeing this in the code:
lib/middleware/authenticate.js
Which, if I read this correctly, means that the request will timeout because the request-ending methods of the
res
andnext
arguments are never invoked. This callback is used forsuccess
anderror
, but notfailure
,pass
orredirect
results.Is there a use-case for this I don't understand, or is this a bug?
EDIT: looks like it is called for
fail
if all strategies fail.The text was updated successfully, but these errors were encountered: