Gated content not working in Chrome (or Chredge)

I’ll go walk the dog.

1 Like

Sitting here clicking Ctrl F5 and switching between pages in fireox, every now and then it works that fails quickly

To be honest this feels very broken indeed!

Wild thought, does it not like the widget being loaded for every page - yet the cookies all look good.

Note I only see a cookie not the auth header I’m used to.

Hey @slim :wave:

Sorry, that took a while to hash out all the details of. I have answers for you. I also have an understanding of why my site build process and prod testing etc. all went fine but you’ve had a lot of trouble. It all comes down to cookies and having a single identity instance :confused: This is going to be deep and complex, but I’ve tested it all pretty intensely so hopefully it’ll help.

Here’s a couple high level things first.

  • Netlify’s identity widget package works great as is. You definitely don’t need any extra wrappers or layers or anything, just use it as normal and heed the following information (that’s a :heavy_plus_sign:!)
  • I have a solution that fixes the behavior in preview-deploys and prod for you. I also have one for local but whether or not it’s worth it somewhat :man-shrugging::skin-tone-2: to me :laughing:

Okay, so starting off - the reason my site process was very different from yours in terms of my experience is because
a) I didn’t use preview deploys. Since my site exists to test a framework and is already using a stubbed/fake sub-domain, I just built things locally then pushed to prod.
b) because my site is built on Gatsby/React, I implemented most of my auth stuff in React itself, not as _redirects with role-gating. I did add those role-gated _redirects but I didn’t focus on testing them locally. I tried it once, assumed _redirects just don’t work locally, then moved on. My _redirects all work on prod and I was happy with that. Having two auth layers makes life a little harder for me but nonetheless all of the following applies

The answer is cookies… and getting there is… tricky.

The key point across all of this is that any site with Identity enabled gets one GoTrue instance spun up, and it gets bound to https://<your-primary-domain.tld/.netlify/identity - the primary part is really important there. When using Identity, be it through the netlify-identity-widget, my framework, or even just gotrue-js directly, in any environment (including local), you’re hitting that singular endpoint.

It gets tricky because ultimately the request for logging in / getting your JWT comes back with a set-cookie directive, but that directive is coming from your primary domain, and therefore the cookie is bound to your primary domain.

That’s made a bit more complicated given that the things happening in the js layer are totally independent from that cookie. Meaning that you may be on a preview-deploy environment and hit your webpage, log in to your site, and get a JWT / user back in javascript land, but when you logged in the cookie was set for your primary domain (which is not your preview-deploy domain) – meaning that the role-based redirects within the preview-deploy environment won’t work and now your role-based redirects on your primary domain will work (the cookie is present) even though you didn’t really “log in” in your prod environment, and the javascript layer in that environment doesn’t think you’re logged either.

Confusing enough? :sweat_smile:

The overall premise that the cookie and its value is fully disjoint from the javascript layer is… hard. I certainly didn’t realize that through all of my own dev in the last few days. The javascript layer stores the user object down in localstorage so that your javascript-level ‘session’ persists across refreshes and page-reloads etc… Even when you logout the javascript doesn’t deal with the cookie - it just requests back to the GoTrue instance and the response is another set-cookie directive with an expiration of -1 (which I believe is just the usual process for “clear this cookie”)… but the end result is two different workflows that control two different systems - javascript runtime vs. CDN _redirects – all of which can exhibit different behavior depending on which environment you’re running in.

Anyway, I believe one of the end results there is that role-gated _redirects just simply don’t work anywhere except the prod environment. Logging in in a deploy-preview sets the cookie for the prod environment, not the deploy-preview… so I don’t think you’ll ever actually get past the role-gate _redirect.

Going to get into my workaround/solution as a second post so you can mull over the above in the meantime. A word of warning - When going in and manually deleting the cookie during my browser session, Chrome appears to not want to add it again on the next login :thinking: not sure if that’s a Chrome but or something, but I would wager that you probably shouldn’t manually delete cookies - just use it as a viewer :confused: This also warrants a better discussion around using incognito windows more - since they get their own cookie context, each time you hit the site at all you probably want to use a new incog window


Hey actually, before I dig into the solutions/workarounds - are you using as your primary domain? I see it on your repo README as ‘master’

Yes and that is now not working in Firefox. Just occasionally when I keep hitting f5 or switching pages.if the same cookie is getting sent every time the points to backend problems.

It seems cookies add more problems than just injecting an authorisation header. Though that is a something the user has to manage.

That doesn’t quite fit with you theory.

Also I spent ages developing a preview deploy with Firefox with no problems. It wasn’t until someone else tried that preview on non firefox.

Still that’s all very helpful.

I could arrange for no gating on previews for now but that’s no good if you want a staging site and test sites say.

Given what you say my trying on multiple deploys at the same time as I normally do is not going to be very safe. Using incognito made no difference and is not uasable for the final live site.


Sorry - I had to hop on a pairing call with a colleague :slight_smile: I agree with most of your sentiment around the workflow being a bit unfriendly for testing on preview sites and such. That said, I do have a workaround but I would like to understand your domain situation first. What’s the URL listed for your identity endpoint under the Identity tab in your site’s dashboard?

I saw that being used and wondered what it was. From what yo usay it’s the enpoint for manging cookies

Certainly having too interacting systems was not somehting I was thinking about an would explain some of the weirdness whe nswitching browsers.

I must admit that haveing 2 systems at play and one (cookies) is trying to add state to stateless JWTs just seems wrong and asking for problems. It seems much easier to undersand and safer to ju tuse JWTs. But I’m not really a expert on this. I just used auth0 with Azure functions before.

Yep - that URL is the GoTrue server endpoint for your GoTrue instance :+1:t2: which manages all the auth and JWT (including cookies)

I was writing up the workaround but I realized that it won’t work for when you’re running the netlify-identity-widget since the widget automatically determines your Identity instance URL :confused:

I think the best solution for running the netlify-identity-widget and having deploy previews is to just be really, really careful and diligent about logging in/out and using new private tabs any time you want to do anything on the site :thinking:

Just adding an update here because I was wrong on one little bit up there - the cookie isn’t actually being set for the primary domain when you authenticate on local / deploy preview / branch preview. The cookie actually isn’t set at all.

In addition, this flag is also used to indicate when cookies are to be ignored in the response. The default is false . XMLHttpRequest from a different domain cannot set cookie values for their own domain unless withCredentials is set to true before making the request.

Essentially meaning that the login process will send back a Set-Cookie directive attempting to set a cookie for your primary domain but it will be ignored unless you’re actually on your primary domain.

My workaround will solve for this (will post tonight) but my workaround only works for sites where you inject the Identity endpoint as an ENV var. netlify-identity-widget doesn’t support that functionality (yet? I may PR that)


Oh, wow. I realise now this is way more complex than I thought and probably for good reason. I was thinking about it all the wrong way. I’d like to check my understanding with you.

So while we see an ID token in the client this is largely an implementation detail of the auth, and possibly irreleveant for the gating. It’s NOT one of the standard OAuth/OpenID flows I was concidering. Indeed, we are passed a user object to use which happens to include a token which is probably just used to encode info. However, given lack of docs any confusion is understandable.

My guess is the cookie is used for the gating as otherwise the front end would need to send the Auth header with every get. The would require intercepting all URL fetches from the browser (eg links). I’ve never really got into cookies as they sort of go against the grain of stateless HTTP and I’ve not neded sessions, but I guess they play and important role here. The cookie has a jwt with roles in it and the edge can use that gating. Is tha tyour understanding too?

From what say this cookie token is managed completley separately from the ID token used in login? That is no doubt the source of so much confusion. unless the same cookie is used for both? Also cookies have lifetimes separate from cookie “exp”, potential for more cofusion there. Plus the cookie is associate with a specific URL - the main deployment only - even more problems.

On a side issue, as suggested in the docs I started using the id token in the Authorizatin header for endpoints provided by Functions and to take advantage of the access token info in the function. What’s not clear yet till I experiment is if the system treats the id token as an access token and manages access or if we have to call the netlify API and do the checks ourselves. For now I’m not too bothered. The reason I say it’s an id token and NOT an access token is it comes through login and doesn’t have “aud” set. Anyway. moving on as this is a confusing topic …

So one issue I think there might be and I will check it just using main deploy only. That is cookie managment may be causing interfence when logged into via two browsers It does seem that logging into a second browser breaks access gating in the first. That is not good. If a new cookie is released that might cause that behaviour. But then again that breaks such a fundamental basic of web use that I doubt that is the cause. I’ll experiment.

I guess I was unlucky in using a branch and PR for deployment and never merging to main. I assumed everyhting worked the same on a preview as main - it seems that is NOT true for gating. Sigh. I’ll see wha thappens when I stick to main deploy only. But it seems something needs to be resolved son so we can use gating and deployment builds without problems.

I was writing up the workaround but I realized that it won’t work for when you’re running the netlify-identity-widget since the widget automatically determines your Identity instance URL

Thanks anyway! I really appreciate all this input. I’d be very tempted to use your code as I prefer the UX. however I’m not in an SPA and seem to have moved on from React these days :slight_smile: I used it with Auth0 in this project - GitHub - AlwaysInMind/aim-web-app: The AlwaysInMind web app

Hopefully a proper solution will be found for all?

I’m slightly confused about that cookie isse. Surely, as the cookie is used by the edge gating logic if it is not set for preview then preview would not work. But it worked just fine for me . Plus I’m sure I always saw the cookie in branch in FFx F12 tools

At this point I need to moved on and recoup some of the time I didn’t have that I spent on this problem. :stuck_out_tongue:

So I’ll stick to main deploy and delete all the previews Hopefully everthing will then be fine - but I have doubts. Fingers crossed. This is a “proof of concept” project to flush out issues like this and see what we can safely use for a production solution.

I’m really hoping we can use the edge gating as it is an awesome USP or Netlify.

Right I think I have got somewhere. I created a new site and added a single user.

The problem seems to be you can only log into one browser tab / browser at a time. That’s unexpected and should be fixed.

  1. Signup a new user
  2. click link in an email which opens a second tab where logged in and can access /applicant
  3. logout and close tab
  4. switch to original tab and login
  5. /applicant 404s
  6. Ctr+F5 and page loads

Thant irratating but not a deal breakerl

  1. open edge while still logged into firefox.
  2. log in same user
  3. /applicant 404s
  4. ctrl F5 does nothing, even after waiting a bit
  5. log out in firefox
  6. Ctrl F5 and still 404s in edge
  7. logout of edge and log in again - stil 404
  8. at some point I manged to get to /applicant in edge - not now though
    9 Log into Firefox and /applicant 404’s

So in this state of neither working I logged in and out of both until I got the page in edge. Then Firefox did not work

So this is very broken right now. And still rather non deterministic.

As we were trying various browsers using the same user we may or may not have logged out and I expect once we logged in twice it all went wrong again.

I hope this will get fixed ASAP. for now we’ll try loggin OUT before logging in again as same user on another browser. I hope it’s not just logging in as same user in another browser eve after loggin out.

A slight addendum. When in the state of firefox and edge logged in and it working in Edge, edge stopped on a page fresh. I logged out of both, logged into Firefox but still 404. Ctrl F5 the ncaused the page to load.

So in summary

  1. logging into in two browsers usually results in failure in 2nd, sometimes failure in both.
  2. Ctrl+F5 sometimes gets it working - pressumably a new cookie is given.
  3. I expect the other ideterminite states are simlpy do with incorrect cookies and my missing the exact state changes.

So I just adde a new user, gave them role=rdm but cannot access the /rdm route. I tried logging in and out etc. It worked fine in the old site.

To be honest, this just does not work! :frowning:
I think I’ll have to use a Function for each page and gate it with code.

That user started to work after a while. Hopefully just a glitch for that one time.

Here’s a nother failure mode tha tis a bit of an edge case as normally you won’t be in the situation.

  1. go to the gated applicant page when logged out - you get 404 s expected
  2. login (which does not change the page) and still get 404
  3. CTRl+F5 makes no differrence. Navigating home then back to applicant wotks - it might take 2 goes

So that is unusal as you would not normaly log in on a gated page and get access. It’s only as Ive been lazy with my UI and left all option available when logged out (it’s a spike after all)

You and me both :stuck_out_tongue:

Right - and only for the gating. The cookie level is fully separate from the javascript level. The javascript level just kicks off the request that comes back with the cookie. Once the cookie’s in, it no longer matters what the javascript level does; the cookie is in. All things involving role-gating with the _redirects file come from the cookie, not the javascript. We have to think of these two layers as separate and understand how they interact independently (which is tricky)


Yes I agree - the JWT/Javascript layer having a different expiration timer than the cookie is indeed another layer to add to the confusion… BUT I should mention that the netlify-identity-widget sets up the cookie request to have no expiration, and stores the JWT in localstorage so in essence, both have an infinite expiration - at least that matches up. When you log in, both are set to never expire. Since cookies and localstorage both persist, even across a browser restart, they’re effectively set until you log out (logging out removes both the localstorage and the response from the /logout endpoint destroys the cookie)

So luckily, the cookie and JWT timers should always be in sync. At least one thing matches :slight_smile:

Yeah. I think that’s your main issue. Again, my workaround won’t work when using the netlify-identity-widget so I’m not really sure how to fix that for you :frowning: It’s worth clarifying too - the cookie is associated to the primary domain only, but only ever actually even gets set when you’re on the primary domain. Meaning that _redirects will never work locally or on a preview deploy, because the cookie will never be set correctly on those domains (localhost and preview-deploy-x–

Combining Functions & Identity is a topic I wrote a significant amount on in this other thread - combining the two makes for a really powerful experience but it’s worth understanding the workflow. Give that a read :+1:t2: feel free to post in that thread if you have questions pertaining to that topic as well.

Sending the Authorization header to a Function occurs at the Javascript level and doesn’t involve the cookies at all, so that should actually work in any environment since all of the environments correctly produce JWTs for Javascript, it’s the cookie stuff that fails when running not on the primary domain.

I fully agree. Specifically for role-gating _redirects, there are issues running on non-main.

1 Like

When you log in, both are set to never expire. Since cookies and localstorage both persist, even across a browser restart, they’re effectively set until you log out (logging out removes both the localstorage and the response from the /logout endpoint destroys the cookie)

But after an hour I find things break and I have to logout and back again to restore correct operation. I havent explored if the issue is the gating or the JWT but interstingly the JWT has exp = 3600

The widget doesn’t help. I was going to check the JWT exp and handle by using the widget refresh function. but it seems that won;t help as gating is manged by the cookie.

Maybe I can access the cookie from javascript and check the jwt within. I never tried.

To be honest though, Netlify need to handle token refresh invisibly, as they’ve made the gating invisible. There’s a growing list of things they need to fix here :slight_smile:

Once again, I started developing on a PR preview branch and the gating was just fine! IN firefox anyway. I’ll try again at some point, but for now I can live with updating the main deployment.

The title of this evergrowing thread is still OK as apart from the loggin in twice issue problems are worse in chromium based browsers.

How do we get Netlify to pickup the issues I’ve listed and look at them?

Yeah… I won’t lie, understand the JWT workflow alone is complex. It’s not clear to me what happens once it expires anyway. In the javascript everything seems to work fine and still considers the user logged in… so I don’t think it’s “expiration” in the sense that the JWT self-destructs, but perhaps an expiration timer of when GoTrue will no longer validate that JWT as being “live”? I’m not sure and I’m not a huge fan of having to wait an hour to find out :man_facepalming:t2: but that’s what this GitHub issue suggests I think. Sending a stale JWT to a Function (which validates the JWT against GoTrue before even running) is the scenario there… but if a post-expiration-date JWT still means “You’re logged in” as far as Javascript goes and the JWT is stored in Localstorage, that should mean that once you’re logged in, you don’t ever have to re-login… as far as the javascript goes

But that’s where it disconnects. If the JWT is always valid but the cookie dies after a day, you could totally have someone login on day one, then come back to the site on day 2 and be logged in but unable to access certain pages that are role-gated in the _redirects… which feels… bad.

All of that happening at the same time is hurting my head :man_facepalming:t2: