fix(auth): handle 400 error during token refresh flow
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user