add refresh token
This commit is contained in:
@@ -18,8 +18,8 @@ abstract class AuthRemoteDataSource {
|
||||
/// Get current user profile
|
||||
Future<UserModel> getProfile();
|
||||
|
||||
/// Refresh access token
|
||||
Future<AuthResponseModel> refreshToken();
|
||||
/// Refresh access token using refresh token
|
||||
Future<AuthResponseModel> refreshToken(String refreshToken);
|
||||
}
|
||||
|
||||
/// Implementation of AuthRemoteDataSource
|
||||
@@ -119,21 +119,28 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AuthResponseModel> refreshToken() async {
|
||||
Future<AuthResponseModel> refreshToken(String refreshToken) async {
|
||||
try {
|
||||
final response = await dioClient.post(ApiConstants.refreshToken);
|
||||
print('📡 DataSource: Calling refresh token API...');
|
||||
final response = await dioClient.post(
|
||||
ApiConstants.refreshToken,
|
||||
data: {'refreshToken': refreshToken},
|
||||
);
|
||||
|
||||
if (response.statusCode == ApiConstants.statusOk) {
|
||||
// API returns nested structure: {success, data: {access_token, user}, message}
|
||||
// API returns nested structure: {success, data: {access_token, refresh_token, user}, message}
|
||||
// Extract the 'data' object
|
||||
final responseData = response.data['data'] as Map<String, dynamic>;
|
||||
print('📡 DataSource: Token refreshed successfully');
|
||||
return AuthResponseModel.fromJson(responseData);
|
||||
} else {
|
||||
throw ServerException('Token refresh failed with status: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
print('❌ DataSource: Refresh token failed - ${e.message}');
|
||||
throw _handleDioError(e);
|
||||
} catch (e) {
|
||||
print('❌ DataSource: Unexpected error refreshing token: $e');
|
||||
throw ServerException('Unexpected error refreshing token: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'user_model.dart';
|
||||
class AuthResponseModel extends AuthResponse {
|
||||
const AuthResponseModel({
|
||||
required super.accessToken,
|
||||
required super.refreshToken,
|
||||
required super.user,
|
||||
});
|
||||
|
||||
@@ -12,6 +13,7 @@ class AuthResponseModel extends AuthResponse {
|
||||
factory AuthResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
return AuthResponseModel(
|
||||
accessToken: json['access_token'] as String,
|
||||
refreshToken: json['refresh_token'] as String,
|
||||
user: UserModel.fromJson(json['user'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
@@ -20,6 +22,7 @@ class AuthResponseModel extends AuthResponse {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'access_token': accessToken,
|
||||
'refresh_token': refreshToken,
|
||||
'user': (user as UserModel).toJson(),
|
||||
};
|
||||
}
|
||||
@@ -28,6 +31,7 @@ class AuthResponseModel extends AuthResponse {
|
||||
factory AuthResponseModel.fromEntity(AuthResponse authResponse) {
|
||||
return AuthResponseModel(
|
||||
accessToken: authResponse.accessToken,
|
||||
refreshToken: authResponse.refreshToken,
|
||||
user: authResponse.user,
|
||||
);
|
||||
}
|
||||
@@ -36,6 +40,7 @@ class AuthResponseModel extends AuthResponse {
|
||||
AuthResponse toEntity() {
|
||||
return AuthResponse(
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
user: user,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,12 +35,13 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
|
||||
print('🔐 Repository: Got response, token length=${authResponse.accessToken.length}');
|
||||
|
||||
// Save token to secure storage only if rememberMe is true
|
||||
// Save tokens to secure storage only if rememberMe is true
|
||||
if (rememberMe) {
|
||||
await secureStorage.saveAccessToken(authResponse.accessToken);
|
||||
print('🔐 Repository: Token saved to secure storage (persistent)');
|
||||
await secureStorage.saveRefreshToken(authResponse.refreshToken);
|
||||
print('🔐 Repository: Access token and refresh token saved to secure storage (persistent)');
|
||||
} else {
|
||||
print('🔐 Repository: Token NOT saved (session only - rememberMe is false)');
|
||||
print('🔐 Repository: Tokens NOT saved (session only - rememberMe is false)');
|
||||
}
|
||||
|
||||
// Set token in Dio client for subsequent requests (always for current session)
|
||||
@@ -86,8 +87,9 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
);
|
||||
final authResponse = await remoteDataSource.register(registerDto);
|
||||
|
||||
// Save token to secure storage
|
||||
// Save both tokens to secure storage
|
||||
await secureStorage.saveAccessToken(authResponse.accessToken);
|
||||
await secureStorage.saveRefreshToken(authResponse.refreshToken);
|
||||
|
||||
// Set token in Dio client for subsequent requests
|
||||
dioClient.setAuthToken(authResponse.accessToken);
|
||||
@@ -127,24 +129,44 @@ class AuthRepositoryImpl implements AuthRepository {
|
||||
@override
|
||||
Future<Either<Failure, AuthResponse>> refreshToken() async {
|
||||
try {
|
||||
final authResponse = await remoteDataSource.refreshToken();
|
||||
print('🔄 Repository: Starting token refresh...');
|
||||
|
||||
// Update token in secure storage
|
||||
// Get refresh token from storage
|
||||
final storedRefreshToken = await secureStorage.getRefreshToken();
|
||||
if (storedRefreshToken == null) {
|
||||
print('❌ Repository: No refresh token found in storage');
|
||||
return const Left(UnauthorizedFailure('No refresh token available'));
|
||||
}
|
||||
|
||||
print('🔄 Repository: Calling datasource with refresh token...');
|
||||
final authResponse = await remoteDataSource.refreshToken(storedRefreshToken);
|
||||
|
||||
// Update both tokens in secure storage (token rotation)
|
||||
await secureStorage.saveAccessToken(authResponse.accessToken);
|
||||
await secureStorage.saveRefreshToken(authResponse.refreshToken);
|
||||
print('🔄 Repository: New tokens saved to secure storage');
|
||||
|
||||
// Update token in Dio client
|
||||
dioClient.setAuthToken(authResponse.accessToken);
|
||||
print('🔄 Repository: New access token set in DioClient');
|
||||
|
||||
return Right(authResponse);
|
||||
} on UnauthorizedException catch (e) {
|
||||
print('❌ Repository: Unauthorized during refresh - ${e.message}');
|
||||
// Clear invalid tokens
|
||||
await secureStorage.deleteAllTokens();
|
||||
return Left(UnauthorizedFailure(e.message));
|
||||
} on TokenExpiredException catch (e) {
|
||||
print('❌ Repository: Token expired during refresh - ${e.message}');
|
||||
// Clear expired tokens
|
||||
await secureStorage.deleteAllTokens();
|
||||
return Left(TokenExpiredFailure(e.message));
|
||||
} on NetworkException catch (e) {
|
||||
return Left(NetworkFailure(e.message));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} catch (e) {
|
||||
print('❌ Repository: Unexpected error during refresh: $e');
|
||||
return Left(ServerFailure('Unexpected error: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user