TF-624 Implement logout oidc on browser
This commit is contained in:
@@ -13,6 +13,8 @@ import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.d
|
|||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/log_out_oidc_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/log_out_oidc_state.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart';
|
import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart';
|
||||||
@@ -41,7 +43,7 @@ abstract class ReloadableController extends BaseController {
|
|||||||
viewState.value.fold(
|
viewState.value.fold(
|
||||||
(failure) {
|
(failure) {
|
||||||
if (failure is GetCredentialFailure) {
|
if (failure is GetCredentialFailure) {
|
||||||
goToLogin();
|
_goToLogin();
|
||||||
} else if (failure is GetSessionFailure) {
|
} else if (failure is GetSessionFailure) {
|
||||||
_handleGetSessionFailure();
|
_handleGetSessionFailure();
|
||||||
} else if (failure is LogoutOidcFailure) {
|
} else if (failure is LogoutOidcFailure) {
|
||||||
@@ -71,8 +73,8 @@ abstract class ReloadableController extends BaseController {
|
|||||||
consumeState(_getCredentialInteractor.execute().asStream());
|
consumeState(_getCredentialInteractor.execute().asStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToLogin() {
|
void _goToLogin({LoginArguments? arguments}) {
|
||||||
pushAndPopAll(AppRoutes.LOGIN);
|
pushAndPopAll(AppRoutes.LOGIN, arguments: arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setUpInterceptors(GetCredentialViewState credentialViewState) {
|
void _setUpInterceptors(GetCredentialViewState credentialViewState) {
|
||||||
@@ -92,13 +94,12 @@ abstract class ReloadableController extends BaseController {
|
|||||||
consumeState(_getSessionInteractor.execute().asStream());
|
consumeState(_getSessionInteractor.execute().asStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGetSessionFailure() {
|
void _handleGetSessionFailure() async {
|
||||||
_deleteCredentialAction();
|
await Future.wait([
|
||||||
goToLogin();
|
_deleteCredentialInteractor.execute(),
|
||||||
}
|
_cachingManager.clearAll(),
|
||||||
|
]);
|
||||||
void _deleteCredentialAction() async {
|
_goToLogin();
|
||||||
await _deleteCredentialInteractor.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGetSessionSuccess(GetSessionSuccess success) {
|
void _handleGetSessionSuccess(GetSessionSuccess success) {
|
||||||
@@ -118,7 +119,8 @@ abstract class ReloadableController extends BaseController {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
_authorizationInterceptors.clear();
|
_authorizationInterceptors.clear();
|
||||||
goToLogin();
|
await HiveCacheConfig().closeHive();
|
||||||
|
_goToLogin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +133,7 @@ abstract class ReloadableController extends BaseController {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
_authorizationInterceptors.clear();
|
_authorizationInterceptors.clear();
|
||||||
goToLogin();
|
await HiveCacheConfig().closeHive();
|
||||||
|
_goToLogin(arguments: LoginArguments(LoginFormType.ssoForm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,15 +99,11 @@ class AccountCacheClient extends HiveCacheClient<AccountCache> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<AccountCache>> openBox() {
|
Future<Box<AccountCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<AccountCache>(tableName);
|
||||||
return Hive.box<AccountCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<AccountCache>(tableName);
|
||||||
return await Hive.openBox<AccountCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -11,15 +11,11 @@ class EmailCacheClient extends HiveCacheClient<EmailCache> {
|
|||||||
String get tableName => 'EmailCache';
|
String get tableName => 'EmailCache';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<EmailCache>> openBox() {
|
Future<Box<EmailCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<EmailCache>(tableName);
|
||||||
return Hive.box<EmailCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<EmailCache>(tableName);
|
||||||
return await Hive.openBox<EmailCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ class MailboxCacheClient extends HiveCacheClient<MailboxCache> {
|
|||||||
String get tableName => 'MailboxCache';
|
String get tableName => 'MailboxCache';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<MailboxCache>> openBox() {
|
Future<Box<MailboxCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<MailboxCache>(tableName);
|
||||||
return Hive.box<MailboxCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<MailboxCache>(tableName);
|
||||||
return await Hive.openBox<MailboxCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -88,15 +88,11 @@ class RecentSearchCacheClient extends HiveCacheClient<RecentSearchCache> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<RecentSearchCache>> openBox() {
|
Future<Box<RecentSearchCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<RecentSearchCache>(tableName);
|
||||||
return Hive.box<RecentSearchCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<RecentSearchCache>(tableName);
|
||||||
return await Hive.openBox<RecentSearchCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ class StateCacheClient extends HiveCacheClient<StateCache> {
|
|||||||
String get tableName => 'StateCache';
|
String get tableName => 'StateCache';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<StateCache>> openBox() {
|
Future<Box<StateCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<StateCache>(tableName);
|
||||||
return Hive.box<StateCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<StateCache>(tableName);
|
||||||
return await Hive.openBox<StateCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -97,15 +97,11 @@ class TokenOidcCacheClient extends HiveCacheClient<TokenOidcCache> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Box<TokenOidcCache>> openBox() {
|
Future<Box<TokenOidcCache>> openBox() async {
|
||||||
return Future.sync(() async {
|
if (Hive.isBoxOpen(tableName)) {
|
||||||
if (Hive.isBoxOpen(tableName)) {
|
return Hive.box<TokenOidcCache>(tableName);
|
||||||
return Hive.box<TokenOidcCache>(tableName);
|
}
|
||||||
}
|
return Hive.openBox<TokenOidcCache>(tableName);
|
||||||
return await Hive.openBox<TokenOidcCache>(tableName);
|
|
||||||
}).catchError((error) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash) async {
|
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash) {
|
||||||
return _tokenOidcCacheManager.getTokenOidc(tokenIdHash);
|
return _tokenOidcCacheManager.getTokenOidc(tokenIdHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> persistTokenOIDC(TokenOIDC tokenOidc) async {
|
Future<void> persistTokenOIDC(TokenOIDC tokenOidc) {
|
||||||
return _tokenOidcCacheManager.persistOneTokenOidc(tokenOidc);
|
return _tokenOidcCacheManager.persistOneTokenOidc(tokenOidc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ class AuthenticationClientMobile implements AuthenticationClientBase {
|
|||||||
Future<bool> logoutOidc(TokenId tokenId, OIDCConfiguration config) async {
|
Future<bool> logoutOidc(TokenId tokenId, OIDCConfiguration config) async {
|
||||||
final endSession = await _appAuth.endSession(EndSessionRequest(
|
final endSession = await _appAuth.endSession(EndSessionRequest(
|
||||||
idTokenHint: tokenId.uuid,
|
idTokenHint: tokenId.uuid,
|
||||||
postLogoutRedirectUrl: config.redirectUrl,
|
postLogoutRedirectUrl: config.logoutRedirectUrl,
|
||||||
discoveryUrl: config.discoveryUrl
|
discoveryUrl: config.discoveryUrl
|
||||||
));
|
));
|
||||||
log('AuthenticationClientMobile::logoutOidc(): ${endSession?.state}');
|
log('AuthenticationClientMobile::logoutOidc(): ${endSession?.state}');
|
||||||
|
|||||||
+7
-2
@@ -44,8 +44,13 @@ class AuthenticationClientWeb implements AuthenticationClientBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> logoutOidc(TokenId tokenId, OIDCConfiguration config) {
|
Future<bool> logoutOidc(TokenId tokenId, OIDCConfiguration config) async {
|
||||||
throw UnimplementedError();
|
final endSession = await _appAuthWeb.endSession(EndSessionRequest(
|
||||||
|
idTokenHint: tokenId.uuid,
|
||||||
|
postLogoutRedirectUrl: config.logoutRedirectUrl,
|
||||||
|
discoveryUrl: config.discoveryUrl
|
||||||
|
));
|
||||||
|
return endSession != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class LogoutOidcInteractor {
|
|||||||
Stream<Either<Failure, Success>> execute() async* {
|
Stream<Either<Failure, Success>> execute() async* {
|
||||||
try {
|
try {
|
||||||
final currentAccount = await _accountRepository.getCurrentAccount();
|
final currentAccount = await _accountRepository.getCurrentAccount();
|
||||||
|
log('LogoutOidcInteractor::execute(): currentAccount: $currentAccount');
|
||||||
if (currentAccount.authenticationType == AuthenticationType.oidc) {
|
if (currentAccount.authenticationType == AuthenticationType.oidc) {
|
||||||
final result = await Future.wait([
|
final result = await Future.wait([
|
||||||
_authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id),
|
_authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id),
|
||||||
@@ -29,6 +29,8 @@ class LogoutOidcInteractor {
|
|||||||
]).then((result) async {
|
]).then((result) async {
|
||||||
final tokenOidc = result.first as TokenOIDC;
|
final tokenOidc = result.first as TokenOIDC;
|
||||||
final oidcConfig = result.last as OIDCConfiguration;
|
final oidcConfig = result.last as OIDCConfiguration;
|
||||||
|
log('LogoutOidcInteractor::execute(): tokenOidc: ${tokenOidc.tokenId.uuid}');
|
||||||
|
log('LogoutOidcInteractor::execute(): oidcConfig: $oidcConfig');
|
||||||
return await _authenticationOIDCRepository.logout(tokenOidc.tokenId, oidcConfig);
|
return await _authenticationOIDCRepository.logout(tokenOidc.tokenId, oidcConfig);
|
||||||
});
|
});
|
||||||
log('LogoutOidcInteractor::execute(): statusSuccess: $result');
|
log('LogoutOidcInteractor::execute(): statusSuccess: $result');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:equatable/equatable.dart';
|
|||||||
class OIDCConfiguration with EquatableMixin {
|
class OIDCConfiguration with EquatableMixin {
|
||||||
static const redirectOidcMobile = 'teammail.mobile://oauthredirect';
|
static const redirectOidcMobile = 'teammail.mobile://oauthredirect';
|
||||||
static const redirectOidcWeb = 'http://localhost:3000/login-callback.html';
|
static const redirectOidcWeb = 'http://localhost:3000/login-callback.html';
|
||||||
|
static const logoutRedirectOidcWeb = 'http://localhost:3000/logout-callback.html';
|
||||||
static const wellKnownOpenId = '.well-known/openid-configuration';
|
static const wellKnownOpenId = '.well-known/openid-configuration';
|
||||||
|
|
||||||
final String authority;
|
final String authority;
|
||||||
@@ -27,6 +28,8 @@ class OIDCConfiguration with EquatableMixin {
|
|||||||
|
|
||||||
String get redirectUrl => BuildUtils.isWeb ? redirectOidcWeb : redirectOidcMobile;
|
String get redirectUrl => BuildUtils.isWeb ? redirectOidcWeb : redirectOidcMobile;
|
||||||
|
|
||||||
|
String get logoutRedirectUrl => BuildUtils.isWeb ? logoutRedirectOidcWeb : redirectOidcMobile;
|
||||||
|
|
||||||
String get clientIdHash => clientId.hashCode.toString();
|
String get clientIdHash => clientId.hashCode.toString();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
const AUTH_RESPONSE_KEY = "auth_info";
|
||||||
|
const CODE_VERIFIER_KEY = "auth_code_verifier";
|
||||||
|
window.onload = function () {
|
||||||
|
localStorage.removeItem(AUTH_RESPONSE_KEY);
|
||||||
|
localStorage.removeItem(CODE_VERIFIER_KEY);
|
||||||
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
if (params.has('redirect_uri')) {
|
||||||
|
const redirect = params.get('redirect_uri');
|
||||||
|
window.location.replace(redirect);
|
||||||
|
} else {
|
||||||
|
window.location.assign("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user