Gated content not working in Chrome (or Chredge)

It shouldn’t. Redirects with role-gates are the same as plain redirects, as far as the CDN is concerned. They’re all pushed out at the same time AFAIK.

To your other points, the roles of a user is actually contained inside the JWT so when they log in and GoTrue provides them a token, the roles that user currently has assigned to them in GoTrue will be embedded within that token. That’s why GoTrue has to run identity-signup synchronously before confirming a user - so it can get those roles back and embed them into the JWT it sends back to the user.

FYI, I believe every time you log in with username/password you are generated a new JWT.


All of the stuff I’ve been doing the last few days has been user-specific for IDs and cross-lookups but I’ll add a page or two for role-gating and see how it goes locally/live. If I see anything I don’t expect, I’ll report back. My identity-signup just assigns member to everyone as a default but I’ll play with it :+1:t2:

Good stuff, let’s keep this going :100:

1 Like

the roles that user currently has assigned to them in GoTrue will be embedded within that token.

Indeed and I access it to present to the user :slight_smile: Yes of course, that makes sense about synchronous function call. Even though I use the async form internally :slight_smile: Fortunately my user info fetch from google sheets is never tooooo slow.

So now I have deployed master everything seems good in all browsers with that URI. Just waiting to hear from other user. I just opened a new PR for previous dev branch so will see how the preview goes now.

Fingers crossed.

Right so everyhting has been fine in Firefox, edge and Chrome on the main deployment.

I installed Opera and tried and it failed but when I tried later it worked.

At this point I give up.

My branch login function was erroring as you said so I fixed. It’s redeployed without errors but when Iogin in i get the error “hook not called” but the function log shows no activity at all.

ah “7:08:23 PM: [ERROR] Function logs are currently unavailable. We are working on resolving the issue.”

Defintetly time for a rest!

Hey @slim - sorry took me a while to get through everything I wanted to. Good news and bad news, I’m afraid.

The good news is that I’m dead certain all of the Netlify Identity ‘stuff’ is working right. I just finished building out a framework for integrating Netlify Identity into react and it’s all worked fantastically. Building the framework was challenging :sweat_smile: but I just finished testing the heck out of it and it’s all working excellently.

I should note again, I did all of this between local dev and pushing live to the prod site (master) – not using Deploy Previews etc…

The bad news is that that probably doesn’t help you much :stuck_out_tongue: So I do apologize for that, but I think it could at least provide a test-bed to see if you’re having client-side issues!


If you’re up for it, I’d like to have you try a few things on my site and see how it works for you. If it all works well across the various browser you use, then that’s at least progress in knowing where we should dig in for your issues.

The site is sun.sargesites.com - not very pretty, but hey when you’re building an Auth framework… :rofl:

Here’s the premise of the site right now (and I’m logging off for a while so it shouldn’t change any time soon)

Public pages - always visible to everyone (though content can change depending on auth status)

  • / (root / index)
  • /sign-up
  • /login
  • /forgot-password

So beyond that, I have two roles: members and admins. Everyone that signs up or gets invited is automatically granted member status. As a member you should be able to see:

  • /members
  • /my-account

If you aren’t logged in, those pages would redirect you to the /login page. Finally, I also have admins which have access to all of the member pages and:

  • /admin

So given all of that, feel free to sign up for a new user and see how the member tier works, then also feel free to log out and log in with the following credentials for the admin role

  • email slim_admin@jon.fm
  • password Password!

And you should (hopefully!) find everything running smoothly. The site does run a good deal of front end code, so you’ll want to fully refresh each page on each path to make sure you’re hitting the _redirects role-gates and not just the Javascript role-gates I wrote.

If you play around with all of that and don’t find any issues across any browsers, I think that’s a good sign and means that we can dig in on the specifics of the code you’re using and how it’s integrated.

Let me know what you think :nerd_face:


Jon

Great to know it works for someone! :-/

let me know what you think

Well OK, I’ll do your testing for free :stuck_out_tongue:

Seriously though thanks - that will help as I’ve drawn a complete blank. Not I find sometimes browse refresh solves the problem for me (only sometimes and my guess that when the JWT is stale).

Here’s what my collegue found using the main deployment URI

" It looks like the new URL is a goer. If I login with from Chrome, Edge, Opera & Firefox from my PC I can access the Applicant pages with no failures."

\0/

But then:

" tried my android phone with chrome and it worked OK

Switched to the Ipad with chrome and it failed so I tried the PC Chrome again and it’s back to the old problem of broken link??? BUT works with firefox. Sorry but its back to the old problem"

ARRRRGGGGG. I so wish this wasn’t Pro Bono work! LOL

So either it’s somehting I’m doing, my Netlify site in a weird state, a netlify bug (like caching something or assuming never switching browsers), or the fact I’m in the UK (OK so that’s desparate straw clutching)

I’ll have a go later today. Right now I’m chasing a FireFox template element DOM bug :frowning:

Ok so your site seems fine ( I should do more testing tough). Shouldn’t that be “Oracle” not “Sun” (showing my age). :stuck_out_tongue:

That also appears to reinforce my current theory - that the bug is in the netlify indentity widget.

I see you do not use that and I much prefer your UX. I only used the offical component as I assumed it would just work. I might rue that decision! Are you just using the core Go component thingie?

So If I can recall all those different OAuth/openID flows - the basic OpenID flow being used here is the Authorisation: bearer <JWT access / usertoken> header being sent on each request from the browsers. And I assume the Netlify edge gating checks the roles in app_metadata and acts appropriately.

So if I’m seeing itermittent problems that vary across browsers it HAS to be client side and so has to be the netlify indentity widget serving up a stale token at times. As we don’t have any loggin for the gated access I’ll fork the widget and hack in some logging of the Token being used.

As an aside, I would have expected the widget to have been thoroughly tested across browsers and using all Netlify authroised services. I did crash it once and noticed it uses MobX so the state machine has a good foundation at least, but bugs are bugs!

speak later

Haha :rofl: debugging is an interesting lifestyle

No problem! We’ll get it sorted out, I’m sure.

:thinking: such random / sporadic behavior. So strange…

I’m not using the netlify-identity-widget, no. Long story short, yes I am interfacing more directly with the gotrue-js library. That said, the netlify-identity-widget should be bug-less but implementing it and using it correctly can absolutely be tricky. They designed it to be super universal and accessible which == window mounted directly and that comes with pros and cons. Since I build in React, I wanted a more react-y wrapper. That’s neither here nor there - your site isn’t using React :laughing: I think our next steps will be really digging into how you implemented the widget and the actual code for it on any of your given pages.

Yes exactly; the netlify-identity-widget stores your user JWT in a cookie that’s sent with each subsequent request and since those JWTs are stand-alone, self-signed, stateless auth tokens, the Edge CDN can just check the role therein. Just to clarify though (and, not wanting to call out mistakes - just want to make sure if you ever implement it you won’t have trouble!!) the header is Authorization: Bearer xyzxyzxyzxyz – spelling & caps :sweat_smile:

I agree that if you’re having intermittent issues, it’s probably with the widget package. That said, I believe strongly that the widget package is built correctly (I used its source as a strong reference for my framework) so I would bet on it being an implementation thing since it can be tough to implement correctly!

That said, I don’t recommend forking and digging into the widget :laughing: it’s pretty headache inducing stuff :stuck_out_tongue: and I don’t think you need to. Also the current JWT is just stored in a cookie so you can open that up in dev tools then use https://www.jsonwebtoken.io/ to decode that token (don’t ever use that with prod / live / ‘hot’ website tokens though; JWT strings are fully valid credentials)

At this point I think we should dig into your implementation of the netlify-identity-widget. In trying to keep all things public and available for future Netlify Community users, I want to keep the discussion here – but is there any chance you could make the repository public so I can scan through it? You could take it back to private after we get the auth stuff figured out if you’d like (is there anything that’s exclusively private already in the repo?). Otherwise you may have to copy+paste a bunch of code here :stuck_out_tongue:


Jon

1 Like

Yup - I got bitten by that yeas ago and you don’t foget that pesky space afterwards

That said, I don’t recommend forking and digging into the widget :laughing: it’s pretty headache inducing stuff :stuck_out_tongue: and I don’t think you need to. Also the current JWT is just stored in a cookie so you can open that up in dev tools then use https://www.jsonwebtoken.io/ to decode that token (don’t ever use that with prod / live / ‘hot’ website tokens though; JWT strings are fully valid credentials

I came to tha conclusion too

I tried using the currentUser() method but that always returns null unless you are in a a on() handler, when you get the user anyway. D’oh. I rasied an issue on that. It seems to just delgate to the gotrue coponent so I’m not sure what’s going on.

But yes, the user and token are stored in localStorage too (dunno if also in cookie) and I’ve been looking. and pasting JWT in to JWT.io. Just using _.cloneDeep to double check is getting stored.

Trouble is, I CANNOT get it to fail now.!!!

Have asked other person if any chance he used old preview URI when it started going wrong again.

Once I have had a definite problem I will share some code! Might to sooner but wouldn;t want you to waste more time if the problem has vanished by deploying master

I don’t want to leave you hanging - I found some really interesting stuff regarding the _redirects engine and running it with deploy previews not on your primary domain (including when the redirects engine runs locally) – I’m digging pretty deep into it but will report back soon. Tl;dr: you may have everything wired up correctly (though I would still like to see the code) and this may be a platform limitation. TBD.

Oooh - would explain why OK now on master deploy. But colleque swares he has problem on master URI

I noticed a few iffy timing issues dure to mixin global scripts with and without defer. Have cleaned up that now so all timing is explicit and added a page to print the user obj on init, login and in local storage. Also added a button to refresh token for fun. That might help.

Just htought, a page refresh to display it might hide any issues with tokens - better keep in memory for each page I guess. Oh well need a SPA now :slight_smile:

Did you find some secret docs? :slight_smile:

interesting factoid user.token.expires_at has been converted to millseconds ready for Date(). ie is token exp: * 1000.

OK for the first time I had somehting definite. it points to delays in the edge gating getting updated on deploy.

I merged a PR across to master and browsed to it in Edge. I logged in as an applicant and got the unwanted 404 for /applicant.

I used the token page and everyhting looks good. I also verifed the token

Fri Aug 21 2020 19:18:40 GMT+0100 (British Summer Time)

{
  "url": "/.netlify/identity",
  "token": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTgwMzM5MjAsInN1YiI6IjQxODY3ODMwLWMzNWQtNGFjOC05ZTA4LTU1ZGVjM2VlZTEyYSIsImVtYWlsIjoic3RldmVsK2FwcEB0d2FtLnVrIiwiYXBwX21ldGFkYXRhIjp7InJvbGVzIjpbImFwcGxpY2FudCJdfSwidXNlcl9tZXRhZGF0YSI6eyJmdWxsX25hbWUiOiJzdGV2ZWwrYXBwIn19.5a-Klx6ga5QB3n_1Z_Z4RY3KFwCa0L8SVs7NjWOhBW4",
    "token_type": "bearer",
    "expires_in": 3600,
    "refresh_token": "xjx7xV0QxyvQytuxA-hR-w",
    "expires_at": 1598033920000
  },
  "id": "41867830-c35d-4ac8-9e08-55dec3eee12a",
  "aud": "",
  "role": "",
  "email": "stevel+app@twam.uk",
  "confirmed_at": "2020-08-07T10:19:59Z",
  "confirmation_sent_at": "2020-08-07T10:19:51Z",
  "app_metadata": {
    "roles": [
      "applicant"
    ]
  },
  "user_metadata": {
    "full_name": "stevel+app"
  },
  "created_at": "2020-08-07T10:19:51Z",
  "updated_at": "2020-08-07T10:19:51Z"

A bit later I changed page back form /user-info to /applicant and got the page.

What I didn’t do is see if it worked first in firefox.

Will try a few more steps

Finally!!! Somehting reproducable

  1. https://master--twam-gallant-swirles-d68516.netlify.app
  2. login as applicant
  3. https://master--twam-gallant-swirles-d68516.netlify.app/applicant
  4. get 404
  5. Check user object - all good
  6. Check cookie - good
{
  "exp": 1598034586,
  "sub": "41867830-c35d-4ac8-9e08-55dec3eee12a",
  "email": "stevel+app@twam.uk",
  "app_metadata": {
    "roles": [
      "applicant"
    ]
  },
  "user_metadata": {
    "full_name": "stevel+app"
  }
}
  1. try page and refresh etc - get 404 all the time. A couple of time it worked after a delay - right now it is not

Ok so I’ll make my repo public now

OK GitHub - OpenDirective/TWAM-App: The TWAM App

Here’s the final rendered static page for the home page


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>TWAM App Spike</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap" >
    <link rel="stylesheet" href="/css/global.css" />
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    <link rel="manifest" href="/site.webmanifest">
    <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
    <script src="/js/netlify-identity.js"></script>
    <script>initNetlifyIdentity()</script>

  </head>
  <body>
    <style>
  .twam-app-header {
    display: grid;
    grid: 1fr / 4fr 1fr 1fr;
    grid-gap: 1rem;
    justify-content: space-between;
    background: var(--twam-blue);
    font-family: var(--font-family);
    position: relative;
    color: var(--white);
    width: 100%;
  }

  .twam-app-header .logo-wrapper {
    align-items: center;
    display: flex;
    height: 56px;
    justify-content: flex-start;
    padding: 0 1rem;
    position: relative;
  }

  .twam-app-header .logo {
    display: block;
    height: auto;
    position: relative;
    transform: rotate(-3deg);
    transition: transform 250ms ease-out;
    width: 75px;
    z-index: 15;
    border: white 1px solid;
  }

  .twam-app-header .logo:active,
  .twam-app-header .logo:focus,
  .twam-app-header .logo:hover {
    transform: rotate(0deg);
  }

  .twam-app-header .logo:active::after,
  .twam-app-header .logo:focus::after,
  .twam-app-header .logo:hover::after {
    opacity: 1;
  }

  .twam-app-header .logo img {
    display: block;
    width: 100%;
  }

  .twam-app-header .title {
    color: var(--white);
    font-size: 1.5rem;
    font-weight: 700;
    margin-left: 0.75rem;
    position: relative;
  }

  .twam-app-header #name,
  .twam-app-header button {
    color: var(--white);
    display: block;
    font-family: var(--font-family);
    font-size: 1.4rem;
    font-weight: 700;
    padding: 0.4rem;
    margin: 0.5rem;
    text-align: center;
  }

  .twam-app-header #name .role {
    font-size: small;
  }

  .twam-app-header button {
    background: var(--twam-red);
    border: var(--white) solid 2px;
    border-radius: 40px;
  }

  .twam-app-header button.hidden,
  .twam-app-header #name.hidden {
    display: none;
  }

  .menu {
    margin-left: 2rem;
  }
  .menu a {
    font-size: 1.4rem;
    font-weight: 700;
    margin-left: 1rem;
    margin-right: 1rem;
  }
  .menu a:link {
    color: white;
  }
  .menu a:visited {
    color: var(--blue);
  }
</style>

<header class="twam-app-header">
  <div class="logo-wrapper">
    <a href="/" class="logo"
      ><img src="/images/TWAM.png" alt="TWAM App Logo"
    /></a>
    <span class="title">TWAM App: Proof of concept:</span>
    <span class="menu"
      ><a href="/all">Public</a> | <a href="/applicant">Applicant</a> |
      <a href="/rdm">RDM</a></span
    >
  </div>

  <button id="signup" class="hidden">Sign Up</button>
  <button id="login" class="hidden">Log In</button>
  <button id="logout" class="hidden">Log Out</button>
  <a id="name" class="hidden" href="/user-info"> </a>
</header>

<script defer>
  function renderUser() {
    const signupButton = document.getElementById('signup')
    const loginButton = document.getElementById('login')
    const logoutButton = document.getElementById('logout')
    signupButton.addEventListener('click', () => netlifyIdentity.open('signup'))
    loginButton.addEventListener('click', () => netlifyIdentity.open('login'))
    logoutButton.addEventListener('click', () => netlifyIdentity.logout())

    function getRoles(user) {
      const roles = user.app_metadata.roles
      return roles && roles.length != 0 ? roles : ['']
    }

    function getRolesText(user) {
      const roles = getRoles(user)
      return roles[0] != '' ? roles.join(', ') : '[none]'
    }

    function getRolePage(user) {
      const role = getRoles(user)[0]
      return { '': '/', applicant: '/applicant', rdm: '/rdm' }[role]
    }

    function setImmediate(fn) {
      setTimeout(fn, 0) // make sure happens on next scheduler loop
    }

    function gotoPage(page) {
      if (page != window.location.pathname) {
        setImmediate(() => {
          window.location.href = page
        })
      }
    }

    function updateUserInfoUI(user, event) {
      const nameDiv = document.getElementById('name')
      if (user) {
        const { full_name } = user.user_metadata

        signupButton.classList.add('hidden')
        loginButton.classList.add('hidden')
        logoutButton.classList.remove('hidden')
        nameDiv.innerHTML = `${full_name}` //TODO handle long names
        nameDiv.classList.remove('hidden')
      } else {
        signupButton.classList.remove('hidden')
        loginButton.classList.remove('hidden')
        logoutButton.classList.add('hidden')
        nameDiv.innerText = ''
        nameDiv.classList.add('hidden')
      }
    }

    function updateHomePageMsg(user) {
      const msg = document.querySelector('#message')
      if (!msg) {
        return
      }

      const role = user && getRoles(user)[0]
      const roleMsg =
        role == 'applicant'
          ? '<br/><br/>You can <a href="/applicant">apply for a TWAM toolkit.</a>'
          : role == 'rdm'
          ? '<br/><br/>You can <a href="/rdm">view your country\'s applications.</a>'
          : ''

      msg.innerHTML = user
        ? `<h1>Welcome ${
            user.user_metadata.full_name
          }.<br/><br/>You have the role(s) "${getRolesText(
            user,
          )}".${roleMsg}</h1>`
        : '<h1>Please log into your account.<br /><br />Sign Up first if you need an account.</h1>'
    }

    const handleUserState = ({ user, state }) => {
      if (event == 'logout') {
        gotoPage('/')
      } else {
        updateUserInfoUI(user, state)
        if (window.location.pathname == '/') {
          updateHomePageMsg(user)
        }
      }
    }

    handleUserStateEvent(handleUserState)
  }

  renderUser()
</script>


    <main>
      <style>
  main {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .centred {
    background: var(--twam-grey);
    border-radius: 1rem;
    padding: 2rem;
  }

  .centred h1 {
    color: var(--white);
  }

  #message a:link {
    color: var(--white);
  }
  #message a:visited {
    color: var(--blue);
  }
</style>

<div class="centred">
  <div id="message"></div>
</div>

<script defer></script>

    </main>

    <style>
  .twam-app-footer {
    background: var(--twam-red);
    color: var(--white);
    font-family: var(--font-family);
    font-size: small;
    padding: 1rem 5vw;
    position: relative;
    text-align: center;
  }

  .twam-app-footer a {
    color: var(--white);
  }

  @media (min-width: 770px) {
    .twam-app-footer {
      padding-bottom: 0.2rem;
      padding-top: 0.2rem;
    }

    .twam-app-footer a {
      display: inline-block;
    }
  }
</style>

<footer class="twam-app-footer">
  Empowering people with the tools that create livelihoods and transform lives -
  <a href="https://www.twam.uk/">TWAM website</a>
</footer>

  </body>
</html>

So it also appears that if I visit the page in Firefox (which still ALWAYS works) I can then visit it in Edge.

All very weird

Could it by my ISP? Its BT so should be OK but I hit problems with them incorrectly killing my work website through their parental controls. Then however I got an obscur eprotocol error , not a straight 404

Now it’s stopped working in Firefox!!! Everything looks the same. I did not redeploy anything.

I really do not know what else to do at this point.

And a CRL+F5 and it’s fine in FFx again. But still nothing in Edge.

Utterly maddening

Typing up a very large response right now :slight_smile: