TF-571 Use response from webfinger to get OIDC from authority

This commit is contained in:
Dat PHAM HOANG
2022-05-27 11:34:53 +07:00
committed by Dat H. Pham
parent ea598c20b8
commit a48cd35d4f
14 changed files with 84 additions and 50 deletions
@@ -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) {
+19 -1
View File
@@ -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": {}
}
}
-8
View File
@@ -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;
+3 -3
View File
@@ -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];