Difference between refresh-token and access-token in the Rest API?
#api
Answer
Overview
In REST API authentication, access tokens and refresh tokens work together to keep users securely logged in without requiring them to re-enter their password frequently.
Access Token
A short-lived token that grants access to protected API resources.
| Property | Value |
|---|---|
| Lifetime | Short (15 min – 1 hour) |
| Sent with | Every API request (Authorization header) |
| Stored in | Memory or secure storage |
| If stolen | Risk is limited (expires quickly) |
dart// Using access token in every API request final response = await http.get( Uri.parse('https://api.example.com/profile'), headers: { 'Authorization': 'Bearer $accessToken', // ← access token 'Content-Type': 'application/json', }, );
Refresh Token
A long-lived token used only to get a new access token when the old one expires.
| Property | Value |
|---|---|
| Lifetime | Long (7 days – 30 days or more) |
| Sent with | Only to the token refresh endpoint |
| Stored in | Secure storage (Keychain/Keystore) |
| If stolen | Higher risk — can generate new access tokens |
dart// Refresh the access token using the refresh token Future<String> refreshAccessToken(String refreshToken) async { final response = await http.post( Uri.parse('https://api.example.com/auth/refresh'), body: jsonEncode({'refresh_token': refreshToken}), headers: {'Content-Type': 'application/json'}, ); final data = jsonDecode(response.body); return data['access_token']; }
The Full Token Flow
text1. User logs in with credentials ↓ 2. Server returns: access_token (short-lived) + refresh_token (long-lived) ↓ 3. App stores both securely (flutter_secure_storage) ↓ 4. Every API request uses access_token in Authorization header ↓ 5. access_token expires (401 Unauthorized) ↓ 6. App silently calls /auth/refresh with refresh_token ↓ 7. Server returns new access_token (+ optionally new refresh_token) ↓ 8. App retries the original request with new access_token
Auto-Refresh with Dio Interceptor
dartdio.interceptors.add(InterceptorsWrapper( onError: (DioException error, handler) async { if (error.response?.statusCode == 401) { try { // Try to refresh final newToken = await refreshAccessToken(getRefreshToken()); saveAccessToken(newToken); // Retry original request with new token final opts = error.requestOptions; opts.headers['Authorization'] = 'Bearer $newToken'; final response = await dio.fetch(opts); handler.resolve(response); } catch (e) { // Refresh failed — logout user logoutUser(); handler.reject(error); } } else { handler.next(error); } }, ));
Secure Storage in Flutter
dartimport 'package:flutter_secure_storage/flutter_secure_storage.dart'; const storage = FlutterSecureStorage(); // Store tokens await storage.write(key: 'access_token', value: accessToken); await storage.write(key: 'refresh_token', value: refreshToken); // Read tokens final token = await storage.read(key: 'access_token');
Comparison
| Feature | Access Token | Refresh Token |
|---|---|---|
| Lifetime | Short (minutes–hours) | Long (days–weeks) |
| Sent to | All API endpoints | Only /auth/refresh |
| Security risk if stolen | Low (expires fast) | High |
| Storage | Memory or secure storage | Secure storage only |
Best Practice: Never store tokens in SharedPreferences (plain text). Always use
which uses Android Keystore and iOS Keychain.textflutter_secure_storage