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) {
|
bool validateUrgentException(dynamic exception) {
|
||||||
return exception is NoNetworkError
|
return exception is NoNetworkError
|
||||||
|| exception is BadCredentialsException
|
|| exception is BadCredentialsException
|
||||||
|| exception is ConnectionError;
|
|| exception is ConnectionError
|
||||||
|
|| exception is RefreshTokenFailedException;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleErrorViewState(Object error, StackTrace stackTrace) {}
|
void handleErrorViewState(Object error, StackTrace stackTrace) {}
|
||||||
@@ -177,6 +178,8 @@ abstract class BaseController extends GetxController
|
|||||||
_handleConnectionErrorException();
|
_handleConnectionErrorException();
|
||||||
} else if (exception is BadCredentialsException) {
|
} else if (exception is BadCredentialsException) {
|
||||||
handleBadCredentialsException();
|
handleBadCredentialsException();
|
||||||
|
} else if (exception is RefreshTokenFailedException) {
|
||||||
|
handleRefreshTokenFailedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +191,8 @@ abstract class BaseController extends GetxController
|
|||||||
_handleConnectionErrorException();
|
_handleConnectionErrorException();
|
||||||
} else if (exception is BadCredentialsException) {
|
} else if (exception is BadCredentialsException) {
|
||||||
handleBadCredentialsException();
|
handleBadCredentialsException();
|
||||||
|
} else if (exception is RefreshTokenFailedException) {
|
||||||
|
handleRefreshTokenFailedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +247,15 @@ abstract class BaseController extends GetxController
|
|||||||
clearDataAndGoToLoginPage();
|
clearDataAndGoToLoginPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleRefreshTokenFailedException() {
|
||||||
|
log('$runtimeType::handleRefreshTokenFailedException:');
|
||||||
|
if (twakeAppManager.hasComposer) {
|
||||||
|
_performSaveAndReconnection();
|
||||||
|
} else {
|
||||||
|
_performReconnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onDataFailureViewState(Failure failure) {
|
void onDataFailureViewState(Failure failure) {
|
||||||
log('$runtimeType::onDataFailureViewState:failure = ${failure.runtimeType}');
|
log('$runtimeType::onDataFailureViewState:failure = ${failure.runtimeType}');
|
||||||
if (failure is FeatureFailure) {
|
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/exceptions/oauth_authorization_error.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.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/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';
|
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
||||||
|
|
||||||
class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
||||||
@@ -93,24 +94,47 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
responseStatusCode: err.response?.statusCode,
|
responseStatusCode: err.response?.statusCode,
|
||||||
tokenOIDC: _token
|
tokenOIDC: _token
|
||||||
)) {
|
)) {
|
||||||
log('AuthorizationInterceptors::onError: Perform get New Token');
|
try {
|
||||||
final newTokenOidc = PlatformInfo.isIOS
|
log('AuthorizationInterceptors::onError: Perform get New Token');
|
||||||
? await _getNewTokenForIOSPlatform()
|
final newTokenOidc = PlatformInfo.isIOS
|
||||||
: await _getNewTokenForOtherPlatform();
|
? 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);
|
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(
|
} else if (validateToRetryTheRequestWithNewToken(
|
||||||
authHeader: requestOptions.headers[HttpHeaders.authorizationHeader],
|
authHeader: requestOptions.headers[HttpHeaders.authorizationHeader],
|
||||||
tokenOIDC: _token
|
tokenOIDC: _token
|
||||||
|
|||||||
@@ -63,3 +63,17 @@ class CannotCalculateChangesMethodResponseException extends MethodLevelErrors {
|
|||||||
class NoNetworkError extends RemoteException {
|
class NoNetworkError extends RemoteException {
|
||||||
const NoNetworkError() : super(message: RemoteException.noNetworkError);
|
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}',
|
'RemoteExceptionThrower::throwException():type: ${error.type} | response: ${error.response} | error: ${error.error}',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (error.error is RefreshTokenFailedException) {
|
||||||
|
throw RefreshTokenFailedException();
|
||||||
|
}
|
||||||
|
|
||||||
final response = error.response;
|
final response = error.response;
|
||||||
final statusCode = response?.statusCode;
|
final statusCode = response?.statusCode;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user