Summary
The JWT payload is decoded with atob() directly, but JWT segments are base64url (-/_ instead of +//) — whenever a token's payload happens to contain those characters, atob throws and the token is treated as invalid.
Evidence
apps/web/src/auth/useAuth.tsx:23-38:
const [, payloadB64] = token.split('.');
const payload = JSON.parse(atob(payloadB64));
Impact
Intermittent and token-content-dependent: a fresh login fails with "Received an unreadable token", and a previously valid session silently logs out. (The decode also assumes ASCII — non-ASCII names would decode garbled.)
Summary
The JWT payload is decoded with
atob()directly, but JWT segments are base64url (-/_instead of+//) — whenever a token's payload happens to contain those characters,atobthrows and the token is treated as invalid.Evidence
apps/web/src/auth/useAuth.tsx:23-38:Impact
Intermittent and token-content-dependent: a fresh login fails with "Received an unreadable token", and a previously valid session silently logs out. (The decode also assumes ASCII — non-ASCII names would decode garbled.)