# 🔄 Authentication API Changes - Frontend Migration Guide **Date:** December 24, 2024 **Breaking Changes:** Yes (token format changed) --- ## Summary of Changes The authentication system has been upgraded to a **WhatsApp-style dual-token strategy** for improved security. This affects login, signup, and token handling. --- ## 🚨 Breaking Changes ### 1. Login/Signup Response Format Changed **Before:** ```json { "success": true, "data": { "user": {...}, "token": "eyJhbG..." } } ``` **After:** ```json { "success": true, "data": { "user": {...}, "access_token": "eyJhbG...", "refresh_token": "eyJhbG...", "token_type": "bearer", "expires_in": 900 } } ``` | Field | Description | |-------|-------------| | `access_token` | Use for API calls (15 min expiry) | | `refresh_token` | Use to get new access tokens (30 day expiry) | | `expires_in` | Seconds until access_token expires | --- ### 2. Token Expiry Times | Before | After | |--------|-------| | Single token: 60 days | Access token: 15 minutes | | | Refresh token: 30 days | --- ### 3. OTP Length Changed | Before | After | |--------|-------| | 4 digits (1234) | 6 digits (123456) | Update your OTP input field to accept 6 characters. --- ### 4. Password Requirements Added Passwords must now contain: - ✅ At least 8 characters - ✅ At least 1 uppercase letter (A-Z) - ✅ At least 1 lowercase letter (a-z) - ✅ At least 1 number (0-9) **Error Response:** ```json { "success": false, "message": "Password must contain at least 8 characters, an uppercase letter, a lowercase letter, a number" } ``` --- ## 🆕 New Endpoint: Token Refresh ### `POST /api/auth/refresh` Use this to get new access tokens without re-login. **Request:** ```json { "refresh_token": "eyJhbGciOiJIUzI1NiIs..." } ``` **Success Response (200):** ```json { "success": true, "message": "Tokens refreshed successfully.", "data": { "access_token": "eyJhbG...", "refresh_token": "eyJhbG...", "token_type": "bearer", "expires_in": 900 } } ``` **Error Response (401):** ```json { "detail": "Invalid or expired refresh token. Please login again." } ``` --- ## 📱 Flutter Implementation Guide ### 1. Update Token Storage ```dart // OLD: Single token await secureStorage.write(key: 'token', value: token); // NEW: Store both tokens await secureStorage.write(key: 'access_token', value: data['access_token']); await secureStorage.write(key: 'refresh_token', value: data['refresh_token']); ``` ### 2. Add Token Refresh Interceptor ```dart class AuthInterceptor extends Interceptor { final Dio dio; final SecureStorage storage; AuthInterceptor(this.dio, this.storage); @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { final token = await storage.read(key: 'access_token'); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); } @override void onError(DioException error, ErrorInterceptorHandler handler) async { if (error.response?.statusCode == 401) { // Access token expired - try to refresh final refreshed = await _refreshTokens(); if (refreshed) { // Retry the original request with new token final newToken = await storage.read(key: 'access_token'); error.requestOptions.headers['Authorization'] = 'Bearer $newToken'; final response = await dio.fetch(error.requestOptions); return handler.resolve(response); } else { // Refresh failed - logout user await _logout(); } } handler.next(error); } Future _refreshTokens() async { try { final refreshToken = await storage.read(key: 'refresh_token'); if (refreshToken == null) return false; final response = await Dio().post( '$baseUrl/api/auth/refresh', data: {'refresh_token': refreshToken}, ); if (response.data['success']) { await storage.write(key: 'access_token', value: response.data['data']['access_token']); await storage.write(key: 'refresh_token', value: response.data['data']['refresh_token']); return true; } return false; } catch (e) { return false; } } Future _logout() async { await storage.delete(key: 'access_token'); await storage.delete(key: 'refresh_token'); // Navigate to login screen } } ``` ### 3. Update Login Handler ```dart Future handleLogin(String identifier, String password) async { final response = await dio.post('/api/auth/login', data: { 'identifier': identifier, 'password': password, }); if (response.data['success']) { final data = response.data['data']; // Store BOTH tokens await storage.write(key: 'access_token', value: data['access_token']); await storage.write(key: 'refresh_token', value: data['refresh_token']); // Store user data final user = User.fromJson(data['user']); await saveUser(user); // Navigate to home navigateToHome(); } } ``` ### 4. Update Password Validation UI ```dart String? validatePassword(String password) { if (password.length < 8) { return 'Password must be at least 8 characters'; } if (!password.contains(RegExp(r'[A-Z]'))) { return 'Password must contain an uppercase letter'; } if (!password.contains(RegExp(r'[a-z]'))) { return 'Password must contain a lowercase letter'; } if (!password.contains(RegExp(r'[0-9]'))) { return 'Password must contain a number'; } return null; } ``` ### 5. Update OTP Input ```dart // Change maxLength from 4 to 6 TextField( controller: otpController, maxLength: 6, // Changed from 4 keyboardType: TextInputType.number, decoration: InputDecoration( hintText: '123456', // Updated hint ), ) ``` --- ## 🔄 Token Flow Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ USER LOGIN │ │ POST /api/auth/login │ │ Returns: access_token (15m) + refresh_token (30d) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ NORMAL API CALLS │ │ Header: Authorization: Bearer │ │ → API responds normally │ └─────────────────────────────────────────────────────────────┘ │ ▼ (after 15 minutes) ┌─────────────────────────────────────────────────────────────┐ │ ACCESS TOKEN EXPIRES │ │ API returns 401 Unauthorized │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ AUTOMATIC REFRESH │ │ POST /api/auth/refresh │ │ Body: { "refresh_token": "..." } │ │ Returns: NEW access_token + NEW refresh_token │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ RETRY ORIGINAL REQUEST │ │ With new access_token → Success! │ │ User never notices! ✨ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## ⚡ Rate Limiting (New) Auth endpoints now have rate limits to prevent brute force: | Endpoint | Limit | |----------|-------| | `/login` | 100 requests/minute per IP | | `/signup` | 100 requests/minute per IP | | `/verify-signup-otp` | 100 requests/minute per IP | **429 Response:** ```json { "detail": "Too many login attempts. Please try again later." } ``` Handle this by showing a "Please wait" message. --- ## ✅ Migration Checklist - [ ] Update login response parsing for `access_token` + `refresh_token` - [ ] Update signup response parsing - [ ] Add token refresh interceptor - [ ] Store both tokens securely - [ ] Update OTP input to 6 digits - [ ] Add password strength validation UI - [ ] Handle 429 rate limit errors - [ ] Test token refresh flow