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.

PropertyValue
LifetimeShort (15 min – 1 hour)
Sent withEvery API request (Authorization header)
Stored inMemory or secure storage
If stolenRisk 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.

PropertyValue
LifetimeLong (7 days – 30 days or more)
Sent withOnly to the token refresh endpoint
Stored inSecure storage (Keychain/Keystore)
If stolenHigher 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

text
1. 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

dart
dio.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

dart
import '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

FeatureAccess TokenRefresh Token
LifetimeShort (minutes–hours)Long (days–weeks)
Sent toAll API endpointsOnly /auth/refresh
Security risk if stolenLow (expires fast)High
StorageMemory or secure storageSecure storage only

Best Practice: Never store tokens in SharedPreferences (plain text). Always use

text
flutter_secure_storage
which uses Android Keystore and iOS Keychain.