Why is passport.authenticate called twice? - passport.js

I'm learning passportjs. I'm looking at the passport-google example here
https://github.com/jaredhanson/passport-google/blob/master/examples/signon/app.js
It contains the following lines of code
app.get('/auth/google',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
And subsequently, these lines:
app.get('/auth/google/return',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
Can someone help me understand why the duplicate call to passport.authenticate is needed?

The two calls actually serve distinct functions depending on what type of request is received and at what stage of authentication the flow is at.
The first call passport.authenticate is to initiate the OpenID authentication (which is what passport-google uses under the hood) and the second call (for the return URL) is used by the OpenID Provider to respond to the prior authentication request. The Passport Strategy reads the relevant assertion from the second request and processes it accordingly -- eventually leading to either a redirection to /login if the assertion failed or a redirection to / if the assertion succeeded.
The source code at https://github.com/jaredhanson/passport-openid/blob/master/lib/passport-openid/strategy.js#L164 contains some well-written comments explaining what's happening.
As a final aside, other Passport strategies may behave differently, so not every strategy with a callback necessarily requires the same seemingly "repeated" calls to passport.authenticate(...).

Related

Passport-auth0 - infinite redirect loop, "Invalid authorization request state."

I'm trying to get a basic Auth0 app running. I followed the Node example on the Auth0 dashboard but I ran into an infinite redirect loop between /login and /callback. I tried to simplify the code using the getting started example of this repo's readme, right now my routing code looks like this:
app.get('/login',
passport.authenticate('auth0', {scope: 'openid email profile'}),
(req, res) => res.redirect("/")
)
app.get("/callback",
passport.authenticate('auth0', {failureRedirect: '/login'}),
(req, res) => {
if(!req.user) throw new Error("user null")
res.redirect("/")
}
)
Everything about my setup follows the instructions I got on my Auth0 dashboard.
I did some digging and found out that /login is called twice, then /callback is called twice, then /login twice and so on and so on. I also found out that if I give the /callback's passport.authenticate a callback, it receives these arguments: null, false, {message: "Invalid authorization request state."}
Google didn't find anything meaningful when I searched for the phrase "Invalid authorization request state." and I did everything according to the docs. Any idea what I'm doing wrong?
I have had the same issue with these error and was caused due to session loss on Kubernetes landing my calls to different instances each time (sort of like load balancing), what we did was to lower from 3 instances to 1 and that fixed it. Also you could set in your Auth0Strategy config the value of state: false to double check if this lets you move on.
It turns out I didn't have the /callback route set up properly. This is the proper setup:
router.get("/callback", (req, res, next) => {
passport.authenticate('auth0', (err, user, info) => {
if(err) return next(err)
if(!user) return res.redirect("/failure?info=" + JSON.stringify(info))
req.logIn(user, err => {
if(err) return next(err)
const returnTo = req.session.returnTo
delete req.session.returnTo
res.redirect(returnTo || '/secret')
})
})(req, res, next)
})
res.redirect should lead somewhere else and req.logIn needs to be called with the proper arguments
For those who are still having issues, the problem might lie elsewhere. When I ran into this it turned out to be caused by session cookies that weren't persisting! It turned out my reverse proxy (running on Apache) wasn't configured properly. I've described my solution here: https://stackoverflow.com/a/67891167/8221175
As for why this might happen for those interested, I've posted an explanation on one of the GitHub issues here: https://github.com/auth0/passport-auth0/issues/89#issuecomment-856971807
In short: The state query parameter in the response of a login call is saved in the session. If sessions don't persist, passport can't get that state data and it'll think someone is hijacking your session. That causes the “Invalid authorization request state.” message, as well as the user to be falsy in the passport.authenticate callback. If your code then redirects to '/login', the whole thing repeats, causing an infinite loop.
The fix is to make sure your cookies are saved properly and your sessions therefore persist. You can use the examples I've provided in the links.

How to check Passport isAuthenticated on express.Router()

Using passport.js, what is the recommended way to check if a user isAuthenticated?
I see examples of people doing things like this:
app.get('/', isAuthenticated, function(req,res){});
How does this even work, app.get only accepts two arguments?
What about when I use express.Router()?
What's the correct syntax for router.get?
More generally, checking isAuthenticated at every route seems inefficient. Is there a better way to check authentication in an Express app?
Thanks.
app.get accepts as many middlewares as you need. According to the documentation:
router.METHOD(path, [callback, ...] callback)
...
You can provide multiple callbacks, and all are treated equally, and behave just like middleware, except that these callbacks may
invoke next('route') to bypass the remaining route callback(s). You
can use this mechanism to perform pre-conditions on a route then pass
control to subsequent routes when there is no reason to proceed with
the route matched.
This is how your authentication middlware function may look like:
function isAuthenticated(req, res, next) {
if(/*check authentification*/) {
return next();
}
res.send('auth failed');
}
On the other hand passport.js provides a built-in function that can be used as Express middleware.
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
Authenticating requests is as simple as calling passport.authenticate() and specifying which strategy to employ. Strategies must be configured prior to using them in a route. Continue reading the chapter on configuration for details.

ExpressJS middleware vs logic

What is the best way to structure the logic in an ExpressJS API.
So lets say I want to create a register and login function, and lets say I want a user to be logged in when he is successfully registered.
Now there are different ways I can achieve this, my question is then essentially: Which method is best and why?
METHOD 1
Everything is middleware:
router.post('/path', someMiddleware, register, login, function(req, res){
//You are registered and logged in.
});
So here both the register and login is treated as middleware, with both of them ending with a call to next(). This also results in the best reusability, because I can call either login and/or register again in any other express routing.
METHOD 2
Register is middleware and calls login (which is now just a standard function):
router.post('/path', someMiddleware, register, function(req, res){
//You are registered and logged in.
});
So here register is still middleware, but just before calling next() it calls login() which in this case is not middleware. The problem here is that if user that is already registered wants to login, should I create a hybrid login function that is also middleware and just call it in that specific post request?
METHOD 3
Logic is not in middleware and gets called in the wrapper:
router.post('/path', someMiddleware, funcWrapper(register, login), function(req, res){
//You are registered and logged in.
});
and then (in pseudo):
funcWrapper(actions...){
foreach actions
action()
}
Here logic and middleware are split, there is a wrapper that loops through all the functions that are passed in as parameter (in this case register and login)
Just a last question:
If I haven't asked enough questions, I have a last one. Is it better practice to end all express routing calls with
..., function(req, res){
//response
});
or with
..., lastMiddleware);
and then in the lastMiddleware there is some response

passport local vs custom authntication

how passport local strategy vs custom code
// passport involved code
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
I can also call my local function which can check the same thing which passport local strategy checks so therefore why to create local strategy
// custom checking function
app.post('/login',
customfunctionhere,
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
Yes, you can write your own local strategy function. However, passport gives you extras like persisting the login (cookies), easily handing success and failure, and more.
Plus, if you are using other strategies like OAuth or google/fb/twitter single sign-on, then using passport for all authentication makes sense.
Note: you do not pass a validation function to passport.authenticate(). Check the passport-local docs for more info: https://github.com/jaredhanson/passport-local

How can I specify what PassportJS does after a callback without using ExpressJS?

The PassportJS website has the following code example, where somebody can specify their own flow of logic after a callback goes through:
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
However, I am not using Express, and am finding it very difficult to replicate the above behavior.
Here is my current code that deals with facebook authentication callbacks:
if(path == '/auth/facebook/callback'){
passport.authenticate('facebook', {failureRedirect: '/failbook', 'successRedirect':'/success'})(req, res, next);
return;
}
I have no idea how to change it so that I can alter the flow of logic post-callback. I'd like to be able to check some variables that I have saved and redirect to different locations accordingly. Any thoughts on how I can go about doing this?
Also - on a related note - if I'm just refreshing the facebook access token (AKA if it has expired and I'm getting a new one), is it possible to not redirect the user anywhere at all, so that their browser doesn't go to a different page at any point in time during the refreshing process (which involves a re-authentication with facebook)?
check this https://groups.google.com/forum/#!topic/passportjs/HTG13e2pb74
its a group which uses passport with node "vanilla"

Resources