Our API gateway authenticates every request with a short-lived JWT access token. Until now, an expired token meant an immediate 401 — even when the user still held a valid refresh token. This document describes the silent-refresh flow we just shipped and how we're rolling it out.
Token validation ran before any refresh logic, so the middleware rejected expired tokens outright:
validateToken() throws TokenExpiredError401 — refreshToken() is never reachedThe result was users getting logged out whenever an access token lapsed mid-session.
The middleware now catches TokenExpiredError specifically and attempts a silent refresh before rejecting. On success it reissues an access token and continues the request; on failure it falls back to 401.
401The refresh path is covered end to end:
| Scenario | Expected |
|---|---|
| Valid token passes through | 200 |
| Expired token, valid refresh | 200 + new access token |
| Expired token, invalid refresh | 401 |
| Malformed token | 401 |
silent_refresh flag at 5% of trafficauth.refresh.success and auth.refresh.failure counters