Skip to content
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

Infinite redirect loop, "Invalid authorization request state." #89

Closed
michaloslav opened this issue Aug 1, 2019 · 3 comments
Closed
Milestone

Comments

@michaloslav
Copy link

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?

[X] I have checked the Auth0 Community for related posts.

[X] I have checked for related or duplicate Issues and PRs.

[X] I have read the Auth0 general contribution guidelines.

[X] I have read the Auth0 Code of Conduct.

[X] I am reporting this to the correct repository (this issue might be for the base Passport library).

  • Version of passport-auth0 used: 1.1.0
  • Version of Node.js used: 10.15.3
@joshcanhelp
Copy link
Contributor

joshcanhelp commented Aug 1, 2019

@michaloslav - It looks like your callback is not quite correct (likely the fault of the README directions, which I'll correct based on your feedback). Here is what I have on my machine:

router.get('/callback', function (req, res, next) {
  passport.authenticate('auth0', function (err, user, info) {
    
    if (err) { 
      return next(err); 
    }
  
    if (!user) { 
      // You'll need to define this route somewhere as well.
      return res.redirect('/auth/failure?type=callbackFailure&info=' + JSON.stringify(info));
    }

    req.logIn(user, function (err) {
      if (err) { 
        return next(err); 
      }
      const returnTo = req.session.returnTo;
      delete req.session.returnTo;
      res.redirect(returnTo || '/user');
    });
  })(req, res, next);
});

It looks like passport.authenticate doesn't have a callback and req.logIn is not called anywhere.

Login looks ok (and seems to be working if you're getting to the callback route) but, so you have it, here's what I've got on my local:

router.get('/login', (req, res, next) => {
  const authenticator = passport.authenticate('auth0', { scope: 'openid email profile' })
  authenticator(req, res, next)
});

Again, let me know if that helps and I'll update the examples.

@michaloslav
Copy link
Author

@joshcanhelp THANK YOU!!!

Oddly enough I actually had the /callback route set up very similarly before but it still wouldn't work which is why I tried following the docs in this repo instead. Now that I think about it though, I probably didn't have delete req.session.returnTo in there and that might have been the cause of this whole thing.

Anyway, it's working now so again, thank you so much

@voinik
Copy link

voinik commented Jun 8, 2021

For those who have implemented the above answer but are still having issues, the problem might lie in your reverse proxy configuration if that's what you're using. Mine's running on Apache and I've described my solution here: https://stackoverflow.com/a/67891167/8221175

As for why this might happen for those interested:
I followed the guide on the Auth0 website, which looks something like this:

        passport.authenticate('auth0', async (err: any, user: any, info: any) => {
            if (err) {
                return next(err);
            }
            if (!user) {
                return res.redirect('/login');
            }
            // ... etc
        })(req, res, next);    

However, if your reverse proxy or cookie/session settings aren't configured correctly, then your cookies might not persist. If that happens, the data in the state query parameter of the first login call will not be able to be retrieved, as it is stored in the session data for later comparison.

When we arrive in the above code, passport tries to retrieve the state data from the current session. Because sessions don't persist, the current session is different from the session into which the original state data was stored. Therefore, the state data it's trying to find doesn't exist and resolves to undefined. Passport then compares that to the state data in req.query.state in req in the code above. Because those two don't match, passport thinks someone else is hijacking the session, pretending to be you. That's the point of the state data.

Anyway, when that happens, passport cancels the authentication and provides the "Invalid authorization request state." message, and thus causes the user to be false in the above code. As you can see, that will cause a redirect to the /login route, starting the whole process over again, causing an infinite loop.

If this is the problem, it has nothing to do with Auth0, but is rather likely caused by faulty configuration. The link to my SO answer above has examples of correct settings so you can fix this problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants