diff --git a/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart b/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart index 583fccc72..a979314c1 100644 --- a/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart +++ b/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart @@ -37,6 +37,13 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { }).catchError(_exceptionThrower.throwException); } + @override + Future discoverOIDC(OIDCConfiguration oidcConfiguration) { + return Future.sync(() async { + return await _oidcHttpClient.discoverOIDC(oidcConfiguration); + }).catchError(_exceptionThrower.throwException); + } + @override Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes) { return Future.sync(() async { @@ -91,9 +98,9 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { } @override - Future logout(TokenId tokenId, OIDCConfiguration config) { + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) { return Future.sync(() async { - return await _authenticationClient.logoutOidc(tokenId, config); + return await _authenticationClient.logoutOidc(tokenId, config, oidcRescovery); }).catchError(_exceptionThrower.throwException); } diff --git a/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart b/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart index 1947d1ff6..db4f90c04 100644 --- a/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart +++ b/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart @@ -3,6 +3,7 @@ import 'package:core/utils/app_logger.dart'; import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:get/get.dart'; import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:model/oidc/token_id.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart'; @@ -43,11 +44,19 @@ class AuthenticationClientMobile implements AuthenticationClientBase { } @override - Future logoutOidc(TokenId tokenId, OIDCConfiguration config) async { + Future logoutOidc(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) async { + final authorizationServiceConfiguration = oidcRescovery.authorizationEndpoint == null || oidcRescovery.tokenEndpoint == null + ? null + : AuthorizationServiceConfiguration( + authorizationEndpoint: oidcRescovery.authorizationEndpoint!, + tokenEndpoint: oidcRescovery.tokenEndpoint!, + endSessionEndpoint: oidcRescovery.endSessionEndpoint); + final endSession = await _appAuth.endSession(EndSessionRequest( idTokenHint: tokenId.uuid, postLogoutRedirectUrl: config.logoutRedirectUrl, - discoveryUrl: config.discoveryUrl + discoveryUrl: config.discoveryUrl, + serviceConfiguration: authorizationServiceConfiguration )); log('AuthenticationClientMobile::logoutOidc(): ${endSession?.state}'); return endSession?.state?.isNotEmpty == true; diff --git a/lib/features/login/data/network/authentication_client/authentication_client_web.dart b/lib/features/login/data/network/authentication_client/authentication_client_web.dart index 4c2f72b4f..919014ad0 100644 --- a/lib/features/login/data/network/authentication_client/authentication_client_web.dart +++ b/lib/features/login/data/network/authentication_client/authentication_client_web.dart @@ -1,6 +1,7 @@ import 'package:core/utils/app_logger.dart'; import 'package:get/get.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart'; import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; import 'package:tmail_ui_user/features/login/data/extensions/token_response_extension.dart'; @@ -45,11 +46,18 @@ class AuthenticationClientWeb implements AuthenticationClientBase { } @override - Future logoutOidc(TokenId tokenId, OIDCConfiguration config) async { + Future logoutOidc(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) async { + final authorizationServiceConfiguration = oidcRescovery.authorizationEndpoint == null || oidcRescovery.tokenEndpoint == null + ? null + : AuthorizationServiceConfiguration( + authorizationEndpoint: oidcRescovery.authorizationEndpoint!, + tokenEndpoint: oidcRescovery.tokenEndpoint!, + endSessionEndpoint: oidcRescovery.endSessionEndpoint); final endSession = await _appAuthWeb.endSession(EndSessionRequest( idTokenHint: tokenId.uuid, postLogoutRedirectUrl: config.logoutRedirectUrl, - discoveryUrl: config.discoveryUrl + discoveryUrl: config.discoveryUrl, + serviceConfiguration: authorizationServiceConfiguration )); return endSession != null; } diff --git a/lib/features/login/data/network/oidc_http_client.dart b/lib/features/login/data/network/oidc_http_client.dart index b6f503fcb..3526e62c7 100644 --- a/lib/features/login/data/network/oidc_http_client.dart +++ b/lib/features/login/data/network/oidc_http_client.dart @@ -7,6 +7,7 @@ import 'package:core/utils/app_logger.dart'; import 'package:model/oidc/oidc_configuration.dart'; import 'package:model/oidc/request/oidc_request.dart'; import 'package:model/oidc/response/oidc_response.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart'; import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart'; import 'package:tmail_ui_user/features/login/data/network/endpoint.dart'; @@ -47,4 +48,14 @@ class OIDCHttpClient { scopes: OIDCConstant.oidcScope ); } + + Future discoverOIDC(OIDCConfiguration configuration) async { + final result = await _dioClient.get(configuration.discoveryUrl); + log('OIDCHttpClient::discoverOIDC(): RESULT: $result'); + if (result is Map) { + return OIDCDiscoveryResponse.fromJson(result); + } else { + return OIDCDiscoveryResponse.fromJson(jsonDecode(result)); + } + } } \ No newline at end of file diff --git a/lib/features/login/data/repository/authentication_oidc_repository_impl.dart b/lib/features/login/data/repository/authentication_oidc_repository_impl.dart index d27da621a..8e709a240 100644 --- a/lib/features/login/data/repository/authentication_oidc_repository_impl.dart +++ b/lib/features/login/data/repository/authentication_oidc_repository_impl.dart @@ -1,5 +1,6 @@ import 'package:model/oidc/oidc_configuration.dart'; import 'package:model/oidc/request/oidc_request.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:model/oidc/response/oidc_response.dart'; import 'package:model/oidc/token_id.dart'; import 'package:model/oidc/token_oidc.dart'; @@ -21,6 +22,12 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository { return _oidcDataSource.getOIDCConfiguration(oidcResponse); } + + @override + Future discoverOIDC(OIDCConfiguration oidcConfiguration) { + return _oidcDataSource.discoverOIDC(oidcConfiguration); + } + @override Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes) { return _oidcDataSource.getTokenOIDC(clientId, redirectUrl, discoveryUrl, scopes); @@ -63,8 +70,8 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository { } @override - Future logout(TokenId tokenId, OIDCConfiguration config) { - return _oidcDataSource.logout(tokenId, config); + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) { + return _oidcDataSource.logout(tokenId, config, oidcRescovery); } @override diff --git a/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart b/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart index 9c9c413e9..0ea5db1b8 100644 --- a/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart +++ b/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart @@ -1,10 +1,10 @@ - import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:model/account/authentication_type.dart'; import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; @@ -12,27 +12,29 @@ import 'package:tmail_ui_user/features/login/domain/repository/authentication_oi import 'package:tmail_ui_user/features/manage_account/domain/state/log_out_oidc_state.dart'; class LogoutOidcInteractor { - final AccountRepository _accountRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository; - LogoutOidcInteractor(this._accountRepository, this._authenticationOIDCRepository); + LogoutOidcInteractor( + this._accountRepository, this._authenticationOIDCRepository); Stream> execute() async* { try { final currentAccount = await _accountRepository.getCurrentAccount(); log('LogoutOidcInteractor::execute(): currentAccount: $currentAccount'); if (currentAccount.authenticationType == AuthenticationType.oidc) { - final result = await Future.wait([ - _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id), - _authenticationOIDCRepository.getStoredOidcConfiguration() - ]).then((result) async { - final tokenOidc = result.first as TokenOIDC; - final oidcConfig = result.last as OIDCConfiguration; - log('LogoutOidcInteractor::execute(): tokenOidc: ${tokenOidc.tokenId.uuid}'); - log('LogoutOidcInteractor::execute(): oidcConfig: $oidcConfig'); - return await _authenticationOIDCRepository.logout(tokenOidc.tokenId, oidcConfig); - }); + final result = await _authenticationOIDCRepository.getStoredOidcConfiguration() + .then((oidcConfig) => Future.wait([ + Future.value(oidcConfig), + _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id), + _authenticationOIDCRepository.discoverOIDC(oidcConfig) + ])) + .then((oidcParameters) async { + final oidcConfig = oidcParameters[0] as OIDCConfiguration; + final tokenOIDC = oidcParameters[1] as TokenOIDC; + final oidcDiscoveryResponse = oidcParameters[2] as OIDCDiscoveryResponse; + return await _authenticationOIDCRepository.logout(tokenOIDC.tokenId, oidcConfig, oidcDiscoveryResponse); + }); log('LogoutOidcInteractor::execute(): statusSuccess: $result'); if (result) { yield Right(LogoutOidcSuccess()); @@ -40,11 +42,12 @@ class LogoutOidcInteractor { yield Left(LogoutOidcFailure(null)); } } else { - yield Left(LogoutOidcFailure(NotFoundAuthenticatedAccountException())); + yield Left( + LogoutOidcFailure(NotFoundAuthenticatedAccountException())); } } catch (e) { log('LogoutOidcInteractor::execute(): EXCEPTION: $e'); yield Left(LogoutOidcFailure(e)); } } -} \ No newline at end of file +}