TF-3278 Handle open app via deep link at MailboxDashboard screen
This commit is contained in:
@@ -16,9 +16,9 @@ abstract class AuthenticationOIDCDataSource {
|
||||
|
||||
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash);
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration);
|
||||
|
||||
Future<void> deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration();
|
||||
|
||||
Future<OIDCConfiguration> getStoredOidcConfiguration();
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> persistAuthorityOidc(String authority) {
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) {
|
||||
return Future.sync(() async {
|
||||
return await _oidcConfigurationCacheManager.persistAuthorityOidc(authority);
|
||||
return await _oidcConfigurationCacheManager.persistOidcConfiguration(oidcConfiguration);
|
||||
}).catchError(_cacheExceptionThrower.throwException);
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAuthorityOidc() {
|
||||
Future<void> deleteOidcConfiguration() {
|
||||
return Future.sync(() async {
|
||||
return await _oidcConfigurationCacheManager.deleteAuthorityOidc();
|
||||
return await _oidcConfigurationCacheManager.deleteOidcConfiguration();
|
||||
}).catchError(_exceptionThrower.throwException);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
extension OidcConfigutationCacheExtension on OidcConfigurationCache {
|
||||
OIDCConfiguration toOIDCConfiguration() {
|
||||
return OIDCConfiguration(
|
||||
authority: authority,
|
||||
isTWP: isTWP,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,54 @@
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tmail_ui_user/features/caching/clients/oidc_configuration_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/extensions/oidc_configutation_cache_extension.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
class OidcConfigurationCacheManager {
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final OidcConfigurationCacheClient _oidcConfigurationCacheClient;
|
||||
|
||||
OidcConfigurationCacheManager(this._sharedPreferences);
|
||||
OidcConfigurationCacheManager(this._sharedPreferences, this._oidcConfigurationCacheClient);
|
||||
|
||||
Future<OIDCConfiguration> getOidcConfiguration() async {
|
||||
final authority = _sharedPreferences.getString(OIDCConstant.keyAuthorityOidc);
|
||||
if (authority == null || authority.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
} else {
|
||||
final oidcConfigurationCache = await _oidcConfigurationCacheClient.getItem(CachingConstants.oidcConfigurationCacheKeyName);
|
||||
if (oidcConfigurationCache == null) {
|
||||
final authority = _sharedPreferences.getString(OIDCConstant.keyAuthorityOidc);
|
||||
|
||||
if (authority == null || authority.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
}
|
||||
|
||||
return OIDCConfiguration(
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes);
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes,
|
||||
);
|
||||
} else {
|
||||
return oidcConfigurationCache.toOIDCConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority) async {
|
||||
log('OidcConfigurationCacheManager::persistAuthorityOidc(): $authority');
|
||||
await _sharedPreferences.setString(OIDCConstant.keyAuthorityOidc, authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) async {
|
||||
log('OidcConfigurationCacheManager::persistOidcConfiguration(): $oidcConfiguration');
|
||||
await _oidcConfigurationCacheClient.insertItem(
|
||||
CachingConstants.oidcConfigurationCacheKeyName,
|
||||
oidcConfiguration.toOidcConfigurationCache(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteAuthorityOidc() async {
|
||||
log('OidcConfigurationCacheManager::deleteAuthorityOidc()');
|
||||
await _sharedPreferences.remove(OIDCConstant.keyAuthorityOidc);
|
||||
Future<void> deleteOidcConfiguration() async {
|
||||
log('OidcConfigurationCacheManager::deleteOidcConfiguration()');
|
||||
await Future.wait([
|
||||
_oidcConfigurationCacheClient.deleteItem(
|
||||
CachingConstants.oidcConfigurationCacheKeyName,
|
||||
),
|
||||
_sharedPreferences.remove(OIDCConstant.keyAuthorityOidc),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
part 'oidc_configuration_cache.g.dart';
|
||||
|
||||
@HiveType(typeId: CachingConstants.OIDC_CONFIGURATION_CACHE_ID)
|
||||
class OidcConfigurationCache extends HiveObject with EquatableMixin {
|
||||
|
||||
@HiveField(0)
|
||||
final String authority;
|
||||
|
||||
@HiveField(1)
|
||||
final bool isTWP;
|
||||
|
||||
OidcConfigurationCache(this.authority, this.isTWP);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [authority, isTWP];
|
||||
}
|
||||
@@ -4,13 +4,17 @@ import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
class OIDCConstant {
|
||||
static String get mobileOidcClientId => 'teammail-mobile';
|
||||
static List<String> get oidcScope => ['openid', 'profile', 'email', 'offline_access'];
|
||||
static const keyAuthorityOidc = 'KEY_AUTHORITY_OIDC';
|
||||
static const authResponseKey = "auth_info";
|
||||
static const String keyAuthorityOidc = 'KEY_AUTHORITY_OIDC';
|
||||
static const String authResponseKey = "auth_info";
|
||||
static const String twakeWorkplaceUrlScheme = 'twakemail.mobile';
|
||||
static const String twakeWorkplaceRedirectUrl = '$twakeWorkplaceUrlScheme://redirect';
|
||||
static const String appParameter = 'tmail';
|
||||
static const String postRegisteredRedirectUrlPathParams = 'post_registered_redirect_url';
|
||||
static const String postLoginRedirectUrlPathParams = 'post_login_redirect_url';
|
||||
static const String endSessionFailedCode = 'end_session_failed';
|
||||
static const String redirectOidcMobile = 'teammail.mobile://oauthredirect';
|
||||
static const String loginRedirectOidcWeb = 'login-callback.html';
|
||||
static const String logoutRedirectOidcWeb = 'logout-callback.html';
|
||||
|
||||
static String get clientId => PlatformInfo.isWeb ? AppConfig.webOidcClientId : mobileOidcClientId;
|
||||
}
|
||||
@@ -52,11 +52,14 @@ class OIDCHttpClient {
|
||||
if (oidcResponse.links.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
}
|
||||
log('OIDCHttpClient::getOIDCConfiguration(): href: ${oidcResponse.links[0].href}');
|
||||
final authority = oidcResponse.links[0].href.toString();
|
||||
final isTWP = authority == AppConfig.saasRegistrationUrl;
|
||||
log('OIDCHttpClient::getOIDCConfiguration():authority: $authority');
|
||||
return OIDCConfiguration(
|
||||
authority: oidcResponse.links[0].href.toString(),
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes
|
||||
scopes: AppConfig.oidcScopes,
|
||||
isTWP: isTWP,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
return _oidcDataSource.getOIDCConfiguration(oidcResponse);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration) {
|
||||
return _oidcDataSource.discoverOIDC(oidcConfiguration);
|
||||
@@ -49,8 +48,8 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> persistAuthorityOidc(String authority) {
|
||||
return _oidcDataSource.persistAuthorityOidc(authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) {
|
||||
return _oidcDataSource.persistOidcConfiguration(oidcConfiguration);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -75,8 +74,8 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAuthorityOidc() {
|
||||
return _oidcDataSource.deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration() {
|
||||
return _oidcDataSource.deleteOidcConfiguration();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
class UserCancelledLogoutOIDCFlowException implements Exception {}
|
||||
@@ -4,6 +4,7 @@ import 'package:core/data/network/config/service_path.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
@@ -12,32 +13,29 @@ extension OidcConfigurationExtensions on OIDCConfiguration {
|
||||
String get redirectUrl {
|
||||
if (PlatformInfo.isWeb) {
|
||||
return AppConfig.domainRedirectUrl.endsWith('/')
|
||||
? AppConfig.domainRedirectUrl + loginRedirectOidcWeb
|
||||
: '${AppConfig.domainRedirectUrl}/$loginRedirectOidcWeb';
|
||||
? AppConfig.domainRedirectUrl + OIDCConstant.loginRedirectOidcWeb
|
||||
: '${AppConfig.domainRedirectUrl}/${OIDCConstant.loginRedirectOidcWeb}';
|
||||
} else {
|
||||
return _isSaasAuthority(authority)
|
||||
return isTWP
|
||||
? OIDCConstant.twakeWorkplaceRedirectUrl
|
||||
: redirectOidcMobile;
|
||||
: OIDCConstant.redirectOidcMobile;
|
||||
}
|
||||
}
|
||||
|
||||
String get logoutRedirectUrl {
|
||||
if (PlatformInfo.isWeb) {
|
||||
if (AppConfig.domainRedirectUrl.endsWith('/')) {
|
||||
return AppConfig.domainRedirectUrl + logoutRedirectOidcWeb;
|
||||
return AppConfig.domainRedirectUrl + OIDCConstant.logoutRedirectOidcWeb;
|
||||
} else {
|
||||
return '${AppConfig.domainRedirectUrl}/$logoutRedirectOidcWeb';
|
||||
return '${AppConfig.domainRedirectUrl}/${OIDCConstant.logoutRedirectOidcWeb}';
|
||||
}
|
||||
} else {
|
||||
return _isSaasAuthority(authority)
|
||||
return isTWP
|
||||
? OIDCConstant.twakeWorkplaceRedirectUrl
|
||||
: redirectOidcMobile;
|
||||
: OIDCConstant.redirectOidcMobile;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSaasAuthority(String authority) =>
|
||||
authority == AppConfig.saasRegistrationUrl;
|
||||
|
||||
String get signInTWPUrl => ServicePath(authority)
|
||||
.withQueryParameters([
|
||||
StringQueryParameter(
|
||||
@@ -57,4 +55,8 @@ extension OidcConfigurationExtensions on OIDCConfiguration {
|
||||
StringQueryParameter('app', OIDCConstant.appParameter),
|
||||
])
|
||||
.generateEndpointPath();
|
||||
|
||||
OidcConfigurationCache toOidcConfigurationCache() {
|
||||
return OidcConfigurationCache(authority, isTWP);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ abstract class AuthenticationOIDCRepository {
|
||||
|
||||
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash);
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration);
|
||||
|
||||
Future<void> deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration();
|
||||
|
||||
Future<OIDCConfiguration> getStoredOidcConfiguration();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class DeleteAuthorityOidcInteractor {
|
||||
try {
|
||||
await Future.wait([
|
||||
_credentialRepository.removeBaseUrl(),
|
||||
_authenticationOIDCRepository.deleteAuthorityOidc(),
|
||||
_authenticationOIDCRepository.deleteOidcConfiguration(),
|
||||
_authenticationOIDCRepository.deleteTokenOIDC(),
|
||||
]);
|
||||
return Right(DeleteAuthorityOidcSuccess());
|
||||
|
||||
@@ -15,7 +15,7 @@ class GetOIDCConfigurationInteractor {
|
||||
try {
|
||||
yield Right<Failure, Success>(GetOIDCConfigurationLoading());
|
||||
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse);
|
||||
await _oidcRepository.persistAuthorityOidc(oidcConfiguration.authority);
|
||||
await _oidcRepository.persistOidcConfiguration(oidcConfiguration);
|
||||
yield Right<Failure, Success>(GetOIDCConfigurationSuccess(oidcConfiguration));
|
||||
} catch (e) {
|
||||
log('GetOIDCConfigurationInteractor::execute(): ERROR: $e');
|
||||
|
||||
@@ -35,7 +35,7 @@ class GetTokenOIDCInteractor {
|
||||
await Future.wait([
|
||||
_credentialRepository.saveBaseUrl(baseUrl),
|
||||
authenticationOIDCRepository.persistTokenOIDC(tokenOIDC),
|
||||
authenticationOIDCRepository.persistAuthorityOidc(config.authority),
|
||||
authenticationOIDCRepository.persistOidcConfiguration(config),
|
||||
]);
|
||||
|
||||
await _accountRepository.setCurrentAccount(
|
||||
|
||||
@@ -58,6 +58,7 @@ import 'package:tmail_ui_user/features/starting_page/domain/state/sign_in_twake_
|
||||
import 'package:tmail_ui_user/features/starting_page/domain/usecase/sign_in_twake_workplace_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
@@ -163,6 +164,7 @@ class LoginController extends ReloadableController {
|
||||
} else if (failure is GetAuthenticatedAccountFailure) {
|
||||
_checkOIDCIsAvailable();
|
||||
} else if (failure is GetSessionFailure) {
|
||||
SmartDialog.dismiss();
|
||||
clearAllData();
|
||||
} else if (failure is DNSLookupToGetJmapUrlFailure) {
|
||||
_username = null;
|
||||
@@ -218,6 +220,7 @@ class LoginController extends ReloadableController {
|
||||
_handleNoSuitableBrowserOIDC(failure)
|
||||
.map((stillFailed) => _handleCommonOIDCFailure());
|
||||
} else if (failure is GetSessionFailure) {
|
||||
SmartDialog.dismiss();
|
||||
clearAllData();
|
||||
} else {
|
||||
super.handleUrgentException(failure: failure, exception: exception);
|
||||
@@ -234,12 +237,6 @@ class LoginController extends ReloadableController {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleGetSessionFailure(GetSessionFailure failure) {
|
||||
SmartDialog.dismiss();
|
||||
super.handleGetSessionFailure(failure);
|
||||
}
|
||||
|
||||
void _registerDeepLinks() {
|
||||
_deepLinksManager = getBinding<DeepLinksManager>();
|
||||
_deepLinksManager?.clearPendingDeepLinkData();
|
||||
@@ -250,16 +247,21 @@ class LoginController extends ReloadableController {
|
||||
|
||||
void _handlePendingDeepLinkDataStream(DeepLinkData? deepLinkData) {
|
||||
log('LoginController::_handlePendingDeepLinkDataStream:DeepLinkData = $deepLinkData');
|
||||
if (deepLinkData == null) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.handleDeepLinksWhenAppOnForegroundNotSignedIn(
|
||||
_deepLinksManager?.handleDeepLinksWhenAppRunning(
|
||||
deepLinkData: deepLinkData,
|
||||
onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is! OpenAppDeepLinkData) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:tmail_ui_user/features/home/domain/state/auto_sign_in_via_deep_link_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart';
|
||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||
|
||||
class LoginNavigateArguments extends RouterArguments {
|
||||
|
||||
final LoginNavigateType navigateType;
|
||||
final AutoSignInViaDeepLinkSuccess? autoSignInViaDeepLinkSuccess;
|
||||
|
||||
LoginNavigateArguments({
|
||||
required this.navigateType,
|
||||
this.autoSignInViaDeepLinkSuccess,
|
||||
});
|
||||
|
||||
factory LoginNavigateArguments.autoSignIn({
|
||||
required AutoSignInViaDeepLinkSuccess autoSignInViaDeepLinkSuccess,
|
||||
}) {
|
||||
return LoginNavigateArguments(
|
||||
navigateType: LoginNavigateType.autoSignIn,
|
||||
autoSignInViaDeepLinkSuccess: autoSignInViaDeepLinkSuccess,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
navigateType,
|
||||
autoSignInViaDeepLinkSuccess,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
enum LoginNavigateType {
|
||||
autoSignIn
|
||||
}
|
||||
Reference in New Issue
Block a user