TF-4049 Pre-fill OIDC login form

This commit is contained in:
dab246
2025-10-13 14:40:25 +07:00
committed by Dat H. Pham
parent 7d80557582
commit 51b85c8b79
12 changed files with 112 additions and 30 deletions
@@ -8,7 +8,13 @@ abstract class AuthenticationOIDCDataSource {
Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration); Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration);
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes); Future<TokenOIDC> getTokenOIDC(
String clientId,
String redirectUrl,
String discoveryUrl,
List<String> scopes, {
String? loginHint,
});
Future<void> persistTokenOIDC(TokenOIDC tokenOidc); Future<void> persistTokenOIDC(TokenOIDC tokenOidc);
@@ -56,9 +56,21 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
} }
@override @override
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes) { Future<TokenOIDC> getTokenOIDC(
String clientId,
String redirectUrl,
String discoveryUrl,
List<String> scopes, {
String? loginHint,
}) {
return Future.sync(() async { return Future.sync(() async {
return await _authenticationClient.getTokenOIDC(clientId, redirectUrl, discoveryUrl, scopes); return await _authenticationClient.getTokenOIDC(
clientId,
redirectUrl,
discoveryUrl,
scopes,
loginHint: loginHint,
);
}).catchError(_exceptionThrower.throwException); }).catchError(_exceptionThrower.throwException);
} }
@@ -13,10 +13,12 @@ abstract class AuthenticationClientBase {
List<String> scopes); List<String> scopes);
Future<TokenOIDC> getTokenOIDC( Future<TokenOIDC> getTokenOIDC(
String clientId, String clientId,
String redirectUrl, String redirectUrl,
String discoveryUrl, String discoveryUrl,
List<String> scopes); List<String> scopes, {
String? loginHint,
});
Future<TokenOIDC> refreshingTokensOIDC( Future<TokenOIDC> refreshingTokensOIDC(
String clientId, String clientId,
@@ -59,14 +59,16 @@ mixin AuthenticationClientInteractionMixin {
String clientId, String clientId,
String redirectUrl, String redirectUrl,
String discoveryUrl, String discoveryUrl,
List<String> scopes, List<String> scopes, {
) { String? loginHint,
}) {
return AuthorizationTokenRequest( return AuthorizationTokenRequest(
clientId, clientId,
redirectUrl, redirectUrl,
discoveryUrl: discoveryUrl, discoveryUrl: discoveryUrl,
scopes: scopes, scopes: scopes,
externalUserAgent: getExternalUserAgent(), externalUserAgent: getExternalUserAgent(),
loginHint: loginHint,
); );
} }
@@ -27,13 +27,15 @@ class AuthenticationClientMobile with AuthenticationClientInteractionMixin
String clientId, String clientId,
String redirectUrl, String redirectUrl,
String discoveryUrl, String discoveryUrl,
List<String> scopes, List<String> scopes, {
) async { String? loginHint,
}) async {
final authorizationTokenRequest = getAuthorizationTokenRequest( final authorizationTokenRequest = getAuthorizationTokenRequest(
clientId, clientId,
redirectUrl, redirectUrl,
discoveryUrl, discoveryUrl,
scopes, scopes,
loginHint: loginHint,
); );
final authorizationTokenResponse = await _appAuth.authorizeAndExchangeCode( final authorizationTokenResponse = await _appAuth.authorizeAndExchangeCode(
authorizationTokenRequest, authorizationTokenRequest,
@@ -25,13 +25,15 @@ class AuthenticationClientWeb with AuthenticationClientInteractionMixin
String clientId, String clientId,
String redirectUrl, String redirectUrl,
String discoveryUrl, String discoveryUrl,
List<String> scopes, List<String> scopes, {
) async { String? loginHint,
}) async {
final authorizationTokenRequest = getAuthorizationTokenRequest( final authorizationTokenRequest = getAuthorizationTokenRequest(
clientId, clientId,
redirectUrl, redirectUrl,
discoveryUrl, discoveryUrl,
scopes, scopes,
loginHint: loginHint,
); );
final authorizationTokenResponse = await _appAuthWeb.authorizeAndExchangeCode( final authorizationTokenResponse = await _appAuthWeb.authorizeAndExchangeCode(
authorizationTokenRequest, authorizationTokenRequest,
@@ -28,8 +28,20 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
} }
@override @override
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes) { Future<TokenOIDC> getTokenOIDC(
return _oidcDataSource.getTokenOIDC(clientId, redirectUrl, discoveryUrl, scopes); String clientId,
String redirectUrl,
String discoveryUrl,
List<String> scopes, {
String? loginHint,
}) {
return _oidcDataSource.getTokenOIDC(
clientId,
redirectUrl,
discoveryUrl,
scopes,
loginHint: loginHint,
);
} }
@override @override
@@ -8,7 +8,13 @@ abstract class AuthenticationOIDCRepository {
Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration); Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration);
Future<TokenOIDC> getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List<String> scopes); Future<TokenOIDC> getTokenOIDC(
String clientId,
String redirectUrl,
String discoveryUrl,
List<String> scopes, {
String? loginHint,
});
Future<void> persistTokenOIDC(TokenOIDC tokenOidc); Future<void> persistTokenOIDC(TokenOIDC tokenOidc);
@@ -2,6 +2,7 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:model/oidc/oidc_configuration.dart';
import 'package:model/oidc/response/oidc_response.dart'; import 'package:model/oidc/response/oidc_response.dart';
import 'package:tmail_ui_user/features/login/domain/model/base_url_oidc_response.dart'; import 'package:tmail_ui_user/features/login/domain/model/base_url_oidc_response.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
@@ -12,12 +13,20 @@ class GetOIDCConfigurationInteractor {
GetOIDCConfigurationInteractor(this._oidcRepository); GetOIDCConfigurationInteractor(this._oidcRepository);
Stream<Either<Failure, Success>> execute(OIDCResponse oidcResponse) async* { Stream<Either<Failure, Success>> execute(
OIDCResponse oidcResponse, {
String? loginHint,
}) async* {
try { try {
yield Right<Failure, Success>(GetOIDCConfigurationLoading()); yield Right<Failure, Success>(GetOIDCConfigurationLoading());
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse); final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse);
await _oidcRepository.persistOidcConfiguration(oidcConfiguration); final configWithLoginHint = oidcConfiguration.copyWidth(
yield Right<Failure, Success>(GetOIDCConfigurationSuccess(oidcConfiguration)); loginHint: loginHint,
);
await _oidcRepository.persistOidcConfiguration(configWithLoginHint);
yield Right<Failure, Success>(
GetOIDCConfigurationSuccess(configWithLoginHint),
);
} catch (e) { } catch (e) {
logError('$runtimeType::execute():oidcResponse = ${oidcResponse.runtimeType} | Exception = $e'); logError('$runtimeType::execute():oidcResponse = ${oidcResponse.runtimeType} | Exception = $e');
if (oidcResponse is BaseUrlOidcResponse) { if (oidcResponse is BaseUrlOidcResponse) {
@@ -27,10 +27,12 @@ class GetTokenOIDCInteractor {
try { try {
yield Right<Failure, Success>(GetTokenOIDCLoading()); yield Right<Failure, Success>(GetTokenOIDCLoading());
final tokenOIDC = await authenticationOIDCRepository.getTokenOIDC( final tokenOIDC = await authenticationOIDCRepository.getTokenOIDC(
config.clientId, config.clientId,
config.redirectUrl, config.redirectUrl,
config.discoveryUrl, config.discoveryUrl,
config.scopes); config.scopes,
loginHint: config.loginHint,
);
await Future.wait([ await Future.wait([
_credentialRepository.saveBaseUrl(baseUri), _credentialRepository.saveBaseUrl(baseUri),
@@ -188,7 +188,7 @@ class LoginController extends ReloadableController {
} else if (success is CheckOIDCIsAvailableSuccess) { } else if (success is CheckOIDCIsAvailableSuccess) {
getOIDCConfiguration(success.oidcResponse); getOIDCConfiguration(success.oidcResponse);
} else if (success is GetOIDCConfigurationSuccess) { } else if (success is GetOIDCConfigurationSuccess) {
_getOIDCConfigurationSuccess(success); _getOIDCConfigurationSuccess(success.oidcConfiguration);
} else if (success is GetTokenOIDCSuccess) { } else if (success is GetTokenOIDCSuccess) {
_getTokenOIDCSuccess(success); _getTokenOIDCSuccess(success);
} else if (success is AuthenticationUserSuccess) { } else if (success is AuthenticationUserSuccess) {
@@ -413,16 +413,23 @@ class LoginController extends ReloadableController {
} }
void getOIDCConfiguration(OIDCResponse oidcResponse) { void getOIDCConfiguration(OIDCResponse oidcResponse) {
consumeState(_getOIDCConfigurationInteractor.execute(oidcResponse)); final loginHint = PlatformInfo.isMobile ? _username?.value : null;
log('$runtimeType::getOIDCConfiguration:loginHint = $loginHint');
consumeState(
_getOIDCConfigurationInteractor.execute(
oidcResponse,
loginHint: loginHint,
),
);
} }
void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) { void _getOIDCConfigurationSuccess(OIDCConfiguration config) {
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
_authenticateOidcOnBrowserAction(success.oidcConfiguration); _authenticateOidcOnBrowserAction(config);
} else if (success.oidcConfiguration.authority == AppConfig.saasRegistrationUrl) { } else if (config.authority == AppConfig.saasRegistrationUrl) {
_getTokenOIDCOnSaaSPlatform(success.oidcConfiguration); _getTokenOIDCOnSaaSPlatform(config);
} else { } else {
_getTokenOIDCAction(success.oidcConfiguration); _getTokenOIDCAction(config);
} }
} }
+20
View File
@@ -8,12 +8,14 @@ class OIDCConfiguration with EquatableMixin {
final String clientId; final String clientId;
final List<String> scopes; final List<String> scopes;
final bool isTWP; final bool isTWP;
final String? loginHint;
OIDCConfiguration({ OIDCConfiguration({
required this.authority, required this.authority,
required this.clientId, required this.clientId,
required this.scopes, required this.scopes,
this.isTWP = false, this.isTWP = false,
this.loginHint,
}); });
String get discoveryUrl { String get discoveryUrl {
@@ -30,5 +32,23 @@ class OIDCConfiguration with EquatableMixin {
clientId, clientId,
scopes, scopes,
isTWP, isTWP,
loginHint,
]; ];
} }
extension OIDCConfigurationExtension on OIDCConfiguration {
OIDCConfiguration copyWidth({
String? authority,
String? clientId,
List<String>? scopes,
bool? isTWP,
String? loginHint,
}) =>
OIDCConfiguration(
authority: authority ?? this.authority,
clientId: clientId ?? this.clientId,
scopes: scopes ?? this.scopes,
isTWP: isTWP ?? this.isTWP,
loginHint: loginHint ?? this.loginHint,
);
}