Invalid state on reload Auth0 callback url, using auth0-spa-js and Angular 8

Hi

We are using the auth0-spa-js in our Angular 8 project, the authentication procedure follows the start-up guide from Auth0 Quickstart. The login seems to work fine, but we receive an ‘Invalid state’ error from our users, if they for some reason refresh the application, while they are on the application Auth0 callback url. The callback url hold the query params “code” and “state” when the refresh page is initiated.

I’m not really sure if the error is desired and how to handle it or this is an effect of the design of the auth service provided in the Quickstart?

Any help is appreciated - thanks in advance

//Mads

1 Like

Hey @mads1,

Welcome to the Community!

Are you using the quick start without customizing anything? If not, could you show us what you have changed in the auth service?

Can you provide me with the steps to reproduce the issue as well so I can troubleshoot?

Thanks,
Dan

Hi Dan

I made a few changes, compared to the quick start:

  1. Removed the handleAuthCallback() from the auth service constructor. The handleAuthCallback() is placed in the ngOnInit() on the Auth0 callback url, in our case /auth. see below
  constructor(private auth0Service: Auth0SpaService) { }

  ngOnInit() {
    this.auth0Service.handleAuthCallback();
  }
  1. In the handleAuthCallback() we use this.router.navigateByUrl(targetRoute), where navigateByUrl is different from the quick guide

In order to reproduce. The problem seems to occur when you refresh the browser, when you are on the Auth0 callback url from a successful login. It is easier to reproduce If you add a delay on the ngOnInit above, then you have time to press the refresh button before redirected.

At this moment, the error is only reported from Chrome 79.0.3945

Please let me know if any other info is needed.

Just added a screenshot of the stacktrace when the error occur on refresh browser

Thanks in advance

I had that issue before, check that you are calling handleAuthCallback() in one place only. I had it on the callback.component.ts and in my app.component.ts so the state from the app was different from the app one

Did @ElitaTnk’s solution help?

To confirm, this is only happening when your user refreshes during the brief callback, not when the request is completely resolved, correct?

I only call handleAuthCallback() one place in the app, so i can’t invoke the solution proposed by @ElitaTnk, but thanks for the input.

@dan.woda Yes, only when the user refresh on the callback url. A refresh on any other page will not result in an error.

Thanks

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

I can reproduce this in the Angular quickstart. Refreshing just after the login, throws the Invalid State issue.

There’s a similar issue mentioned in Auth0-SPA-JS FAQ but it only affects the Firefox browser. The issue reported in this thread can be reproduced on Chrome.

Not sure how to fix that though.

This issue can be reproduced for different frameworks like React as well. In other words, It isn’t specific to Angular.

In summary, before a login transaction starts auth0-spa-js library creates a random state variable and stores it in a cookie. Then, it makes a redirect to Auth0 server with this state parameter. After the user is authenticated Auth0 returns a code along with the same state parameter. Auth-spa-js library checks if the state returned matches with the state stored in the cookie and if it matches makes a code exchange call to get the issued tokens.

auth0-spa-js deletes the state cookie just before the code exchange request. If the user refreshes the browser before the code exchange completes, same code block in the library runs again but this time due to the state cookie being deleted, library throws a state mismatch error.

The state cookie deletion happens on this line. If transactionManager.remove() function call was moved anywhere after the token exchange API call, it could help to avoid the state mismatch error after the page refresh but this doesn’t help on its’ own. That is because, if the first oauth/token call hits Auth0 server, the code becomes invalid hence the next call with the same code fails with an invalid authorization code error. Unfortunately, as of today, invalidation of the code is mandatory by RFC6749 section 4.1.2 so it isn’t possible to allow code reuse at this time.

code
         REQUIRED.  The authorization code generated by the
         authorization server.  The authorization code MUST expire
         shortly after it is issued to mitigate the risk of leaks.  A
         maximum authorization code lifetime of 10 minutes is
         RECOMMENDED.  The client MUST NOT use the authorization code
         more than once.  If an authorization code is used more than
         once, the authorization server MUST deny the request and SHOULD
         revoke (when possible) all tokens previously issued based on
         that authorization code.  The authorization code is bound to
         the client identifier and redirection URI. 

I would like to offer two workaround solutions to this issue.

1- Once the app detects an invalid state error, in the error handler it may start a new authentication request for the state mismatch errors. This will log the user in without the login page displayed if the seamless SSO feature is enabled on the tenant.

To prevent any infinite loops due to state mismatch error, app may create a new cookie to keep the state of the retry mechanism. This can ensure that the login attempts happen only for a few times.

2- Alternatively, you may implement logging in with a popup window as documented here. With the popup mode, users may have a better idea that the login step didn’t complete so they may not refresh the browser.

4 Likes