fix(auth): handle 400 error during token refresh flow

This commit is contained in:
dab246
2026-02-05 15:14:19 +07:00
committed by Dat H. Pham
parent eb1805d010
commit ada44dd5d0
4 changed files with 72 additions and 16 deletions
+15 -1
View File
@@ -156,7 +156,8 @@ abstract class BaseController extends GetxController
bool validateUrgentException(dynamic exception) {
return exception is NoNetworkError
|| exception is BadCredentialsException
|| exception is ConnectionError;
|| exception is ConnectionError
|| exception is RefreshTokenFailedException;
}
void handleErrorViewState(Object error, StackTrace stackTrace) {}
@@ -177,6 +178,8 @@ abstract class BaseController extends GetxController
_handleConnectionErrorException();
} else if (exception is BadCredentialsException) {
handleBadCredentialsException();
} else if (exception is RefreshTokenFailedException) {
handleRefreshTokenFailedException();
}
}
@@ -188,6 +191,8 @@ abstract class BaseController extends GetxController
_handleConnectionErrorException();
} else if (exception is BadCredentialsException) {
handleBadCredentialsException();
} else if (exception is RefreshTokenFailedException) {
handleRefreshTokenFailedException();
}
}
@@ -242,6 +247,15 @@ abstract class BaseController extends GetxController
clearDataAndGoToLoginPage();
}
void handleRefreshTokenFailedException() {
log('$runtimeType::handleRefreshTokenFailedException:');
if (twakeAppManager.hasComposer) {
_performSaveAndReconnection();
} else {
_performReconnection();
}
}
void onDataFailureViewState(Failure failure) {
log('$runtimeType::onDataFailureViewState:failure = ${failure.runtimeType}');
if (failure is FeatureFailure) {
@@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/login/data/network/authentication_client/
import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart';
import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart';
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
@@ -93,24 +94,47 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
responseStatusCode: err.response?.statusCode,
tokenOIDC: _token
)) {
log('AuthorizationInterceptors::onError: Perform get New Token');
final newTokenOidc = PlatformInfo.isIOS
? await _getNewTokenForIOSPlatform()
: await _getNewTokenForOtherPlatform();
try {
log('AuthorizationInterceptors::onError: Perform get New Token');
final newTokenOidc = PlatformInfo.isIOS
? await _getNewTokenForIOSPlatform()
: await _getNewTokenForOtherPlatform();
if (newTokenOidc.token == _token?.token) {
log('AuthorizationInterceptors::onError: Token duplicated');
return super.onError(err, handler);
}
_updateNewToken(newTokenOidc);
final personalAccount = await _updateCurrentAccount(tokenOIDC: newTokenOidc);
if (PlatformInfo.isIOS) {
await _iosSharingManager.saveKeyChainSharingSession(personalAccount);
}
isRetryRequest = true;
} on DioException catch (refreshError, st) {
if (refreshError.response?.statusCode == 400) {
logError(
'AuthorizationInterceptors: Refresh Token Failed 400',
exception: refreshError,
stackTrace: st,
);
clear();
final sessionExpiredError = DioException(
requestOptions: err.requestOptions,
error: RefreshTokenFailedException(),
type: DioExceptionType.badResponse,
response: refreshError.response,
);
return handler.reject(sessionExpiredError);
}
if (newTokenOidc.token == _token?.token) {
log('AuthorizationInterceptors::onError: Token duplicated');
return super.onError(err, handler);
}
_updateNewToken(newTokenOidc);
final personalAccount = await _updateCurrentAccount(tokenOIDC: newTokenOidc);
if (PlatformInfo.isIOS) {
await _iosSharingManager.saveKeyChainSharingSession(personalAccount);
}
isRetryRequest = true;
} else if (validateToRetryTheRequestWithNewToken(
authHeader: requestOptions.headers[HttpHeaders.authorizationHeader],
tokenOIDC: _token
+14
View File
@@ -62,4 +62,18 @@ class CannotCalculateChangesMethodResponseException extends MethodLevelErrors {
class NoNetworkError extends RemoteException {
const NoNetworkError() : super(message: RemoteException.noNetworkError);
}
class RefreshTokenFailedException extends RemoteException {
final int? statusCode;
RefreshTokenFailedException({
super.message =
'Refresh Token failed with 400 Bad Request. The session is invalid/revoked.',
this.statusCode = 400,
});
@override
String toString() =>
"RefreshTokenFailedException(status: $statusCode): $message";
}
@@ -37,6 +37,10 @@ class RemoteExceptionThrower extends ExceptionThrower {
'RemoteExceptionThrower::throwException():type: ${error.type} | response: ${error.response} | error: ${error.error}',
);
if (error.error is RefreshTokenFailedException) {
throw RefreshTokenFailedException();
}
final response = error.response;
final statusCode = response?.statusCode;