TF-571 Setup and get OIDC configuration
This commit is contained in:
@@ -40,6 +40,9 @@ android {
|
||||
versionName flutterVersionName
|
||||
// The number of method references in a .dex file cannot exceed 64K
|
||||
multiDexEnabled true
|
||||
manifestPlaceholders = [
|
||||
'appAuthRedirectScheme': 'teammail.mobile'
|
||||
]
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.APP_BROWSER" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>teammail-mobile</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
@@ -3,4 +3,6 @@ import 'package:model/model.dart';
|
||||
|
||||
abstract class AuthenticationOIDCDataSource {
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri);
|
||||
}
|
||||
@@ -17,4 +17,13 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) {
|
||||
return Future.sync(() async {
|
||||
return await _oidcHttpClient.getOIDCConfiguration(baseUri);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,8 @@ class OIDCHttpClient {
|
||||
return OIDCResponse.fromJson(jsonDecode(result));
|
||||
}
|
||||
}
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) async {
|
||||
return OIDCConfiguration.initial();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:model/oidc/request/oidc_request.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
|
||||
@@ -11,4 +12,9 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
|
||||
return _oidcDataSource.checkOIDCIsAvailable(oidcRequest);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) {
|
||||
return _oidcDataSource.getOIDCConfiguration(baseUri);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:model/oidc/request/oidc_request.dart';
|
||||
|
||||
abstract class AuthenticationOIDCRepository {
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
class GetOIDCConfigurationSuccess extends UIState {
|
||||
|
||||
final OIDCConfiguration oidcConfiguration;
|
||||
|
||||
GetOIDCConfigurationSuccess(this.oidcConfiguration);
|
||||
|
||||
@override
|
||||
List<Object> get props => [oidcConfiguration];
|
||||
}
|
||||
|
||||
class GetOIDCConfigurationFailure extends FeatureFailure {
|
||||
final dynamic exception;
|
||||
|
||||
GetOIDCConfigurationFailure(this.exception);
|
||||
|
||||
@override
|
||||
List<Object> get props => [exception];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
|
||||
|
||||
class GetOIDCConfigurationInteractor {
|
||||
final AuthenticationOIDCRepository _oidcRepository;
|
||||
|
||||
GetOIDCConfigurationInteractor(this._oidcRepository);
|
||||
|
||||
Future<Either<Failure, Success>> execute(Uri baseUri) async {
|
||||
try {
|
||||
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(baseUri);
|
||||
log('GetOIDCConfigurationInteractor::execute(): oidcConfiguration: $oidcConfiguration');
|
||||
return Right<Failure, Success>(GetOIDCConfigurationSuccess(oidcConfiguration));
|
||||
} catch (e) {
|
||||
log('GetOIDCConfigurationInteractor::execute(): ERROR: $e');
|
||||
return Left<Failure, Success>(GetOIDCConfigurationFailure(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart';
|
||||
@@ -35,6 +36,9 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
return AppLocalizations.of(context).requiredEmail;
|
||||
} else if (failure is LoginMissPasswordAction) {
|
||||
return AppLocalizations.of(context).requiredPassword;
|
||||
} else if (failure is GetOIDCConfigurationFailure
|
||||
|| failure is LoginCanNotVerifySSOConfigurationAction) {
|
||||
return AppLocalizations.of(context).canNotVerifySSOConfiguration;
|
||||
} else {
|
||||
return AppLocalizations.of(context).unknown_error_login_message;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:tmail_ui_user/features/login/domain/repository/authentication_re
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
|
||||
|
||||
class LoginBindings extends BaseBindings {
|
||||
@@ -32,6 +33,7 @@ class LoginBindings extends BaseBindings {
|
||||
Get.find<DynamicUrlInterceptors>(),
|
||||
Get.find<AuthorizationInterceptors>(),
|
||||
Get.find<CheckOIDCIsAvailableInteractor>(),
|
||||
Get.find<GetOIDCConfigurationInteractor>(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -56,6 +58,9 @@ class LoginBindings extends BaseBindings {
|
||||
Get.lazyPut(() => CheckOIDCIsAvailableInteractor(
|
||||
Get.find<AuthenticationOIDCRepository>(),
|
||||
));
|
||||
Get.lazyPut(() => GetOIDCConfigurationInteractor(
|
||||
Get.find<AuthenticationOIDCRepository>(),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -6,8 +6,10 @@ import 'package:get/get.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
@@ -20,6 +22,7 @@ class LoginController extends GetxController {
|
||||
final DynamicUrlInterceptors _dynamicUrlInterceptors;
|
||||
final AuthorizationInterceptors _authorizationInterceptors;
|
||||
final CheckOIDCIsAvailableInteractor _checkOIDCIsAvailableInteractor;
|
||||
final GetOIDCConfigurationInteractor _getOIDCConfigurationInteractor;
|
||||
|
||||
final TextEditingController urlInputController = TextEditingController();
|
||||
|
||||
@@ -28,6 +31,7 @@ class LoginController extends GetxController {
|
||||
this._dynamicUrlInterceptors,
|
||||
this._authorizationInterceptors,
|
||||
this._checkOIDCIsAvailableInteractor,
|
||||
this._getOIDCConfigurationInteractor,
|
||||
);
|
||||
|
||||
var loginState = LoginState(Right(LoginInitAction())).obs;
|
||||
@@ -61,13 +65,13 @@ class LoginController extends GetxController {
|
||||
|
||||
void _checkOIDCIsAvailable() async {
|
||||
final baseUri = BuildUtils.isWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
|
||||
|
||||
log('LoginController::_checkOIDCIsAvailable(): baseUri: $baseUri');
|
||||
if (baseUri == null) {
|
||||
loginState.value = LoginState(Left(LoginMissUrlAction()));
|
||||
} else {
|
||||
loginState.value = LoginState(Right(LoginLoadingAction()));
|
||||
await _checkOIDCIsAvailableInteractor
|
||||
.execute(OIDCRequest(baseUrl: baseUri.path))
|
||||
.execute(OIDCRequest(baseUrl: baseUri.origin))
|
||||
.then((response) => response.fold(
|
||||
(failure) => _showFormLoginWithCredentialAction(),
|
||||
(success) => success is CheckOIDCIsAvailableSuccess
|
||||
@@ -93,7 +97,12 @@ class LoginController extends GetxController {
|
||||
|
||||
void handleLoginPressed() {
|
||||
if (loginFormType.value == LoginFormType.ssoForm) {
|
||||
|
||||
final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
|
||||
if (baseUri != null) {
|
||||
_getOIDCConfiguration(baseUri);
|
||||
} else {
|
||||
loginState.value = LoginState(Left(LoginMissUrlAction()));
|
||||
}
|
||||
} else {
|
||||
final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
|
||||
final userName = _parseUserName(_userNameText);
|
||||
@@ -110,6 +119,30 @@ class LoginController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void _getOIDCConfiguration(Uri baseUri) async {
|
||||
loginState.value = LoginState(Right(LoginLoadingAction()));
|
||||
await _getOIDCConfigurationInteractor.execute(baseUri)
|
||||
.then((response) => response.fold(
|
||||
(failure) {
|
||||
if (failure is GetOIDCConfigurationFailure) {
|
||||
loginState.value = LoginState(Left(failure));
|
||||
} else {
|
||||
loginState.value = LoginState(Left(LoginCanNotVerifySSOConfigurationAction()));
|
||||
}
|
||||
},
|
||||
(success) {
|
||||
if (success is GetOIDCConfigurationSuccess) {
|
||||
_getOIDCConfigurationSuccess(success);
|
||||
} else {
|
||||
loginState.value = LoginState(Left(LoginCanNotVerifySSOConfigurationAction()));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) {
|
||||
loginState.value = LoginState(Right(success));
|
||||
}
|
||||
|
||||
void _loginAction(Uri baseUrl, UserName userName, Password password) async {
|
||||
loginState.value = LoginState(Right(LoginLoadingAction()));
|
||||
await _authenticationInteractor.execute(baseUrl, userName, password)
|
||||
@@ -125,11 +158,12 @@ class LoginController extends GetxController {
|
||||
pushAndPop(AppRoutes.SESSION);
|
||||
}
|
||||
|
||||
void _loginFailureAction(AuthenticationUserFailure failure) {
|
||||
void _loginFailureAction(FeatureFailure failure) {
|
||||
loginState.value = LoginState(Left(failure));
|
||||
}
|
||||
|
||||
void formatUrl(String url) {
|
||||
log('LoginController::formatUrl(): $url');
|
||||
if (url.isValid()) {
|
||||
urlInputController.text = url.removePrefix();
|
||||
}
|
||||
|
||||
@@ -41,4 +41,10 @@ class LoginMissUsernameAction extends Failure {
|
||||
class LoginMissPasswordAction extends Failure {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
@immutable
|
||||
class LoginCanNotVerifySSOConfigurationAction extends Failure {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@@ -1393,4 +1393,10 @@ class AppLocalizations {
|
||||
'Sign-in with my SSO account',
|
||||
name: 'loginInputSSOMessage');
|
||||
}
|
||||
|
||||
String get canNotVerifySSOConfiguration {
|
||||
return Intl.message(
|
||||
'Can not verify SSO configuration, please check with your system administrator',
|
||||
name: 'canNotVerifySSOConfiguration');
|
||||
}
|
||||
}
|
||||
@@ -76,4 +76,5 @@ export 'identity/identity_request_dto.dart';
|
||||
// OIDC
|
||||
export 'oidc/response/oidc_response.dart';
|
||||
export 'oidc/response/oidc_link_dto.dart';
|
||||
export 'oidc/request/oidc_request.dart';
|
||||
export 'oidc/request/oidc_request.dart';
|
||||
export 'oidc/oidc_configuration.dart';
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class OIDCConfiguration with EquatableMixin {
|
||||
static const redirectOidc = 'teammail.mobile://oauthredirect';
|
||||
static const wellKnownOpenId = '.well-known/openid-configuration';
|
||||
|
||||
final String authority;
|
||||
final String clientId;
|
||||
final String redirectUrl = redirectOidc;
|
||||
final List<String> scopes;
|
||||
|
||||
OIDCConfiguration({
|
||||
required this.authority,
|
||||
required this.clientId,
|
||||
required this.scopes
|
||||
});
|
||||
|
||||
factory OIDCConfiguration.initial() {
|
||||
return OIDCConfiguration(
|
||||
authority: 'https://auth.upn.integration-open-paas.org/auth/realms/mobile',
|
||||
clientId: 'teammail-mobile',
|
||||
scopes: ['openid', 'offline_access']
|
||||
);
|
||||
}
|
||||
|
||||
String get discoveryUrl {
|
||||
if (authority.endsWith('/')) {
|
||||
return authority + wellKnownOpenId;
|
||||
} else {
|
||||
return authority + '/' + wellKnownOpenId;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
authority,
|
||||
clientId,
|
||||
scopes
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user