Public Access
The login token moves out of localStorage into an HttpOnly cookie (15 min), so a script on the page can no longer read it. A random refresh token (30 days, sliding) renews it; only its SHA-256 is stored. Each refresh replaces the token, and a replaced token that comes back revokes every token of that login. Unsafe requests need an X-TBP-Client or Authorization header, and CORS allows only the exact frontend origin. A missing or expired token now answers 401 instead of 403, so the frontend can refresh.