diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index 25401f06f3..a39f56ee88 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -492,9 +492,11 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { } async fingerprint() { - const fingerprint = await this.cryptoService.getFingerprint( - await this.stateService.getUserId(), + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); + const publicKey = await firstValueFrom(this.cryptoService.userPublicKey$(activeUserId)); + const fingerprint = await this.cryptoService.getFingerprint(activeUserId, publicKey); const dialogRef = FingerprintDialogComponent.open(this.dialogService, { fingerprint, diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 923187bfcd..95ca61de46 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -549,7 +549,11 @@ export class GetCommand extends DownloadCommand { private async getFingerprint(id: string) { let fingerprint: string[] = null; if (id === "me") { - fingerprint = await this.cryptoService.getFingerprint(await this.stateService.getUserId()); + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); + const publicKey = await firstValueFrom(this.cryptoService.userPublicKey$(activeUserId)); + fingerprint = await this.cryptoService.getFingerprint(activeUserId, publicKey); } else if (Utils.isGuid(id)) { try { const response = await this.apiService.getUserPublicKey(id); diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index c3fd55fa8b..535eb6be83 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; import * as path from "path"; import * as jsdom from "jsdom"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { InternalUserDecryptionOptionsServiceAbstraction, @@ -121,7 +121,6 @@ import { import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service"; import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider"; import { SendService } from "@bitwarden/common/tools/send/services/send.service"; -import { UserId } from "@bitwarden/common/types/guid"; import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; @@ -772,13 +771,13 @@ export class ServiceContainer { this.authService.logOut(() => { /* Do nothing */ }); - const userId = (await this.stateService.getUserId()) as UserId; + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); await Promise.all([ - this.eventUploadService.uploadEvents(userId as UserId), + this.eventUploadService.uploadEvents(userId), this.cryptoService.clearKeys(), this.cipherService.clear(userId), this.folderService.clear(userId), - this.collectionService.clear(userId as UserId), + this.collectionService.clear(userId), ]); await this.stateEventRunnerService.handleEvent("logout", userId); diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index ebcae98a88..3f29c1583b 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -196,22 +196,23 @@ export class SettingsComponent implements OnInit, OnDestroy { async ngOnInit() { this.vaultTimeoutOptions = await this.generateVaultTimeoutOptions(); - - this.userHasMasterPassword = await this.userVerificationService.hasMasterPassword(); - - this.isWindows = (await this.platformUtilsService.getDevice()) === DeviceType.WindowsDesktop; + const activeAccount = await firstValueFrom(this.accountService.activeAccount$); this.isLinux = (await this.platformUtilsService.getDevice()) === DeviceType.LinuxDesktop; - if ((await this.stateService.getUserId()) == null) { + if (activeAccount == null || activeAccount.id == null) { return; } - this.currentUserEmail = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.email)), - ); - this.currentUserId = (await this.stateService.getUserId()) as UserId; + this.userHasMasterPassword = await this.userVerificationService.hasMasterPassword(); + + this.isWindows = this.platformUtilsService.getDevice() === DeviceType.WindowsDesktop; + + this.currentUserEmail = activeAccount.email; + this.currentUserId = activeAccount.id; this.availableVaultTimeoutActions$ = this.refreshTimeoutSettings$.pipe( - switchMap(() => this.vaultTimeoutSettingsService.availableVaultTimeoutActions$()), + switchMap(() => + this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(activeAccount.id), + ), ); // Load timeout policy @@ -236,12 +237,8 @@ export class SettingsComponent implements OnInit, OnDestroy { }), ); - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - // Load initial values - this.userHasPinSet = await this.pinService.isPinSet(userId); - - const activeAccount = await firstValueFrom(this.accountService.activeAccount$); + this.userHasPinSet = await this.pinService.isPinSet(activeAccount.id); const initialValues = { vaultTimeout: await firstValueFrom( diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 089eb1c027..90515be5d9 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -235,7 +235,8 @@ export class AppComponent implements OnInit, OnDestroy { this.modalService.closeAll(); if ( message.userId == null || - message.userId === (await this.stateService.getUserId()) + message.userId === + (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))) ) { await this.router.navigate(["lock"]); } @@ -274,9 +275,11 @@ export class AppComponent implements OnInit, OnDestroy { await this.openModal(PremiumComponent, this.premiumRef); break; case "showFingerprintPhrase": { - const fingerprint = await this.cryptoService.getFingerprint( - await this.stateService.getUserId(), + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); + const publicKey = await firstValueFrom(this.cryptoService.userPublicKey$(activeUserId)); + const fingerprint = await this.cryptoService.getFingerprint(activeUserId, publicKey); const dialogRef = FingerprintDialogComponent.open(this.dialogService, { fingerprint }); await firstValueFrom(dialogRef.closed); break; diff --git a/apps/desktop/src/app/layout/account-switcher.component.ts b/apps/desktop/src/app/layout/account-switcher.component.ts index f641d801b8..0db0cd7427 100644 --- a/apps/desktop/src/app/layout/account-switcher.component.ts +++ b/apps/desktop/src/app/layout/account-switcher.component.ts @@ -183,8 +183,11 @@ export class AccountSwitcherComponent { }): Promise<{ [userId: string]: InactiveAccount }> { const inactiveAccounts: { [userId: string]: InactiveAccount } = {}; + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); for (const userId in baseAccounts) { - if (userId == null || userId === (await this.stateService.getUserId())) { + if (userId == null || userId === activeUserId) { continue; } diff --git a/apps/desktop/src/auth/lock.component.ts b/apps/desktop/src/auth/lock.component.ts index 350512b0f3..0d7033e904 100644 --- a/apps/desktop/src/auth/lock.component.ts +++ b/apps/desktop/src/auth/lock.component.ts @@ -1,6 +1,6 @@ import { Component, NgZone, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { firstValueFrom, switchMap } from "rxjs"; +import { firstValueFrom, map, switchMap } from "rxjs"; import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; @@ -183,7 +183,7 @@ export class LockComponent extends BaseLockComponent implements OnInit, OnDestro } private async canUseBiometric() { - const userId = await this.stateService.getUserId(); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); return await ipc.platform.biometric.enabled(userId); } diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 828fe8ea3f..6bb119f6bf 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -24,7 +24,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; -import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -274,7 +273,7 @@ export class AppComponent implements OnDestroy, OnInit { await this.displayLogoutReason(logoutReason); await this.eventUploadService.uploadEvents(); - const userId = (await this.stateService.getUserId()) as UserId; + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); const logoutPromise = firstValueFrom( this.authService.authStatusFor$(userId).pipe( diff --git a/apps/web/src/app/auth/settings/account/profile.component.ts b/apps/web/src/app/auth/settings/account/profile.component.ts index 8b659e579d..70cb70c581 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.ts +++ b/apps/web/src/app/auth/settings/account/profile.component.ts @@ -1,13 +1,12 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormControl, FormGroup } from "@angular/forms"; -import { Subject, takeUntil } from "rxjs"; +import { firstValueFrom, map, Subject, takeUntil } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UpdateProfileRequest } from "@bitwarden/common/auth/models/request/update-profile.request"; import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { ChangeAvatarDialogComponent } from "./change-avatar-dialog.component"; @@ -30,8 +29,7 @@ export class ProfileComponent implements OnInit, OnDestroy { constructor( private apiService: ApiService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private stateService: StateService, + private accountService: AccountService, private dialogService: DialogService, private toastService: ToastService, ) {} @@ -39,7 +37,9 @@ export class ProfileComponent implements OnInit, OnDestroy { async ngOnInit() { this.profile = await this.apiService.getProfile(); this.loading = false; - this.fingerprintMaterial = await this.stateService.getUserId(); + this.fingerprintMaterial = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); this.formGroup.get("name").setValue(this.profile.name); this.formGroup.get("email").setValue(this.profile.email); diff --git a/apps/web/src/app/auth/settings/security/security-keys.component.ts b/apps/web/src/app/auth/settings/security/security-keys.component.ts index 8de629dc83..a8892def5c 100644 --- a/apps/web/src/app/auth/settings/security/security-keys.component.ts +++ b/apps/web/src/app/auth/settings/security/security-keys.component.ts @@ -1,8 +1,9 @@ import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DialogService } from "@bitwarden/components"; import { ApiKeyComponent } from "./api-key.component"; @@ -21,7 +22,7 @@ export class SecurityKeysComponent implements OnInit { constructor( private userVerificationService: UserVerificationService, - private stateService: StateService, + private accountService: AccountService, private apiService: ApiService, private dialogService: DialogService, ) {} @@ -31,7 +32,9 @@ export class SecurityKeysComponent implements OnInit { } async viewUserApiKey() { - const entityId = await this.stateService.getUserId(); + const entityId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); await ApiKeyComponent.open(this.dialogService, { data: { keyType: "user", @@ -47,7 +50,9 @@ export class SecurityKeysComponent implements OnInit { } async rotateUserApiKey() { - const entityId = await this.stateService.getUserId(); + const entityId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); await ApiKeyComponent.open(this.dialogService, { data: { keyType: "user", diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 1ffe5b4353..619bab8f03 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -58,6 +58,14 @@ export abstract class StateService { setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise; getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise; setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise; + + /** + * @deprecated Use `TokenService.hasAccessToken$()` or `AuthService.authStatusFor$` instead. + */ getIsAuthenticated: (options?: StorageOptions) => Promise; + + /** + * @deprecated Use `AccountService.activeAccount$` instead. + */ getUserId: (options?: StorageOptions) => Promise; } diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts index 487a2578b5..942b8ce451 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts @@ -138,8 +138,6 @@ describe("VaultTimeoutService", () => { return new BehaviorSubject(accounts[userId]?.vaultTimeout); }); - stateService.getUserId.mockResolvedValue(globalSetups?.userId); - // Set desired user active and known users on accounts service : note the only thing that matters here is that the ID are set if (globalSetups?.userId) { accountService.activeAccountSubject.next({