PM-7392 - WIP - Enhancing logout callback to consider the logout reason + move show toast logic into logout callback
This commit is contained in:
parent
d0782554f2
commit
706935098b
|
@ -551,6 +551,9 @@
|
|||
"loggedOut": {
|
||||
"message": "Logged out"
|
||||
},
|
||||
"loggedOutDesc": {
|
||||
"message": "You have been logged out of your account."
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "Your login session has expired."
|
||||
},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { KeyConnectorService as AbstractKeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||
|
||||
|
@ -40,7 +41,7 @@ import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.fa
|
|||
|
||||
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
||||
keyConnectorServiceOptions: {
|
||||
logoutCallback: (expired: boolean, userId?: string) => Promise<void>;
|
||||
logoutCallback: (logoutReason: LogoutReason, userId?: string) => Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
AuthRequestService,
|
||||
LoginEmailServiceAbstraction,
|
||||
LoginEmailService,
|
||||
LogoutReason,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
|
@ -372,8 +373,8 @@ export default class MainBackground {
|
|||
}
|
||||
};
|
||||
|
||||
const logoutCallback = async (expired: boolean, userId?: UserId) =>
|
||||
await this.logout(expired, userId);
|
||||
const logoutCallback = async (logoutReason: LogoutReason, userId?: UserId) =>
|
||||
await this.logout(logoutReason, userId);
|
||||
|
||||
this.logService = new ConsoleLogService(false);
|
||||
this.cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
|
@ -579,7 +580,7 @@ export default class MainBackground {
|
|||
return Promise.resolve();
|
||||
},
|
||||
this.logService,
|
||||
(expired: boolean) => this.logout(expired),
|
||||
(logoutReason: LogoutReason) => this.logout(logoutReason),
|
||||
);
|
||||
|
||||
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
|
||||
|
@ -1209,7 +1210,8 @@ export default class MainBackground {
|
|||
}
|
||||
}
|
||||
|
||||
async logout(expired: boolean, userId?: UserId) {
|
||||
// TODO: figure out what this should look like
|
||||
async logout(logoutReason: LogoutReason, userId?: UserId) {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(
|
||||
map((a) => a?.id),
|
||||
|
@ -1279,7 +1281,6 @@ export default class MainBackground {
|
|||
this.messagingService.send("switchAccountFinish");
|
||||
} else {
|
||||
this.messagingService.send("doneLoggingOut", {
|
||||
expired: expired,
|
||||
userId: userBeingLoggedOut,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { ApiService as AbstractApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
|
||||
|
@ -26,7 +27,7 @@ import { stateServiceFactory, StateServiceInitOptions } from "./state-service.fa
|
|||
type ApiServiceFactoryOptions = FactoryOptions & {
|
||||
apiServiceOptions: {
|
||||
refreshAccessTokenErrorCallback?: () => Promise<void>;
|
||||
logoutCallback: (expired: boolean) => Promise<void>;
|
||||
logoutCallback: (logoutReason: LogoutReason) => Promise<void>;
|
||||
customUserAgent?: string;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -84,14 +84,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
tap((msg: any) => {
|
||||
if (msg.command === "doneLoggingOut") {
|
||||
this.authService.logOut(async () => {
|
||||
if (msg.expired) {
|
||||
this.toastService.showToast({
|
||||
variant: "warning",
|
||||
title: this.i18nService.t("loggedOut"),
|
||||
message: this.i18nService.t("loginExpired"),
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["home"]);
|
||||
|
|
|
@ -743,6 +743,9 @@
|
|||
"loggedOut": {
|
||||
"message": "Logged out"
|
||||
},
|
||||
"loggedOutDesc": {
|
||||
"message": "You have been logged out of your account."
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "Your login session has expired."
|
||||
},
|
||||
|
|
|
@ -584,6 +584,9 @@
|
|||
"loggedOut": {
|
||||
"message": "Logged out"
|
||||
},
|
||||
"loggedOutDesc": {
|
||||
"message": "You have been logged out of your account."
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "Your login session has expired."
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { InjectionToken } from "@angular/core";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import {
|
||||
AbstractStorageService,
|
||||
|
@ -35,7 +36,7 @@ export const MEMORY_STORAGE = new SafeInjectionToken<AbstractStorageService>("ME
|
|||
export const SECURE_STORAGE = new SafeInjectionToken<AbstractStorageService>("SECURE_STORAGE");
|
||||
export const STATE_FACTORY = new SafeInjectionToken<StateFactory>("STATE_FACTORY");
|
||||
export const LOGOUT_CALLBACK = new SafeInjectionToken<
|
||||
(expired: boolean, userId?: string) => Promise<void>
|
||||
(logoutReason: LogoutReason, userId?: string) => Promise<void>
|
||||
>("LOGOUT_CALLBACK");
|
||||
export const LOCKED_CALLBACK = new SafeInjectionToken<(userId?: string) => Promise<void>>(
|
||||
"LOCKED_CALLBACK",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ErrorHandler, LOCALE_ID, NgModule } from "@angular/core";
|
||||
import { Subject } from "rxjs";
|
||||
import { firstValueFrom, Subject } from "rxjs";
|
||||
|
||||
import {
|
||||
AuthRequestServiceAbstraction,
|
||||
|
@ -13,6 +13,7 @@ import {
|
|||
InternalUserDecryptionOptionsServiceAbstraction,
|
||||
UserDecryptionOptionsService,
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
LogoutReason,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
|
@ -117,6 +118,7 @@ import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/bill
|
|||
import { BillingApiService } from "@bitwarden/common/billing/services/billing-api.service";
|
||||
import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service";
|
||||
import { PaymentMethodWarningsService } from "@bitwarden/common/billing/services/payment-method-warnings.service";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
|
@ -234,7 +236,7 @@ import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-
|
|||
import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { ToastOptions, ToastService } from "@bitwarden/components";
|
||||
import {
|
||||
ImportApiService,
|
||||
ImportApiServiceAbstraction,
|
||||
|
@ -319,9 +321,65 @@ const safeProviders: SafeProvider[] = [
|
|||
safeProvider({
|
||||
provide: LOGOUT_CALLBACK,
|
||||
useFactory:
|
||||
(messagingService: MessagingServiceAbstraction) => (expired: boolean, userId?: string) =>
|
||||
Promise.resolve(messagingService.send("logout", { expired: expired, userId: userId })),
|
||||
deps: [MessagingServiceAbstraction],
|
||||
(
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
toastService: ToastService,
|
||||
i18nService: I18nServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
) =>
|
||||
async (logoutReason: LogoutReason, userId?: string) => {
|
||||
const isDesktop = platformUtilsService.getClientType() === ClientType.Desktop;
|
||||
|
||||
let toastOptions: ToastOptions;
|
||||
switch (logoutReason) {
|
||||
case "sessionExpired": {
|
||||
toastOptions = {
|
||||
variant: "warning",
|
||||
title: i18nService.t("loggedOut"),
|
||||
message: i18nService.t("loginExpired"),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "accessTokenUnableToBeDecrypted": {
|
||||
toastOptions = {
|
||||
variant: "error",
|
||||
title: i18nService.t("loggedOut"),
|
||||
message: i18nService.t("accessTokenUnableToBeDecrypted"),
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "refreshTokenSecureStorageRetrievalFailure": {
|
||||
toastOptions = {
|
||||
variant: "error",
|
||||
title: i18nService.t("loggedOut"),
|
||||
message: i18nService.t("refreshTokenSecureStorageRetrievalFailure"),
|
||||
};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
toastOptions = {
|
||||
variant: "error",
|
||||
title: i18nService.t("loggedOut"),
|
||||
message: i18nService.t("loggedOutDesc"),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const activeToast = toastService.showToast(toastOptions);
|
||||
if (isDesktop) {
|
||||
// Since desktop has process reload on logout, we need to wait for the toast to be hidden before triggering the logout.
|
||||
await firstValueFrom(activeToast.onHidden);
|
||||
}
|
||||
|
||||
return Promise.resolve(messagingService.send("logout", { userId: userId }));
|
||||
},
|
||||
deps: [
|
||||
MessagingServiceAbstraction,
|
||||
ToastService,
|
||||
I18nServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LOCKED_CALLBACK,
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
export type LogoutReason =
|
||||
| "invalidGrantError"
|
||||
| "vaultTimeout"
|
||||
| "invalidSecurityStamp"
|
||||
| "logoutNotification"
|
||||
| "keyConnectorError"
|
||||
| "sessionExpired"
|
||||
| "accessTokenUnableToBeDecrypted"
|
||||
| "refreshTokenSecureStorageRetrievalFailure";
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationUserType } from "../../admin-console/enums";
|
||||
|
@ -57,7 +59,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
|||
private logService: LogService,
|
||||
private organizationService: OrganizationService,
|
||||
private keyGenerationService: KeyGenerationService,
|
||||
private logoutCallback: (expired: boolean, userId?: string) => Promise<void>,
|
||||
private logoutCallback: (logoutReason: LogoutReason, userId?: string) => Promise<void>,
|
||||
private stateProvider: StateProvider,
|
||||
) {
|
||||
this.usesKeyConnectorState = this.stateProvider.getActive(USES_KEY_CONNECTOR);
|
||||
|
@ -192,7 +194,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
|||
if (this.logoutCallback != null) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.logoutCallback(false);
|
||||
this.logoutCallback("keyConnectorError");
|
||||
}
|
||||
throw new Error("Key Connector error");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service";
|
||||
import { OrganizationConnectionType } from "../admin-console/enums";
|
||||
import { OrganizationSponsorshipCreateRequest } from "../admin-console/models/request/organization/organization-sponsorship-create.request";
|
||||
|
@ -160,7 +162,7 @@ export class ApiService implements ApiServiceAbstraction {
|
|||
private stateService: StateService,
|
||||
private refreshAccessTokenErrorCallback: () => Promise<void>,
|
||||
private logService: LogService,
|
||||
private logoutCallback: (expired: boolean) => Promise<void>,
|
||||
private logoutCallback: (logoutReason: LogoutReason) => Promise<void>,
|
||||
private customUserAgent: string = null,
|
||||
) {
|
||||
this.device = platformUtilsService.getDevice();
|
||||
|
@ -1888,7 +1890,7 @@ export class ApiService implements ApiServiceAbstraction {
|
|||
responseJson != null &&
|
||||
responseJson.error === "invalid_grant")
|
||||
) {
|
||||
await this.logoutCallback(true);
|
||||
await this.logoutCallback("invalidGrantError");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import * as signalR from "@microsoft/signalr";
|
|||
import * as signalRMsgPack from "@microsoft/signalr-protocol-msgpack";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
|
||||
import { AuthRequestServiceAbstraction } from "../../../auth/src/common/abstractions";
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "../abstractions/notifications.service";
|
||||
|
@ -36,7 +38,7 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
|||
private appIdService: AppIdService,
|
||||
private apiService: ApiService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logoutCallback: (expired: boolean) => Promise<void>,
|
||||
private logoutCallback: (logoutReason: LogoutReason) => Promise<void>,
|
||||
private stateService: StateService,
|
||||
private authService: AuthService,
|
||||
private authRequestService: AuthRequestServiceAbstraction,
|
||||
|
@ -188,7 +190,7 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
|||
if (isAuthenticated) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.logoutCallback(true);
|
||||
this.logoutCallback("logoutNotification");
|
||||
}
|
||||
break;
|
||||
case NotificationType.SyncSendCreate:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { combineLatest, filter, firstValueFrom, map, switchMap, timeout } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
|
||||
import { SearchService } from "../../abstractions/search.service";
|
||||
import { VaultTimeoutSettingsService } from "../../abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../../abstractions/vault-timeout/vault-timeout.service";
|
||||
|
@ -34,7 +36,10 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private stateEventRunnerService: StateEventRunnerService,
|
||||
private lockedCallback: (userId?: string) => Promise<void> = null,
|
||||
private loggedOutCallback: (expired: boolean, userId?: string) => Promise<void> = null,
|
||||
private loggedOutCallback: (
|
||||
logoutReason: LogoutReason,
|
||||
userId?: string,
|
||||
) => Promise<void> = null,
|
||||
) {}
|
||||
|
||||
async init(checkOnInterval: boolean) {
|
||||
|
@ -145,7 +150,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||
|
||||
async logOut(userId?: string): Promise<void> {
|
||||
if (this.loggedOutCallback != null) {
|
||||
await this.loggedOutCallback(false, userId);
|
||||
await this.loggedOutCallback("vaultTimeout", userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { LogoutReason, UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
import { InternalOrganizationServiceAbstraction } from "../../../admin-console/abstractions/organization/organization.service.abstraction";
|
||||
|
@ -71,7 +71,7 @@ export class SyncService implements SyncServiceAbstraction {
|
|||
private sendApiService: SendApiService,
|
||||
private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||
private avatarService: AvatarService,
|
||||
private logoutCallback: (expired: boolean) => Promise<void>,
|
||||
private logoutCallback: (logoutReason: LogoutReason) => Promise<void>,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
private tokenService: TokenService,
|
||||
) {}
|
||||
|
@ -313,7 +313,7 @@ export class SyncService implements SyncServiceAbstraction {
|
|||
const stamp = await this.tokenService.getSecurityStamp(response.id);
|
||||
if (stamp != null && stamp !== response.securityStamp) {
|
||||
if (this.logoutCallback != null) {
|
||||
await this.logoutCallback(true);
|
||||
await this.logoutCallback("invalidSecurityStamp");
|
||||
}
|
||||
|
||||
throw new Error("Stamp has changed");
|
||||
|
|
Loading…
Reference in New Issue