Back to Feed
article 3m read

A token that was never going to change kept the signup check waiting

New accounts got a session token before their organization existed, so my access check's patient retries could never find it. I replaced the waiting with a check that asks the sign-in provider for a fresh token, added a fake database to test the access rules, and found a redirect loop and a few other holes rereading the diff.

Article DepthPractice

Flow

Which side is behind

  1. 1Token names an organization, database does not

    The database is late. A short wait can help.

  2. 2Token has none, database has one

    The token is old. Send the person back through sign-in for a new one.

  3. 3Neither has one, the sign-in service does

    Both are behind. Use the service's answer, and pick carefully if there are several.

  4. 4Nobody has one

    The account is not set up. Show a page that says so instead of redirecting.

Flow

The loop I want to add

  1. 1Token has no organization

    Run the check once before showing anything that belongs to an organization.

  2. 2Organization found

    Send the person back through sign-in and count one try.

  3. 3Token now names it

    Show the dashboard. No more redirects.

  4. 4Still nothing after a few tries

    Stop bouncing. Show a page that explains the state and offers sign-out.

A new account on my dashboard could make the access check sleep for several seconds, ask the sign-in service, look once more, and then show a card that said to refresh. The waiting was on purpose. It was built for a slow database. But the real problem was a token that was never going to change.

Two clocks at sign-up

People sign up through a hosted sign-in service. Two things happen, and I don't control the order. The service gives the browser a signed token. The service also calls my app, and my app writes the user, the organization and the membership into my database.

The old check saw one problem: rows are missing, so wait. It retried, then asked the service, then gave up and showed the card.

The catch is that the token carries the person's organization, and that part is fixed the moment the token is made. A brand-new person gets their token before their organization exists. So the token has no organization, and my server can't add one. The old check only looked up an organization when the token named one. So it never asked, even after my database had the row. Waiting fixes a slow database. It can't fix an old token.

A check that only reads

Now the access check just reads. One lookup each, no sleeping. A second check asks one question: does this token lack an organization that I know the person has?

If the token names no team but one is known for the person, the browser is sent back through sign-in.

If yes, the browser goes back through the sign-in service with that organization attached, and the service should hand back a token that names it. The dashboard's data calls do the same thing when they get turned away. I haven't tested that last hop yet.

A fake database for the tests

I also added a fake database for tests. Each query gets the next answer from a list, so a test reads like a story: user found, organization found, no membership. That made it easy to check that people only see their own organization's data. My commit message calls all this tests and a logging fix, which undersells it.

Holes in the bounce

A few things still bother me:

  • With no organization anywhere, the home page and the refresh route bounce the person back and forth, and nothing counts the trips.
  • The refresh route trusts a return link from the address bar, which could send someone to another site.
  • Someone in several organizations lands in whichever one comes back first.

What I want next is a small counter on that bounce, and a plain page at the end of it that says the account isn't set up and offers a way to sign out. I deleted the old card without putting anything in its place.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward