From c9f89958f36e6870581d0b27231e0a9c3c04121f Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 7 Jun 2022 18:50:09 +0700 Subject: [PATCH] TF-624 Add AuthenticationClient for mobile/web platform to authenticate oidc --- .../authentication_client_base.dart | 31 +++++++ .../authentication_client_mobile.dart | 87 +++++++++++++++++++ .../authentication_client_web.dart | 47 ++++++++++ .../app_auth_mobile_plugin.dart | 4 + .../app_auth_plugin/app_auth_plugin.dart | 2 + .../app_auth_plugin/app_auth_web_plugin.dart | 2 + 6 files changed, 173 insertions(+) create mode 100644 lib/features/login/data/network/authentication_client/authentication_client_base.dart create mode 100644 lib/features/login/data/network/authentication_client/authentication_client_mobile.dart create mode 100644 lib/features/login/data/network/authentication_client/authentication_client_web.dart create mode 100644 lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_mobile_plugin.dart create mode 100644 lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart create mode 100644 lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_web_plugin.dart diff --git a/lib/features/login/data/network/authentication_client/authentication_client_base.dart b/lib/features/login/data/network/authentication_client/authentication_client_base.dart new file mode 100644 index 000000000..f2a0aec12 --- /dev/null +++ b/lib/features/login/data/network/authentication_client/authentication_client_base.dart @@ -0,0 +1,31 @@ + +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_mobile.dart' + if (dart.library.html) 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_web.dart'; + +abstract class AuthenticationClientBase { + Future authenticateOidcOnBrowser( + String clientId, + String redirectUrl, + String discoveryUrl, + List scopes); + + Future getTokenOIDC( + String clientId, + String redirectUrl, + String discoveryUrl, + List scopes); + + Future refreshingTokensOIDC( + String clientId, + String redirectUrl, + String discoveryUrl, + List scopes, + String refreshToken); + + Future logoutOidc(TokenId tokenId, OIDCConfiguration config); + + factory AuthenticationClientBase() => getAuthenticationClientImplementation(); +} \ No newline at end of file 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 new file mode 100644 index 000000000..0f3724b95 --- /dev/null +++ b/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart @@ -0,0 +1,87 @@ + +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/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_response_extension.dart'; +import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; +import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; + +class AuthenticationClientMobile implements AuthenticationClientBase { + + final FlutterAppAuth _appAuth; + + AuthenticationClientMobile(this._appAuth); + + @override + Future getTokenOIDC(String clientId, String redirectUrl, + String discoveryUrl, List scopes) async { + final authorizationTokenResponse = await _appAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + clientId, + redirectUrl, + discoveryUrl: discoveryUrl, + scopes: scopes, + preferEphemeralSession: true)); + + log('AuthenticationClientMobile::getTokenOIDC(): token: ${authorizationTokenResponse?.accessToken}'); + + if (authorizationTokenResponse != null) { + final tokenOIDC = authorizationTokenResponse.toTokenOIDC(); + if (tokenOIDC.isTokenValid()) { + return tokenOIDC; + } else { + throw AccessTokenInvalidException(); + } + } else { + throw NotFoundAccessTokenException(); + } + } + + @override + Future logoutOidc(TokenId tokenId, OIDCConfiguration config) async { + final endSession = await _appAuth.endSession(EndSessionRequest( + idTokenHint: tokenId.uuid, + postLogoutRedirectUrl: config.redirectUrl, + discoveryUrl: config.discoveryUrl + )); + log('AuthenticationClientMobile::logoutOidc(): ${endSession?.state}'); + return endSession?.state?.isNotEmpty == true; + } + + @override + Future refreshingTokensOIDC(String clientId, String redirectUrl, + String discoveryUrl, List scopes, String refreshToken) async { + final tokenResponse = await _appAuth.token(TokenRequest( + clientId, + redirectUrl, + discoveryUrl: discoveryUrl, + refreshToken: refreshToken, + scopes: scopes)); + + log('AuthenticationClientMobile::refreshingTokensOIDC(): refreshToken: ${tokenResponse?.accessToken}'); + + if (tokenResponse != null) { + final tokenOIDC = tokenResponse.toTokenOIDC(); + if (tokenOIDC.isTokenValid()) { + return tokenOIDC; + } else { + throw AccessTokenInvalidException(); + } + } else { + throw NotFoundAccessTokenException(); + } + } + + @override + Future authenticateOidcOnBrowser(String clientId, String redirectUrl, + String discoveryUrl, List scopes) { + return Future.value(null); + } +} + +AuthenticationClientBase getAuthenticationClientImplementation() => + AuthenticationClientMobile(Get.find()); \ No newline at end of file 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 new file mode 100644 index 000000000..463947b6c --- /dev/null +++ b/lib/features/login/data/network/authentication_client/authentication_client_web.dart @@ -0,0 +1,47 @@ + +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; +import 'package:flutter_appauth_platform_interface/flutter_appauth_platform_interface.dart'; +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; + +import '../../utils/library_platform/app_auth_plugin/app_auth_plugin.dart'; + +class AuthenticationClientWeb implements AuthenticationClientBase { + + final AppAuthWebPlugin _appAuthWeb; + + AuthenticationClientWeb(this._appAuthWeb); + + @override + Future getTokenOIDC(String clientId, String redirectUrl, + String discoveryUrl, List scopes) async { + throw UnimplementedError(); + } + + @override + Future logoutOidc(TokenId tokenId, OIDCConfiguration config) { + throw UnimplementedError(); + } + + @override + Future refreshingTokensOIDC(String clientId, String redirectUrl, + String discoveryUrl, List scopes, String refreshToken) { + throw UnimplementedError(); + } + + @override + Future authenticateOidcOnBrowser(String clientId, String redirectUrl, + String discoveryUrl, List scopes) async { + await _appAuthWeb.authorizeAndExchangeCode( + AuthorizationTokenRequest( + clientId, + redirectUrl, + discoveryUrl: discoveryUrl, + scopes: scopes)); + } +} + +AuthenticationClientBase getAuthenticationClientImplementation() => + AuthenticationClientWeb(Get.find()); \ No newline at end of file diff --git a/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_mobile_plugin.dart b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_mobile_plugin.dart new file mode 100644 index 000000000..b09fbbad5 --- /dev/null +++ b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_mobile_plugin.dart @@ -0,0 +1,4 @@ + +import 'package:flutter_appauth_platform_interface/flutter_appauth_platform_interface.dart'; + +class AppAuthWebPlugin extends FlutterAppAuthPlatform {} diff --git a/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart new file mode 100644 index 000000000..7b9927688 --- /dev/null +++ b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart @@ -0,0 +1,2 @@ + +export 'app_auth_mobile_plugin.dart' if (dart.library.html) 'app_auth_web_plugin.dart'; \ No newline at end of file diff --git a/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_web_plugin.dart b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_web_plugin.dart new file mode 100644 index 000000000..b702d23b2 --- /dev/null +++ b/lib/features/login/data/utils/library_platform/app_auth_plugin/app_auth_web_plugin.dart @@ -0,0 +1,2 @@ + +export 'package:flutter_appauth_web/flutter_appauth_web.dart'; \ No newline at end of file