Skip to content

Commit 6f73b33

Browse files
committed
PWA復帰時に認証が切れる問題に対応
- accessTokenExpiresAt の状態を localStorage に持つようにする
1 parent d7e301e commit 6f73b33

1 file changed

Lines changed: 22 additions & 6 deletions

File tree

frontend/src/composables/useAuth.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import type {
1010

1111
const API_BASE = import.meta.env.VITE_API_BASE || "";
1212

13-
// Storage key for user info only (tokens are in httpOnly cookies)
13+
// Storage keys (tokens are in httpOnly cookies, only metadata stored locally)
1414
const STORAGE_KEY_USER = "soeji-auth-user";
15+
const STORAGE_KEY_TOKEN_EXPIRES = "soeji-auth-token-expires";
1516

1617
// Token refresh check interval (every 30 seconds)
1718
const TOKEN_CHECK_INTERVAL_MS = 30 * 1000;
@@ -42,11 +43,16 @@ async function checkAndRefreshToken(): Promise<void> {
4243
return;
4344
}
4445

45-
if (accessTokenExpiresAt) {
46-
const remainingMs = accessTokenExpiresAt.getTime() - Date.now();
47-
if (remainingMs < TOKEN_REFRESH_THRESHOLD_MS) {
48-
await doRefreshAccessToken();
49-
}
46+
// If accessTokenExpiresAt is not available (e.g., after process restart),
47+
// attempt refresh to ensure we have a valid token
48+
if (!accessTokenExpiresAt) {
49+
await doRefreshAccessToken();
50+
return;
51+
}
52+
53+
const remainingMs = accessTokenExpiresAt.getTime() - Date.now();
54+
if (remainingMs < TOKEN_REFRESH_THRESHOLD_MS) {
55+
await doRefreshAccessToken();
5056
}
5157
}
5258

@@ -98,6 +104,8 @@ async function doRefreshAccessToken(): Promise<boolean> {
98104

99105
const data: RefreshResponse = await response.json();
100106
accessTokenExpiresAt = new Date(data.accessTokenExpiresAt);
107+
// Persist to localStorage for recovery after process restart
108+
localStorage.setItem(STORAGE_KEY_TOKEN_EXPIRES, data.accessTokenExpiresAt);
101109

102110
return true;
103111
} catch (error) {
@@ -109,13 +117,19 @@ async function doRefreshAccessToken(): Promise<boolean> {
109117
// Load stored user on initialization
110118
function loadStoredUser(): void {
111119
const storedUser = localStorage.getItem(STORAGE_KEY_USER);
120+
const storedExpires = localStorage.getItem(STORAGE_KEY_TOKEN_EXPIRES);
112121

113122
if (storedUser) {
114123
try {
115124
const user = JSON.parse(storedUser);
116125
currentUser.value = user;
117126
isAuthenticated.value = true;
118127
mustChangePassword.value = user.mustChangePassword || false;
128+
129+
// Restore token expiration time if available
130+
if (storedExpires) {
131+
accessTokenExpiresAt = new Date(storedExpires);
132+
}
119133
} catch {
120134
clearAuthState();
121135
}
@@ -125,6 +139,7 @@ function loadStoredUser(): void {
125139
function clearAuthState(): void {
126140
stopRefreshCheckTimer();
127141
localStorage.removeItem(STORAGE_KEY_USER);
142+
localStorage.removeItem(STORAGE_KEY_TOKEN_EXPIRES);
128143
currentUser.value = null;
129144
isAuthenticated.value = false;
130145
mustChangePassword.value = false;
@@ -134,6 +149,7 @@ function clearAuthState(): void {
134149

135150
function setAuthState(user: AuthUser, expiresAt: Date): void {
136151
localStorage.setItem(STORAGE_KEY_USER, JSON.stringify(user));
152+
localStorage.setItem(STORAGE_KEY_TOKEN_EXPIRES, expiresAt.toISOString());
137153
currentUser.value = user;
138154
isAuthenticated.value = true;
139155
mustChangePassword.value = user.mustChangePassword || false;

0 commit comments

Comments
 (0)