Harden AuthInterceptor: retry guard, single-flight refresh, shared pref keys
Addresses PR review on the auth interceptor: - Guard against unbounded retry recursion: tag the replayed request with extra['auth_retried'] and skip the refresh/replay path on a second 401 so it can't recurse refresh -> replay indefinitely. Drop the redundant manual Authorization header on the replay (onRequest re-injects it). - Fix the concurrent-refresh race: coalesce concurrent 401s onto a single in-flight refresh future so the rotating refresh token is only consumed once, instead of later refreshes posting an already-consumed token and spuriously logging out. (Chose the shared-future approach over QueuedInterceptor, which deadlocks with the replay-via-same-Dio pattern.) - Centralise the 'bearer' / 'refresh_token' / 'customer_data' pref keys in a PrefKeys constants class used by the interceptor, network manager, auth service and router so they can't silently diverge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tm_app/core/constants/pref_keys.dart';
|
||||
import 'package:tm_app/core/utils/logger.dart';
|
||||
import 'package:tm_app/data/services/api/auth_api.dart';
|
||||
import 'package:tm_app/data/services/model/customer/customer.dart';
|
||||
@@ -58,13 +59,13 @@ class AuthService {
|
||||
final refreshToken = result.value.data.refreshToken;
|
||||
final customer = result.value.data.customer;
|
||||
|
||||
await prefs.setString('bearer', token!);
|
||||
await prefs.setString('refresh_token', refreshToken!);
|
||||
await prefs.setString(PrefKeys.bearer, token!);
|
||||
await prefs.setString(PrefKeys.refreshToken, refreshToken!);
|
||||
|
||||
// Save customer data as JSON string
|
||||
if (customer != null) {
|
||||
final customerJson = jsonEncode(customer.toJson());
|
||||
await prefs.setString('customer_data', customerJson);
|
||||
await prefs.setString(PrefKeys.customerData, customerJson);
|
||||
AppLogger().info('👤 Customer data saved to preferences');
|
||||
}
|
||||
}
|
||||
@@ -102,9 +103,9 @@ class AuthService {
|
||||
|
||||
Future<void> localLogout() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('bearer');
|
||||
await prefs.remove('refresh_token');
|
||||
await prefs.remove('customer_data');
|
||||
await prefs.remove(PrefKeys.bearer);
|
||||
await prefs.remove(PrefKeys.refreshToken);
|
||||
await prefs.remove(PrefKeys.customerData);
|
||||
appLogger.info('tokens and customer data cleared!');
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ class AuthService {
|
||||
|
||||
Future<Result<bool>> checkIsLoggedIn() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final token = prefs.getString('bearer');
|
||||
final token = prefs.getString(PrefKeys.bearer);
|
||||
if (token == null) {
|
||||
return const Result.ok(false);
|
||||
}
|
||||
@@ -150,7 +151,7 @@ class AuthService {
|
||||
|
||||
Future<Customer?> getStoredCustomer() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final customerJson = prefs.getString('customer_data');
|
||||
final customerJson = prefs.getString(PrefKeys.customerData);
|
||||
if (customerJson == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user