Why Some Users Can't Log In: Chasing Stale Tokens Through Staging
TL;DR
- A 404 “cohort not found” from the surveys endpoint traced back to a cohort that genuinely doesn’t exist.
- The client was holding a stale Auth0 token that logout never cleared.
- Failing devices don’t sort by iOS version: 15.7 worked, 16.7.11 hung, 18.5 was fine.
- Auth0 logs show no user ID on failed attempts, so it’s breaking early in the exchange.
- Prime suspects are our old react-native-auth0 (2.17.4) against RN 0.80, and leftover refresh token rotation settings.
Good news first: GitLab’s recruiter emailed about moving to the fourth round for the intermediate front-end engineer role, building a workflow catalog for AI implementations. Genuinely exciting.
Then reality, in the form of a post-deployment bug in the ILiv mobile app.
The symptom
When the app checks for available surveys, it should get a clean 204. Instead some users got a 404 carrying “cohort not found”.
I went into the AWS logs and confirmed the cohort attached to that 404 was linked to my own user ID. MongoDB Compass confirmed the rest: the cohort didn’t exist. Not misconfigured, not orphaned — absent.
Following it into Auth0
That sent me up the auth layers. Auth0 showed exchange failures and, stranger, no devices linked to the problematic user at all.
The shape that emerged: the client is holding a stale Auth0 token. It doesn’t appear in Auth0’s logs because as far as Auth0 is concerned that session is gone, but it’s still camping out on the frontend, and the app keeps presenting it.
Which points at logout. If logout doesn’t clear the local token, the app will happily keep using a defunct one, and every symptom above follows from that.
Confirming it’s the logout path
Without local logs — the failing user ID wasn’t in our dataset — I had to work from Auth0’s monitoring. Streaming logs in real time showed a repeated failed-exchange pattern, and critically, no user ID attached to the failures. That means it’s breaking early, at the initial Auth0 call, before the user is resolved.
The revealing detail: after I revoked tokens manually, Auth0 recorded successful logouts. So logout works on Auth0’s side. It’s our side that isn’t clearing.
What doesn’t explain it
I wanted a clean story about iOS versions, and I don’t have one. An iPhone on 15.7 was unaffected. Another on 16.7.11 hung during login. My own device on 18.5 and an Android on API 31 were both fine. My first theory was that the library misbehaves on older iOS, but Mickey’s phone is older still and works, so age alone isn’t it.
It also isn’t the user account. I changed one affected user’s password and logged in successfully on a different device.
Where suspicion sits now
Two candidates, neither confirmed.
Library version against the new RN architecture. We’re on react-native-auth0 2.17.4 and 4.6 exists. Searching for undecodable-token issues on 2.17.4 surfaced hints that RN 0.80’s handling of authentication callbacks is implicated. That would fit failures that break before a user ID is resolved.
Leftover refresh token rotation. We trialled rotating refresh tokens. If the setting and our current behaviour disagree, Auth0 would reject token requests — and rotation invalidates a refresh token after first use, which would make refresh cycles fail in exactly this confusing way.
I walked the handling flow looking for somewhere it swallows this. Missing credentials return early; exceptions get caught and handled rather than thrown. I wrapped getUserMetadataFromToken in a try-catch for the empty-or-malformed token case. No bug surfaced there, which pushes it back toward refresh.
Actions
The fix on our side is to flush expired tokens on failure so the client can retry cleanly instead of presenting a corpse. That addresses the user-facing symptom regardless of which root cause wins.
I also hard-deleted one inactive Auth0 user — the isperformattee.com account — as a cautious test, one user at a time. It executed cleanly with no errors from Auth0. Worth keeping at least one such user around for testing rather than clearing them all out.
Next session: add logging around every token retrieval failure, because right now the most informative thing I have is an absence of log lines.
ryer.io