From 4da3b810f7c7625552a487789034b6497b841a15 Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 7 Jan 2025 10:53:23 +0700 Subject: [PATCH 1/3] TF-3348 Move accessToken from persistent storage to session storage for web --- .../data/local/token_oidc_cache_manager.dart | 14 ++--- .../local/web_token_oidc_cache_manager.dart | 58 +++++++++++++++++++ lib/main/bindings/local/local_bindings.dart | 8 ++- .../local/local_isolate_bindings.dart | 17 ++++-- 4 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 lib/features/login/data/local/web_token_oidc_cache_manager.dart diff --git a/lib/features/login/data/local/token_oidc_cache_manager.dart b/lib/features/login/data/local/token_oidc_cache_manager.dart index 83e015428b..e5363cf971 100644 --- a/lib/features/login/data/local/token_oidc_cache_manager.dart +++ b/lib/features/login/data/local/token_oidc_cache_manager.dart @@ -6,13 +6,13 @@ import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extensio import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; class TokenOidcCacheManager { - final TokenOidcCacheClient _tokenOidcCacheClient; + final TokenOidcCacheClient tokenOidcCacheClient; - TokenOidcCacheManager(this._tokenOidcCacheClient); + const TokenOidcCacheManager(this.tokenOidcCacheClient); Future getTokenOidc(String tokenIdHash) async { log('TokenOidcCacheManager::getTokenOidc(): tokenIdHash: $tokenIdHash'); - final tokenCache = await _tokenOidcCacheClient.getItem(tokenIdHash); + final tokenCache = await tokenOidcCacheClient.getItem(tokenIdHash); log('TokenOidcCacheManager::getTokenOidc(): tokenCache: $tokenCache'); if (tokenCache == null) { throw NotFoundStoredTokenException(); @@ -23,17 +23,17 @@ class TokenOidcCacheManager { Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); - await _tokenOidcCacheClient.clearAllData(); + await tokenOidcCacheClient.clearAllData(); log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); - await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenOIDC.toTokenOidcCache()); + await tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenOIDC.toTokenOidcCache()); log('TokenOidcCacheManager::persistOneTokenOidc(): done'); } Future deleteTokenOidc() async { - await _tokenOidcCacheClient.clearAllData(); + await tokenOidcCacheClient.clearAllData(); } - Future closeTokenOIDCHiveCacheBox() => _tokenOidcCacheClient.closeBox(); + Future closeTokenOIDCHiveCacheBox() => tokenOidcCacheClient.closeBox(); } \ No newline at end of file diff --git a/lib/features/login/data/local/web_token_oidc_cache_manager.dart b/lib/features/login/data/local/web_token_oidc_cache_manager.dart new file mode 100644 index 0000000000..2c6d62114e --- /dev/null +++ b/lib/features/login/data/local/web_token_oidc_cache_manager.dart @@ -0,0 +1,58 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_cache_extension.dart'; +import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; +import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; +import 'package:universal_html/html.dart'; + +class WebTokenOidcCacheManager extends TokenOidcCacheManager { + const WebTokenOidcCacheManager(super.tokenOidcCacheClient); + + static const _sessionStorageTokenKey = 'twake_mail_token_session_storage'; + + @override + Future getTokenOidc(String tokenIdHash) async { + log('WebTokenOidcCacheManager::getTokenOidc(): tokenIdHash: $tokenIdHash'); + final tokenHiveCache = await tokenOidcCacheClient.getItem(tokenIdHash); + final tokenSessionStorageCache = window.sessionStorage[_sessionStorageTokenKey]; + log('WebTokenOidcCacheManager::getTokenOidc(): tokenHiveCache: $tokenHiveCache'); + log('WebTokenOidcCacheManager::getTokenOidc(): tokenSessionStorageCache: $tokenSessionStorageCache'); + if (tokenHiveCache == null) { + throw NotFoundStoredTokenException(); + } else { + return TokenOidcCache( + tokenSessionStorageCache ?? 'dummy_token', + tokenHiveCache.tokenId, + tokenHiveCache.refreshToken, + expiredTime: tokenSessionStorageCache != null + ? tokenHiveCache.expiredTime + : DateTime.now().subtract(const Duration(hours: 1)), + ).toTokenOidc(); + } + } + + @override + Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { + log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); + await tokenOidcCacheClient.clearAllData(); + log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); + log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); + log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); + final tokenHiveCache = TokenOidcCache( + '', + tokenOIDC.tokenId.uuid, + tokenOIDC.refreshToken, + expiredTime: tokenOIDC.expiredTime, + ); + await tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenHiveCache); + window.sessionStorage[_sessionStorageTokenKey] = tokenOIDC.token; + log('TokenOidcCacheManager::persistOneTokenOidc(): done'); + } + + @override + Future deleteTokenOidc() async { + await tokenOidcCacheClient.clearAllData(); + window.sessionStorage.remove(_sessionStorageTokenKey); + } +} \ No newline at end of file diff --git a/lib/main/bindings/local/local_bindings.dart b/lib/main/bindings/local/local_bindings.dart index ae3a6faee0..ba288e89a8 100644 --- a/lib/main/bindings/local/local_bindings.dart +++ b/lib/main/bindings/local/local_bindings.dart @@ -1,5 +1,6 @@ import 'package:core/utils/file_utils.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -29,6 +30,7 @@ import 'package:tmail_ui_user/features/login/data/local/authentication_info_cach import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/local/web_token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_spam_report_manager.dart'; @@ -63,7 +65,11 @@ class LocalBindings extends Bindings { Get.put(RecentSearchCacheClient()); Get.put(RecentSearchCacheManager(Get.find())); Get.put(TokenOidcCacheClient()); - Get.put(TokenOidcCacheManager(Get.find())); + if (PlatformInfo.isWeb) { + Get.put(WebTokenOidcCacheManager(Get.find())); + } else { + Get.put(TokenOidcCacheManager(Get.find())); + } Get.put(AccountCacheClient()); Get.put(AccountCacheManager(Get.find())); Get.put(EncryptionKeyCacheClient()); diff --git a/lib/main/bindings/local/local_isolate_bindings.dart b/lib/main/bindings/local/local_isolate_bindings.dart index 2777e5ad98..653409dba6 100644 --- a/lib/main/bindings/local/local_isolate_bindings.dart +++ b/lib/main/bindings/local/local_isolate_bindings.dart @@ -1,4 +1,5 @@ +import 'package:core/utils/platform_info.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/caching/clients/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/encryption_key_cache_client.dart'; @@ -6,6 +7,7 @@ import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.d import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/local/web_token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; class LocalIsolateBindings extends Bindings { @@ -17,10 +19,17 @@ class LocalIsolateBindings extends Bindings { void _bindingCaching() { Get.put(TokenOidcCacheClient(), tag: BindingTag.isolateTag); - Get.put(TokenOidcCacheManager( - Get.find(tag: BindingTag.isolateTag)), - tag: BindingTag.isolateTag - ); + if (PlatformInfo.isWeb) { + Get.put(WebTokenOidcCacheManager( + Get.find(tag: BindingTag.isolateTag)), + tag: BindingTag.isolateTag + ); + } else { + Get.put(TokenOidcCacheManager( + Get.find(tag: BindingTag.isolateTag)), + tag: BindingTag.isolateTag + ); + } Get.put(AccountCacheClient(), tag: BindingTag.isolateTag); Get.put(AccountCacheManager( Get.find(tag: BindingTag.isolateTag)), From 34aedd865a01d7fcf6616af66b21196e17a3099f Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 7 Jan 2025 11:42:14 +0700 Subject: [PATCH 2/3] fixup! TF-3348 Move accessToken from persistent storage to session storage for web --- .../login/data/local/token_oidc_cache_manager.dart | 14 +++++++------- .../data/local/web_token_oidc_cache_manager.dart | 14 +++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/features/login/data/local/token_oidc_cache_manager.dart b/lib/features/login/data/local/token_oidc_cache_manager.dart index e5363cf971..0e1c456d5e 100644 --- a/lib/features/login/data/local/token_oidc_cache_manager.dart +++ b/lib/features/login/data/local/token_oidc_cache_manager.dart @@ -6,13 +6,13 @@ import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extensio import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; class TokenOidcCacheManager { - final TokenOidcCacheClient tokenOidcCacheClient; + final TokenOidcCacheClient _tokenOidcCacheClient; - const TokenOidcCacheManager(this.tokenOidcCacheClient); + const TokenOidcCacheManager(this._tokenOidcCacheClient); Future getTokenOidc(String tokenIdHash) async { log('TokenOidcCacheManager::getTokenOidc(): tokenIdHash: $tokenIdHash'); - final tokenCache = await tokenOidcCacheClient.getItem(tokenIdHash); + final tokenCache = await _tokenOidcCacheClient.getItem(tokenIdHash); log('TokenOidcCacheManager::getTokenOidc(): tokenCache: $tokenCache'); if (tokenCache == null) { throw NotFoundStoredTokenException(); @@ -23,17 +23,17 @@ class TokenOidcCacheManager { Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); - await tokenOidcCacheClient.clearAllData(); + await _tokenOidcCacheClient.clearAllData(); log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); - await tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenOIDC.toTokenOidcCache()); + await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenOIDC.toTokenOidcCache()); log('TokenOidcCacheManager::persistOneTokenOidc(): done'); } Future deleteTokenOidc() async { - await tokenOidcCacheClient.clearAllData(); + await _tokenOidcCacheClient.clearAllData(); } - Future closeTokenOIDCHiveCacheBox() => tokenOidcCacheClient.closeBox(); + Future closeTokenOIDCHiveCacheBox() => _tokenOidcCacheClient.closeBox(); } \ No newline at end of file diff --git a/lib/features/login/data/local/web_token_oidc_cache_manager.dart b/lib/features/login/data/local/web_token_oidc_cache_manager.dart index 2c6d62114e..5a7b4edaac 100644 --- a/lib/features/login/data/local/web_token_oidc_cache_manager.dart +++ b/lib/features/login/data/local/web_token_oidc_cache_manager.dart @@ -1,5 +1,6 @@ import 'package:core/utils/app_logger.dart'; import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.dart'; import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_cache_extension.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; @@ -7,14 +8,17 @@ import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_ex import 'package:universal_html/html.dart'; class WebTokenOidcCacheManager extends TokenOidcCacheManager { - const WebTokenOidcCacheManager(super.tokenOidcCacheClient); + const WebTokenOidcCacheManager(this._tokenOidcCacheClient) + : super(_tokenOidcCacheClient); + + final TokenOidcCacheClient _tokenOidcCacheClient; static const _sessionStorageTokenKey = 'twake_mail_token_session_storage'; @override Future getTokenOidc(String tokenIdHash) async { log('WebTokenOidcCacheManager::getTokenOidc(): tokenIdHash: $tokenIdHash'); - final tokenHiveCache = await tokenOidcCacheClient.getItem(tokenIdHash); + final tokenHiveCache = await _tokenOidcCacheClient.getItem(tokenIdHash); final tokenSessionStorageCache = window.sessionStorage[_sessionStorageTokenKey]; log('WebTokenOidcCacheManager::getTokenOidc(): tokenHiveCache: $tokenHiveCache'); log('WebTokenOidcCacheManager::getTokenOidc(): tokenSessionStorageCache: $tokenSessionStorageCache'); @@ -35,7 +39,7 @@ class WebTokenOidcCacheManager extends TokenOidcCacheManager { @override Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); - await tokenOidcCacheClient.clearAllData(); + await _tokenOidcCacheClient.clearAllData(); log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); @@ -45,14 +49,14 @@ class WebTokenOidcCacheManager extends TokenOidcCacheManager { tokenOIDC.refreshToken, expiredTime: tokenOIDC.expiredTime, ); - await tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenHiveCache); + await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenHiveCache); window.sessionStorage[_sessionStorageTokenKey] = tokenOIDC.token; log('TokenOidcCacheManager::persistOneTokenOidc(): done'); } @override Future deleteTokenOidc() async { - await tokenOidcCacheClient.clearAllData(); + await _tokenOidcCacheClient.clearAllData(); window.sessionStorage.remove(_sessionStorageTokenKey); } } \ No newline at end of file From e4a42674d72f2ae99062a4df02dd10130e854ff2 Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 7 Jan 2025 16:23:42 +0700 Subject: [PATCH 3/3] fixup! TF-3348 Move accessToken from persistent storage to session storage for web --- .../data/extensions/token_oidc_extension.dart | 4 ++ .../local/web_token_oidc_cache_manager.dart | 8 +-- .../bindings/local/local_bindings_test.dart | 60 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 test/main/bindings/local/local_bindings_test.dart diff --git a/lib/features/login/data/extensions/token_oidc_extension.dart b/lib/features/login/data/extensions/token_oidc_extension.dart index 5fbcce7d41..a559e0fdb6 100644 --- a/lib/features/login/data/extensions/token_oidc_extension.dart +++ b/lib/features/login/data/extensions/token_oidc_extension.dart @@ -5,4 +5,8 @@ extension TokenOidcExtension on TokenOIDC { TokenOidcCache toTokenOidcCache() { return TokenOidcCache(token, tokenId.uuid, refreshToken, expiredTime: expiredTime); } + + TokenOidcCache toTokenOidcCacheWithoutToken() { + return TokenOidcCache('', tokenId.uuid, refreshToken, expiredTime: expiredTime); + } } \ No newline at end of file diff --git a/lib/features/login/data/local/web_token_oidc_cache_manager.dart b/lib/features/login/data/local/web_token_oidc_cache_manager.dart index 5a7b4edaac..961759b89d 100644 --- a/lib/features/login/data/local/web_token_oidc_cache_manager.dart +++ b/lib/features/login/data/local/web_token_oidc_cache_manager.dart @@ -2,6 +2,7 @@ import 'package:core/utils/app_logger.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.dart'; import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_cache_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; @@ -43,12 +44,7 @@ class WebTokenOidcCacheManager extends TokenOidcCacheManager { log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); - final tokenHiveCache = TokenOidcCache( - '', - tokenOIDC.tokenId.uuid, - tokenOIDC.refreshToken, - expiredTime: tokenOIDC.expiredTime, - ); + final tokenHiveCache = tokenOIDC.toTokenOidcCacheWithoutToken(); await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenHiveCache); window.sessionStorage[_sessionStorageTokenKey] = tokenOIDC.token; log('TokenOidcCacheManager::persistOneTokenOidc(): done'); diff --git a/test/main/bindings/local/local_bindings_test.dart b/test/main/bindings/local/local_bindings_test.dart new file mode 100644 index 0000000000..535e4255d0 --- /dev/null +++ b/test/main/bindings/local/local_bindings_test.dart @@ -0,0 +1,60 @@ +import 'package:core/utils/file_utils.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/instance_manager.dart'; +import 'package:mockito/annotations.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/local/web_token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/main/bindings/local/local_bindings.dart'; + +import 'local_bindings_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), +]) +void main() { + late LocalBindings localBindings; + + setUp(() { + localBindings = LocalBindings(); + Get.put(MockFlutterSecureStorage()); + Get.put(MockSharedPreferences()); + Get.put(MockFileUtils()); + }); + + group('local bindings test:', () { + test( + 'should inject WebTokenOidcCacheManager ' + 'when platform is web', + () { + // arrange + PlatformInfo.isTestingForWeb = true; + localBindings.dependencies(); + + // act + final cacheManager = Get.find(); + + // assert + expect(cacheManager, isInstanceOf()); + PlatformInfo.isTestingForWeb = false; + }); + + test( + 'should inject TokenOidcCacheManager ' + 'when platform is not web (default)', + () { + // arrange + localBindings.dependencies(); + + // act + final cacheManager = Get.find(); + + // assert + expect(cacheManager, isInstanceOf()); + }); + }); +} \ No newline at end of file