Fix token refresh lifecycle in Flutter mobile app — refresh token not persisted or used
Problem
After the mobile app is idle long enough for the access token to expire (24 hours), all API calls return 401 errors. The user is forced to log in again even though the backend fully supports token refresh.
Root Cause
The backend correctly returns both accessToken and refreshToken on login, and the POST /auth/refresh-token endpoint works properly. The problem is entirely in the Flutter mobile app:
-
Refresh token is never persisted.
UserService.login()stores the full response (includingrefreshToken) in_currentUser(memory only). When the app restarts or is backgrounded long enough, the refresh token is lost. -
Refresh endpoint is never called.
AuthProxy.refreshToken()exists inlib/services/auth_proxy.dartbut is never invoked anywhere in the app. -
No 401 interceptor. There is no HTTP interceptor that detects 401 responses and automatically triggers a token refresh + retry.
What Needs to Change (Flutter only)
1. Persist the refresh token
- On login, save
refreshTokentoSharedPreferencesorflutter_secure_storage - On app startup, check if a stored refresh token exists and is valid
- Clear stored refresh token on logout
2. Add an HTTP interceptor
- In the
ApiServiceHTTP client, add a response interceptor that catches 401 errors - When a 401 is caught:
- Read the stored refresh token
- Call
POST /auth/refresh-tokenwith{ refreshToken } - If successful: update the stored access token, retry the original request
- If refresh fails: redirect to login screen
3. Proactive refresh
- Optionally, check token expiration before requests and refresh proactively when the access token is close to expiring
Backend API Reference (no backend changes needed)
The backend already supports all of this:
-
Login:
POST /api/auth/login→ returns{ accessToken, refreshToken, ...userFields } -
Refresh:
POST /api/auth/refresh-token→ accepts{ refreshToken }, returns{ success: true, accessToken, refreshToken } - Access token TTL: 24 hours
- Refresh token TTL: 7 days
- Tokens are rotated on refresh (new refresh token issued each time)
- Token versioning prevents reuse after logout/password change
Related
- Supersedes #342 (closed) (which incorrectly attributed the problem to the backend)