Back to Feed
article 3m read

Auth middleware would have logged everyone out the minute the auth server hiccupped

The SDK middleware turned every failed token check into a redirect to sign-in, so an outage on my side would have looked like a mass logout. I split the answer into yes, no and couldn't-check, and made the middleware answer an outage with a 503. In the same commit the browser client went the other way and now throws away good tokens when the server is unreachable.

Article DepthPractice

Flow

How the page guard decides

  1. 1Public page?

    Exact path or a real sub-path of a public page skips the check.

  2. 2Token there?

    No token means a redirect to sign-in that remembers where you were going.

  3. 3Ask the auth server

    One request, one reply shape, and the reply must say valid.

  4. 4Valid

    The page loads.

  5. 5Clear no

    Redirect to sign-in.

  6. 6Couldn't check

    Send a 503 and leave the cookie alone.

The auth middleware was set up so that if the auth server went down for a minute, everyone signed in would get kicked to the sign-in page. Their sessions were still good. The page just couldn't tell.

I'm building an SDK for my own auth service. Its Next.js part has middleware that guards pages and helpers that tell the server code who the user is. My first change that day made both of them ask the auth server whether a token is real, instead of just checking that a cookie exists. Good change. The helper that did the asking had a problem.

Two kinds of "no"

The helper returned a user or nothing. It returned nothing for a fake token, an expired token, a server error, a dropped connection, or a garbled reply. The first two are about the user. The last three are about my servers. The middleware treated every nothing as "go sign in".

So an outage would look like a mass logout. People would land on a sign-in page that needs the same dead server. Probably they'd retry, add load, and blame their password.

Yes, no, and "I couldn't check"

The next commit gave the helper three answers. If the server says the token is valid, you get a user. If the server clearly says no, you get nothing. If the server errors, can't be reached, or sends junk, the helper throws a special "unavailable" error.

The middleware catches that error and sends back a 503, which means "try again soon". It doesn't redirect and doesn't touch the cookie. The server helpers pass the error up instead of saying nobody is signed in. The middleware needs to handle the new error.

The middleware catches the unavailable error and answers with a 503, and only a real nobody gets redirected to sign in.

A 401 would be wrong here, because a lot of client code drops its token on a 401.

A lookalike path and plain HTTP

Public pages were matched by prefix, so any path that started like the sign-in page skipped the check. Now it has to match exactly or be a real sub-path. The server address must also be https unless it's localhost.

A faked server and a wrong route

The tests use a faked server. They don't cover a server that hangs, and a not-found still counts as a firm no, so a wrong address in config would look like every user got rejected. None of this has seen a real outage.

The browser client is the bigger problem. In the same commit, its restore step started deleting the stored token on any failure, outages included. So the half of the kit that runs in the browser now treats an outage as a logout, the mistake I just fixed in the server half, and it goes further by throwing the token away.

Keep reading

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

5 paths forward
Related idea

New users logged in to nothing because sign-up waited so long to pick a workspace

Workspace creation waited for a later webhook so invited people wouldn't get a spare one, and self-serve people could log in before it fired. Now the creation handler asks the identity provider for memberships on the spot, a later event repairs misses, and the session lookup retries, but the comment that justifies it contradicts the one it replaced and nothing tests which is right.

via #identity · #reliability
Related idea

Paying customers matched by email, even the addresses the billing code made up itself

The lookup that decided whether an organization already had a payment customer searched by an email my code sometimes guessed or generated, and could hand one org's billing record to another. I moved identity onto the identity provider's org ID and tied retries to it, but the same commit left an ignored retry flag, a delete check that reads error wording, a gutted seat counter, and no tests.

via #identity · #reliability