fix(frontend): update token refresh threshold and fix race condition

The 14-day refresh threshold was designed for the old 30-day tokens but the backend now emits 1-hour tokens. The threshold caused every page load to trigger an immediate /refresh-token call, which revoked the old JTI mid-flight while concurrent requests (profile, giftcards, bookings, etc.) were still using it, causing 401 errors.

Changes:
- Refresh threshold: 14 days -> 5 minutes (matches 1-hour token lifetime)
- Interval check: 60 min -> 2 min (must be shorter than threshold)
- Race condition: refresh now completes before profile fetch, preventing JTI invalidation during concurrent requests

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-18 16:34:35 +01:00
co-authored by Sisyphus
parent 96ec3f3e33
commit df6c0dece5
+15 -8
View File
@@ -32,14 +32,16 @@ class AuthStore {
constructor() {
if (browser) {
this.initializeAuth();
// Check token refresh every hour
// Check token refresh every 2 minutes
// (must be shorter than the 5-minute threshold so the
// refresh check fires before the token actually expires)
setInterval(
() => {
if (this.token) {
this.refreshTokenIfNeeded();
}
},
60 * 60 * 1000
2 * 60 * 1000
);
}
}
@@ -78,9 +80,13 @@ class AuthStore {
firstName: '',
lastName: ''
};
this.fetchUserProfile();
// Check if token needs refresh
this.refreshTokenIfNeeded();
// Refresh first so any JTI invalidation from rotation
// happens before other requests use this token.
// Then fetch profile with the (potentially refreshed) token.
this.refreshTokenIfNeeded().then(() => {
this.fetchUserProfile();
});
} else {
this.clearAuth();
}
@@ -195,9 +201,10 @@ class AuthStore {
return;
}
// Refresh if token expires in less than 14 days
const fourteenDays = 14 * 24 * 60 * 60 * 1000;
if (decoded.exp * 1000 - Date.now() < fourteenDays) {
// Refresh if token expires in less than 5 minutes
// (1-hour token lifetime from backend)
const fiveMinutes = 5 * 60 * 1000;
if (decoded.exp * 1000 - Date.now() < fiveMinutes) {
try {
const response = await fetch('/api/refresh-token', {
method: 'POST',