TF-571 Use response from webfinger to get OIDC from authority
This commit is contained in:
committed by
Dat H. Pham
parent
ea598c20b8
commit
a48cd35d4f
@@ -2,9 +2,9 @@
|
||||
import 'package:model/model.dart';
|
||||
|
||||
abstract class AuthenticationOIDCDataSource {
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
Future<OIDCResponse> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri);
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri, OIDCResponse oidcResponse);
|
||||
|
||||
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes);
|
||||
}
|
||||
@@ -9,19 +9,19 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
AuthenticationOIDCDataSourceImpl(this._oidcHttpClient);
|
||||
|
||||
@override
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
|
||||
Future<OIDCResponse> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
|
||||
return Future.sync(() async {
|
||||
final oidcResponse = await _oidcHttpClient.checkOIDCIsAvailable(oidcRequest);
|
||||
return oidcResponse != null;
|
||||
return oidcResponse!;
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) {
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri, OIDCResponse oidcResponse) {
|
||||
return Future.sync(() async {
|
||||
return await _oidcHttpClient.getOIDCConfiguration(baseUri);
|
||||
return await _oidcHttpClient.getOIDCConfiguration(baseUri, oidcResponse);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
class OIDCConstant {
|
||||
static String get mobileOidcClientId => 'teammail-mobile';
|
||||
static List<String> get oidcScope => ['openid', 'offline_access'];
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
class CanNotFoundOIDCAuthority implements Exception {}
|
||||
@@ -5,8 +5,10 @@ import 'package:core/core.dart';
|
||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/endpoint.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';
|
||||
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
|
||||
|
||||
class OIDCHttpClient {
|
||||
|
||||
@@ -21,7 +23,7 @@ class OIDCHttpClient {
|
||||
.generateOIDCPath(Uri.parse(oidcRequest.baseUrl))
|
||||
.withQueryParameters([
|
||||
StringQueryParameter('resource', oidcRequest.resourceUrl),
|
||||
StringQueryParameter('rel', oidcRequest.relUrl),
|
||||
StringQueryParameter('rel', OIDCRequest.relUrl),
|
||||
])
|
||||
.generateEndpointPath()
|
||||
);
|
||||
@@ -33,8 +35,16 @@ class OIDCHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) async {
|
||||
return OIDCConfiguration.initial();
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri, OIDCResponse oidcResponse) async {
|
||||
if (oidcResponse.links.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
}
|
||||
log('OIDCHttpClient::getOIDCConfiguration(): href: ${oidcResponse.links[0].href}');
|
||||
return OIDCConfiguration(
|
||||
authority: oidcResponse.links[0].href.toString(),
|
||||
clientId: OIDCConstant.mobileOidcClientId,
|
||||
scopes: OIDCConstant.oidcScope
|
||||
);
|
||||
}
|
||||
|
||||
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes) async {
|
||||
|
||||
@@ -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_response.dart';
|
||||
import 'package:model/oidc/token_oidc.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';
|
||||
@@ -10,13 +11,13 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
AuthenticationOIDCRepositoryImpl(this._oidcDataSource);
|
||||
|
||||
@override
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
|
||||
Future<OIDCResponse> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
|
||||
return _oidcDataSource.checkOIDCIsAvailable(oidcRequest);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri) {
|
||||
return _oidcDataSource.getOIDCConfiguration(baseUri);
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri, OIDCResponse oidcResponse) {
|
||||
return _oidcDataSource.getOIDCConfiguration(baseUri, oidcResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import 'package:model/model.dart';
|
||||
|
||||
abstract class AuthenticationOIDCRepository {
|
||||
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
Future<OIDCResponse> checkOIDCIsAvailable(OIDCRequest oidcRequest);
|
||||
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri);
|
||||
Future<OIDCConfiguration> getOIDCConfiguration(Uri baseUri, OIDCResponse oidcResponse);
|
||||
|
||||
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:model/oidc/response/oidc_response.dart';
|
||||
|
||||
class CheckOIDCIsAvailableSuccess extends UIState {
|
||||
final OIDCResponse oidcResponse;
|
||||
|
||||
CheckOIDCIsAvailableSuccess();
|
||||
CheckOIDCIsAvailableSuccess(this.oidcResponse);
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
List<Object> get props => [oidcResponse];
|
||||
}
|
||||
|
||||
class CheckOIDCIsAvailableFailure extends FeatureFailure {
|
||||
|
||||
@@ -13,9 +13,7 @@ class CheckOIDCIsAvailableInteractor {
|
||||
try {
|
||||
final result = await _oidcRepository.checkOIDCIsAvailable(oidcRequest);
|
||||
log('CheckOIDCIsAvailableInteractor::execute(): result: $result');
|
||||
return result
|
||||
? Right<Failure, Success>(CheckOIDCIsAvailableSuccess())
|
||||
: Left<Failure, Success>(CheckOIDCIsAvailableFailure(null));
|
||||
return Right<Failure, Success>(CheckOIDCIsAvailableSuccess(result));
|
||||
} catch (e) {
|
||||
log('CheckOIDCIsAvailableInteractor::execute(): ERROR: $e');
|
||||
return Left<Failure, Success>(CheckOIDCIsAvailableFailure(e));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:model/model.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';
|
||||
|
||||
@@ -8,9 +9,9 @@ class GetOIDCConfigurationInteractor {
|
||||
|
||||
GetOIDCConfigurationInteractor(this._oidcRepository);
|
||||
|
||||
Future<Either<Failure, Success>> execute(Uri baseUri) async {
|
||||
Future<Either<Failure, Success>> execute(Uri baseUri, OIDCResponse oidcResponse) async {
|
||||
try {
|
||||
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(baseUri);
|
||||
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(baseUri, oidcResponse);
|
||||
log('GetOIDCConfigurationInteractor::execute(): oidcConfiguration: $oidcConfiguration');
|
||||
return Right<Failure, Success>(GetOIDCConfigurationSuccess(oidcConfiguration));
|
||||
} catch (e) {
|
||||
|
||||
@@ -45,6 +45,7 @@ class LoginController extends GetxController {
|
||||
String? _urlText;
|
||||
String? _userNameText;
|
||||
String? _passwordText;
|
||||
OIDCResponse? _oidcResponse;
|
||||
|
||||
void setUrlText(String url) => _urlText = url.trim().formatURLValid();
|
||||
|
||||
@@ -75,8 +76,9 @@ class LoginController extends GetxController {
|
||||
loginState.value = LoginState(Left(LoginMissUrlAction()));
|
||||
} else {
|
||||
loginState.value = LoginState(Right(LoginLoadingAction()));
|
||||
log('LoginController::_checkOIDCIsAvailable(): origin: + ${baseUri.origin}');
|
||||
await _checkOIDCIsAvailableInteractor
|
||||
.execute(OIDCRequest(baseUrl: baseUri.origin))
|
||||
.execute(OIDCRequest(baseUrl: baseUri.origin, resourceUrl: baseUri.origin))
|
||||
.then((response) => response.fold(
|
||||
(failure) => _showFormLoginWithCredentialAction(),
|
||||
(success) => success is CheckOIDCIsAvailableSuccess
|
||||
@@ -88,6 +90,7 @@ class LoginController extends GetxController {
|
||||
void _showFormLoginWithSSOAction(CheckOIDCIsAvailableSuccess success) {
|
||||
loginState.value = LoginState(Right(success));
|
||||
loginFormType.value = LoginFormType.ssoForm;
|
||||
_oidcResponse = success.oidcResponse;
|
||||
}
|
||||
|
||||
void handleBackInCredentialForm() {
|
||||
@@ -126,22 +129,26 @@ class LoginController extends GetxController {
|
||||
|
||||
void _getOIDCConfiguration(Uri baseUri) async {
|
||||
loginState.value = LoginState(Right(LoginLoadingAction()));
|
||||
await _getOIDCConfigurationInteractor.execute(baseUri)
|
||||
if (_oidcResponse != null) {
|
||||
await _getOIDCConfigurationInteractor.execute(baseUri, _oidcResponse!)
|
||||
.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()));
|
||||
}
|
||||
}));
|
||||
(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) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2022-05-26T12:07:26.941919",
|
||||
"@@last_modified": "2022-05-27T10:11:30.148525",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -1353,5 +1353,23 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"loginInputSSOMessage": "Sign-in with my SSO account",
|
||||
"@loginInputSSOMessage": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"canNotVerifySSOConfiguration": "Can not verify SSO configuration, please check with your system administrator",
|
||||
"@canNotVerifySSOConfiguration": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"canNotGetToken": "Can not get token, please check with your system administrator",
|
||||
"@canNotGetToken": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,6 @@ class OIDCConfiguration with EquatableMixin {
|
||||
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;
|
||||
|
||||
@@ -4,10 +4,10 @@ import 'package:equatable/equatable.dart';
|
||||
class OIDCRequest with EquatableMixin {
|
||||
|
||||
final String baseUrl;
|
||||
final String resourceUrl = 'https://gateway.upn.integration-open-paas.org';
|
||||
final String relUrl = 'http://openid.net/specs/connect/1.0/issuer';
|
||||
final String resourceUrl;
|
||||
static const String relUrl = 'http://openid.net/specs/connect/1.0/issuer';
|
||||
|
||||
OIDCRequest({required this.baseUrl});
|
||||
OIDCRequest({required this.baseUrl, required this.resourceUrl});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [baseUrl, resourceUrl, relUrl];
|
||||
|
||||
Reference in New Issue
Block a user