@@ -14,9 +14,9 @@ import 'package:model/oidc/token_oidc.dart';
1414import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart' ;
1515import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart' ;
1616import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart' ;
17- import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart' ;
1817import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart' ;
1918import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart' ;
19+ import 'package:tmail_ui_user/main/exceptions/chained_request_error.dart' ;
2020import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart' ;
2121
2222class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
@@ -86,81 +86,46 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
8686 logError ('AuthorizationInterceptors::onError(): DIO_ERROR = $err ' );
8787 try {
8888 final requestOptions = err.requestOptions;
89- final extraInRequest = requestOptions.extra;
90- bool isRetryRequest = false ;
89+ final extra = requestOptions.extra;
9190
92- if (validateToRefreshToken (
91+ // Decide whether we need to refresh or retry
92+ final shouldRefresh = validateToRefreshToken (
9393 responseStatusCode: err.response? .statusCode,
94- tokenOIDC: _token
95- )) {
96- log ('AuthorizationInterceptors::onError: Perform get New Token' );
97- final newTokenOidc = PlatformInfo .isIOS
98- ? await _getNewTokenForIOSPlatform ()
99- : await _getNewTokenForOtherPlatform ();
100-
101- if (newTokenOidc.token == _token? .token) {
102- log ('AuthorizationInterceptors::onError: Token duplicated' );
103- return super .onError (err, handler);
104- }
105- _updateNewToken (newTokenOidc);
106-
107- final personalAccount = await _updateCurrentAccount (tokenOIDC: newTokenOidc);
108-
109- if (PlatformInfo .isIOS) {
110- await _iosSharingManager.saveKeyChainSharingSession (personalAccount);
111- }
112-
113- isRetryRequest = true ;
114- } else if (validateToRetryTheRequestWithNewToken (
94+ tokenOIDC: _token,
95+ );
96+ final shouldRetryWithNewToken = validateToRetryTheRequestWithNewToken (
11597 authHeader: requestOptions.headers[HttpHeaders .authorizationHeader],
116- tokenOIDC: _token
117- )) {
118- log ('AuthorizationInterceptors::onError: Request using old token' );
119- isRetryRequest = true ;
120- } else {
98+ tokenOIDC: _token,
99+ );
100+
101+ if (! shouldRefresh && ! shouldRetryWithNewToken) {
121102 return super .onError (err, handler);
122103 }
123104
124- if (isRetryRequest) {
125- if (extraInRequest.containsKey (FileUploader .uploadAttachmentExtraKey)) {
126- log ('AuthorizationInterceptors::onError: Retry upload request with TokenId = ${_token ?.tokenIdHash }' );
127- final uploadExtra = extraInRequest[FileUploader .uploadAttachmentExtraKey];
128-
129- requestOptions.headers[HttpHeaders .authorizationHeader] = _getTokenAsBearerHeader (_token! .token);
105+ // Refresh token if required
106+ if (shouldRefresh) {
107+ final newToken = await _refreshToken ();
108+ if (newToken == null ) {
109+ // Failed or duplicate token
110+ return super .onError (err, handler);
111+ }
112+ }
130113
131- final newOptions = Options (
132- method : requestOptions.method,
133- headers : requestOptions.headers,
134- ) ;
114+ // Retry request with updated token
115+ final updatedAuthHeader = _getTokenAsBearerHeader (_token ! .token);
116+ requestOptions.headers[ HttpHeaders .authorizationHeader] =
117+ updatedAuthHeader ;
135118
136- final response = await _dio.request (
137- requestOptions.path,
138- data: _getDataUploadRequest (uploadExtra),
139- queryParameters: requestOptions.queryParameters,
140- options: newOptions,
141- );
119+ final bool isUploadRequest =
120+ extra.containsKey (FileUploader .uploadAttachmentExtraKey);
142121
143- return handler.resolve (response);
144- } else {
145- log ('AuthorizationInterceptors::onError: Retry request with TokenId = ${_token ?.tokenIdHash }' );
146- requestOptions.headers[HttpHeaders .authorizationHeader] = _getTokenAsBearerHeader (_token! .token);
122+ final response = isUploadRequest
123+ ? await _retryUploadRequest (requestOptions, extra)
124+ : await _retryNormalRequest (requestOptions);
147125
148- final response = await _dio.fetch (requestOptions);
149- return handler.resolve (response);
150- }
151- } else {
152- return super .onError (err, handler);
153- }
126+ return handler.resolve (response);
154127 } catch (e) {
155- logError ('AuthorizationInterceptors::onError:Exception: $e ' );
156- if (e is ServerError || e is TemporarilyUnavailable ) {
157- return super .onError (
158- DioError (requestOptions: err.requestOptions, error: e),
159- handler,
160- );
161- } else {
162- return super .onError (err.copyWith (error: e), handler);
163- }
128+ return _handleRetryException (err, handler, e);
164129 }
165130 }
166131
@@ -211,24 +176,29 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
211176
212177 String _getTokenAsBearerHeader (String token) => 'Bearer $token ' ;
213178
214- Future <PersonalAccount > _updateCurrentAccount ({required TokenOIDC tokenOIDC}) async {
215- final currentAccount = await _accountCacheManager.getCurrentAccount ();
179+ Future <PersonalAccount ?> _updateCurrentAccount ({required TokenOIDC tokenOIDC}) async {
180+ try {
181+ final currentAccount = await _accountCacheManager.getCurrentAccount ();
216182
217- await _accountCacheManager.deleteCurrentAccount (currentAccount.id);
183+ await _accountCacheManager.deleteCurrentAccount (currentAccount.id);
218184
219- await _tokenOidcCacheManager.persistOneTokenOidc (tokenOIDC);
185+ await _tokenOidcCacheManager.persistOneTokenOidc (tokenOIDC);
220186
221- final personalAccount = PersonalAccount (
222- tokenOIDC.tokenIdHash,
223- AuthenticationType .oidc,
224- isSelected: true ,
225- accountId: currentAccount.accountId,
226- apiUrl: currentAccount.apiUrl,
227- userName: currentAccount.userName
228- );
229- await _accountCacheManager.setCurrentAccount (personalAccount);
187+ final personalAccount = PersonalAccount (
188+ tokenOIDC.tokenIdHash,
189+ AuthenticationType .oidc,
190+ isSelected: true ,
191+ accountId: currentAccount.accountId,
192+ apiUrl: currentAccount.apiUrl,
193+ userName: currentAccount.userName
194+ );
195+ await _accountCacheManager.setCurrentAccount (personalAccount);
230196
231- return personalAccount;
197+ return personalAccount;
198+ } catch (e) {
199+ logError ('AuthorizationInterceptors::_updateCurrentAccount: Exception = $e ' );
200+ return null ;
201+ }
232202 }
233203
234204 Future <TokenOIDC ?> _getTokenInKeychain (TokenOIDC currentTokenOidc) async {
@@ -275,6 +245,67 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
275245 return _invokeRefreshTokenFromServer ();
276246 }
277247
248+ Future <TokenOIDC ?> _refreshToken () async {
249+ log ('AuthorizationInterceptors::onError: Perform get New Token' );
250+
251+ final newToken = PlatformInfo .isIOS
252+ ? await _getNewTokenForIOSPlatform ()
253+ : await _getNewTokenForOtherPlatform ();
254+
255+ // Skip if duplicated
256+ if (newToken.token == _token? .token) {
257+ log ('AuthorizationInterceptors::onError: Token duplicated' );
258+ return null ;
259+ }
260+
261+ _updateNewToken (newToken);
262+
263+ final account = await _updateCurrentAccount (tokenOIDC: newToken);
264+ if (PlatformInfo .isIOS && account != null ) {
265+ await _iosSharingManager.saveKeyChainSharingSession (account);
266+ }
267+
268+ return newToken;
269+ }
270+
271+ Future <Response <dynamic >> _retryNormalRequest (RequestOptions options) async {
272+ log ('AuthorizationInterceptors::_retryNormalRequest: Retry request with TokenId = ${_token ?.tokenIdHash }' );
273+ return _dio.fetch (options);
274+ }
275+
276+ Future <Response <dynamic >> _retryUploadRequest (
277+ RequestOptions options,
278+ Map <String , dynamic > extra,
279+ ) async {
280+ log ('AuthorizationInterceptors::_retryUploadRequest: Retry upload request with TokenId = ${_token ?.tokenIdHash }' );
281+ final uploadExtra = extra[FileUploader .uploadAttachmentExtraKey];
282+ final newOptions = Options (
283+ method: options.method,
284+ headers: options.headers,
285+ );
286+
287+ return _dio.request (
288+ options.path,
289+ data: _getDataUploadRequest (uploadExtra),
290+ queryParameters: options.queryParameters,
291+ options: newOptions,
292+ );
293+ }
294+
295+ Future <void > _handleRetryException (
296+ DioError err,
297+ ErrorInterceptorHandler handler,
298+ Object e,
299+ ) async {
300+ logError ('AuthorizationInterceptors::_handleRetryException:Exception: $e ' );
301+ final chainedError = ChainedRequestError (
302+ requestOptions: err.requestOptions,
303+ primaryError: err,
304+ secondaryError: e,
305+ );
306+ return super .onError (chainedError, handler);
307+ }
308+
278309 void clear () {
279310 _authorization = null ;
280311 _token = null ;
0 commit comments