@@ -10,8 +10,9 @@ import type {
1010
1111const 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 )
1414const 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)
1718const 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
110118function 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 {
125139function 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
135150function 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