401 Unauthorized When App Runs in 2+ Tabs

App details:

  • Pro account
  • We use GoTrue Identity
  • Nothing else special

When we try to load our app more than once on the same computer, the newly loaded app receives a 401. These are all the circumstances:

  • 2+ tabs on same browser window
    • eg Chrome 2+ tabs
  • 2+ separate same type browser windows
    • eg Chrome 2+ windows 1 tab each
  • 2+ browsers of different types
    • eg Chrome and Firefox

I can’t find related discussions on this forum

My only clues are:

  • same session cookie
  • Chrome SameSite cookies (may be same as previous)

I thought this must be common for Netlify apps and would be previously discussed.

Where is this happening? On your site, or the Netlify UI, or somewhere else?

I think this is related to how you are using GoTrue in your app, it seems to me like you’re sending the wrong identity token in the second call. I would debug that and check the remember me functionality with setCookie parameter

@hrishikesh It happens on our site after authentication

@sidati Would you please explain what you mean by “wrong identity token in the second call”?

  • What second call are you referring to?
  • Which identity token should be used?

All:

First to clarify:

In the following cases, all errors/failures occur well after log in. It happens like this:

  • Navigate to an area where specific documents must be loaded and we see the failure on the second browser
  • Inside the failed browser, refresh twice (???) and that browser takes the token away from the opposite browser
  • Return to the previously working browser and it now fails as did the opposite
  • This can be repeated any number of times with the same alternating success-failure results

The following two lists show the current status of the problem. This is a working scenario:

  • Same computer same browser (Chrome) with 2 tabs works fine. This is just, for example, duplicating the first tab to a second. Changes in one tab are properly reflected in the second

These are two failing scenarios:

  • Same computer different browsers (Chrome and Firefox) still fails as described
  • Difference computer, any browser, sign in as the same user fails

SameSite Update

  • Using Chrome developer tools I hadn’t noticed before that the SameSite cookie attribute value was not set so defaulted to Lax (Chrome seems not to complain/warn). Only Firefox reports this in its console.
Some cookies are misusing the recommended "SameSite" attribute 4

> Cookie "userid" does not have a proper "SameSite" attribute value. Soon, cookies without the "SameSite" attribute or with an invalid value will be treated as "Lax". This means that the cookie will no longer be sent in third-party contexts. If your application depends on this cookie being available in such contexts, please add the "SameSite=None" attribute to it. To know more about the "SameSite" attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite login.json

> Cookie "token" does not have a proper "SameSite" attribute value. Soon, cookies without the "SameSite" attribute or with an invalid value will be treated as "Lax". This means that the cookie will no longer be sent in third-party contexts. If your application depends on this cookie being available in such contexts, please add the "SameSite=None" attribute to it. To know more about the "SameSite" attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite login.json
  • I have no idea what “recommended “SameSite” attribute 4” means, but everything I read indicates that any cross-site content will work only with SameSite=None and Secure (maybe 4 and None are the same)
    • I have now set to the recommended value None and included Secure
    • Further I noticed that Cloudflare sets two cookies and both have SameSite=None and Secure
    • I don’t know whether Cloudflare cookies were previously allowed, but it looks like they are set before our app’s cookies. They are probably set around our app.
    • Still, the cookie attributes of SameSite=None and Secure have not made a difference (still broken)

In the end changing SameSite to the recommeded value and setting Secure seems to have done nothing to help.

Hi @sidati Please pardon my repeated question. It would help a lot to get an answer for this:

Would you please explain what you mean by “wrong identity token in the second call”?

  • What second call are you referring to?
  • Which identity token should be used?

Thanks for your kind consideration and help.

Some question will help me understand :

  1. Are you using a service worker ?
  2. Is your app and identity api share the same hostname ?
  3. If you log in on chrome first and on firefox second can you navigate normally on firefox and the issue only star when you start using chrome ?
  1. Are you using a service worker ?

We introduced a web worker for some async persistence but it is not yet on the production app. And this issue predates the web worker by several months. (We’ve simply avoided this rabbit hole until now in favor of features and stabilizing overall.)

  1. Is your app and identity api share the same hostname ?

We use Netlify Identity with GoTrue, so I assume it is the same hostname.

  1. If you log in on chrome first and on firefox second can you navigate normally on firefox and the issue only star when you start using chrome ?

Yes, exactly. Then I return to Chrome and (wait for it) refresh twice and Chrome is good and Firefox is broken. I can continue that pattern many times and it continues consistently.

Are you using the audience option when creating a GoTrue instance ?

No, we do not use audience. This is an example of our usage:

  const auth = new GoTrue({
    APIUrl: appURL() + '.netlify/identity',
    audience: '',
    setCookie: false,
  });

OK then, cause I found bug with aud property but you are not using it so your app won’t be affected, and since you sat setCookie: false I don’t think it’s a cookie issue, the only thing I can recommend without looking to your app source code is to deploy your app to a new Netlify site and see if the issue still present.

P.S. be aware that without the setCookie: true, the token is not saved in localStorage so if you don’t have a logic in your app to save the JWT and send it as Authorization header with each request, the user must log-in every time he refreshes the page.

Thanks, @sidati.

We set the gotrue.user ourselves after successful authentication for both user/pw and OAuth:

localStorage.setItem('gotrue.user', JSON.stringify(response));

It’s used as needed in the app:

  const localUser: User = JSON.parse(browser
      ? localStorage.getItem('gotrue.user')
      : null
  );

We also remove that key upon logout:

localStorage.removeItem('gotrue.user');
  • Question is, would not using setCookie: true be a reason for experiencing the 401 with multiple browsers?

Additional:

  • Also one additional point might help. If we use two browser windows of Chrome+Chrome under the same Chrome user profile, everything works correctly. If the two different browsers are on two different Chrome user profiles, we experience the 401.

About your question, it would not cause 401 if you handle it properly :slight_smile:, Do you have a backend logic handling JWT ? On what pages the 401 is being returned ?

Haha :blush: Yeah, but how is the whole point.

In the meantime we’ve found that it seems to (possibly) be more related to failing database access but with some influence from the user token. We’re not sure yet. Let’s put this on pause until we learn more. Will reply when we understand better.

1 Like

Hey @VaughnVernon, just to confirm,

Are you waiting to learn more yourself or from us?

I think this is expected (based on the fact that this has happened to me previously). Netlify Identity depends on a refresh token value provided once a new token is obtained. The previous token is then invalidated. So when you login using another device, you get a new refresh token, so the one in the previous device is no longer respected.

Regarding the other issue:

I might have some theories, but to confirm, do you think we can get a test account on your site?

Thanks @hrishikesh and @sidati Yes we have learned more ourselves, as follows.

This seems to identify at least part of the problem:

  • Same computer, two browser types: Chrome and Firefox
    • Authentication of the same user on two different browser types generates two different gotrue.user token values. The tokens are identical up to the final 43 characters following the trailing . forward. Here are the postfix values for Chrome followed by Firefox:
Chrome:
.qwv1p_uKioO0E1KkdoQBqMow6jLx8avMhYJ9qHimR7Q

Firefox:
.aHpDhwypl2p4O-2Bya15M3IyaNtvKiTaWlQjYBhM1Xg
  • On different computers the tokens seem to be different in both the longer initial segment and the trailing segment. The initial segment is different only in a few places.

  • This is the case when signing on as:

    • the same user on the same computer on different browsers
    • the same user on the different computers regardless of two browsers (same or different)
  • We are using the token to ensure that we are dealing with the correct user before we permit use of the database. So our 401 failure occurs before we execute a query on the database.

Necessary Use Cases

After having considered all of this further, it is likely or even certain that we shouldn’t be concerned with this. Our actual practical use cases are:

  • The same user should be able to:
    • Open multiple tabs on the same browser instance
    • Open one or more additional same-type browser instances with any number of tabs

If we were to support multiple different browser types on the same machine:

  • It seems more likely that we should do so for test only. It’s really not our primary use case.
    • This in fact was the reason that we noticed this “problem” a number of months ago
    • Our concern should be more focused on multi-user collaboration (WebRTC) than testing with the same user across different browsers.
  • If we were to support mutiple browser types on the same computer, we’d probably still force a sign off on the initial browser in order to give the new browser type the capabilities.
  • If we were to support same user sign in on multiple computers it could/would likely be used to defeat our subscription model (currently not live) be enabling multiple users to sign in as the same user and use different features (or possibly even the same) simultaneously

And… since the above core use cases are now working (which were not some months ago, we will probably call it quits on this.

  • It would be nice, however, to understand the generated tokens of same user, one computer, different browser types
  • Or, would it be unsafe to rely on the token format and contents even for test purposes?

Your Test Account

Also, our registration is currently 100% free. So you can create an account of your own for analysis and “debugging.” However, all of our source is obfuscated so source viewing and break points are unavailable.

Thanks for the detailed posts. Our current Identity implementation is indeed not meant to be used on:

  1. Same device, different browser
  2. Different devices

As you’ve noticed, you get a different token which invalidates the previous one.

As far as same browser, different tab is concerned, what I’m assuming right now is the following:

  1. The token expires after its default validity period of 1 hour.
  2. You open the new tab which notices that the token is outdated and tries to refresh it.
  3. The current tab still tries to use the token that’s saved in the browser’s session, and since that’s now invalid, it fails.

This is just a theory and there’s a 51% chance, this might not be happening. I’ll try to check with some test credentials later this week.

Thanks, @hrishikesh and @sidati. We now know how to deal with this. It’s been a very helpful discussion.

1 Like

In fact I did some testing and it seems every login action create new JWT without invalidate any other JWTs of the same user. so unless the user logout or the JWT expires, the JWT will always be present and valid :blush:

https://gotruejs-playground.netlify.app/

Thanks for the extra tests, @sidati. Your help and @hrishikesh’s has assisted us in deciding to handle same-user on multiple browser instances differently, while still using Netlify’s Identity. It clearly was not meant to be used as we were attempting, and a different approach is absolutely necessary and warrented. Thanks again.

1 Like