1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +00:00

[PM-5362]Create MP Service for state provider migration (#7623)

* create mp and kdf service

* update mp service interface to not rely on active user

* rename observable methods

* update crypto service with new MP service

* add master password service to login strategies
- make fake service for easier testing
- fix crypto service tests

* update auth service and finish strategies

* auth request refactors

* more service refactors and constructor updates

* setMasterKey refactors

* remove master key methods from crypto service

* remove master key and hash from state service

* missed fixes

* create migrations and fix references

* fix master key imports

* default force set password reason to none

* add password reset reason observable factory to service

* remove kdf changes and migrate only disk data

* update migration number

* fix sync service deps

* use disk for force set password state

* fix desktop migration

* fix sso test

* fix tests

* fix more tests

* fix even more tests

* fix even more tests

* fix cli

* remove kdf service abstraction

* add missing deps for browser

* fix merge conflicts

* clear reset password reason on lock or logout

* fix tests

* fix other tests

* add jsdocs to abstraction

* use state provider in crypto service

* inverse master password service factory

* add clearOn to master password service

* add parameter validation to master password service

* add component level userId

* add missed userId

* migrate key hash

* fix login strategy service

* delete crypto master key from account

* migrate master key encrypted user key

* rename key hash to master key hash

* use mp service for getMasterKeyEncryptedUserKey

* fix tests
This commit is contained in:
Jake Fink
2024-04-04 10:22:41 -04:00
committed by GitHub
parent df25074bdf
commit b1abfb0a5c
79 changed files with 1340 additions and 498 deletions

View File

@@ -17,18 +17,21 @@ import {
FactoryOptions,
factory,
} from "../../../platform/background/service-factories/factory-options";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
type AuthRequestServiceFactoryOptions = FactoryOptions;
export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions &
AppIdServiceInitOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
StateServiceInitOptions;
ApiServiceInitOptions;
export function authRequestServiceFactory(
cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices,
@@ -41,9 +44,10 @@ export function authRequestServiceFactory(
async () =>
new AuthRequestService(
await appIdServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
),
);
}

View File

@@ -31,6 +31,11 @@ import {
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
type KeyConnectorServiceFactoryOptions = FactoryOptions & {
@@ -40,6 +45,8 @@ type KeyConnectorServiceFactoryOptions = FactoryOptions & {
};
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
TokenServiceInitOptions &
@@ -58,6 +65,8 @@ export function keyConnectorServiceFactory(
opts,
async () =>
new KeyConnectorService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),

View File

@@ -59,6 +59,7 @@ import {
PasswordStrengthServiceInitOptions,
} from "../../../tools/background/service_factories/password-strength-service.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
authRequestServiceFactory,
AuthRequestServiceInitOptions,
@@ -71,6 +72,10 @@ import {
keyConnectorServiceFactory,
KeyConnectorServiceInitOptions,
} from "./key-connector-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { tokenServiceFactory, TokenServiceInitOptions } from "./token-service.factory";
import { twoFactorServiceFactory, TwoFactorServiceInitOptions } from "./two-factor-service.factory";
import {
@@ -81,6 +86,8 @@ import {
type LoginStrategyServiceFactoryOptions = FactoryOptions;
export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CryptoServiceInitOptions &
ApiServiceInitOptions &
TokenServiceInitOptions &
@@ -111,6 +118,8 @@ export function loginStrategyServiceFactory(
opts,
async () =>
new LoginStrategyService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),

View File

@@ -0,0 +1,42 @@
import {
InternalMasterPasswordServiceAbstraction,
MasterPasswordServiceAbstraction,
} from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
import {
CachedServices,
factory,
FactoryOptions,
} from "../../../platform/background/service-factories/factory-options";
import {
stateProviderFactory,
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
type MasterPasswordServiceFactoryOptions = FactoryOptions;
export type MasterPasswordServiceInitOptions = MasterPasswordServiceFactoryOptions &
StateProviderInitOptions;
export function internalMasterPasswordServiceFactory(
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
opts: MasterPasswordServiceInitOptions,
): Promise<InternalMasterPasswordServiceAbstraction> {
return factory(
cache,
"masterPasswordService",
opts,
async () => new MasterPasswordService(await stateProviderFactory(cache, opts)),
);
}
export async function masterPasswordServiceFactory(
cache: { masterPasswordService?: InternalMasterPasswordServiceAbstraction } & CachedServices,
opts: MasterPasswordServiceInitOptions,
): Promise<MasterPasswordServiceAbstraction> {
return (await internalMasterPasswordServiceFactory(
cache,
opts,
)) as MasterPasswordServiceAbstraction;
}

View File

@@ -31,6 +31,11 @@ import {
stateServiceFactory,
} from "../../../platform/background/service-factories/state-service.factory";
import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "./master-password-service.factory";
import { PinCryptoServiceInitOptions, pinCryptoServiceFactory } from "./pin-crypto-service.factory";
import {
userDecryptionOptionsServiceFactory,
@@ -46,6 +51,8 @@ type UserVerificationServiceFactoryOptions = FactoryOptions;
export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions &
StateServiceInitOptions &
CryptoServiceInitOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
I18nServiceInitOptions &
UserVerificationApiServiceInitOptions &
UserDecryptionOptionsServiceInitOptions &
@@ -66,6 +73,8 @@ export function userVerificationServiceFactory(
new UserVerificationService(
await stateServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await userVerificationApiServiceFactory(cache, opts),
await userDecryptionOptionsServiceFactory(cache, opts),

View File

@@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -41,6 +42,7 @@ export class LockComponent extends BaseLockComponent {
fido2PopoutSessionData$ = fido2PopoutSessionData$();
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
@@ -66,6 +68,7 @@ export class LockComponent extends BaseLockComponent {
accountService: AccountService,
) {
super(
masterPasswordService,
router,
i18nService,
platformUtilsService,

View File

@@ -1,65 +1,9 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/auth/components/set-password.component";
import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-set-password",
templateUrl: "set-password.component.html",
})
export class SetPasswordComponent extends BaseSetPasswordComponent {
constructor(
apiService: ApiService,
i18nService: I18nService,
cryptoService: CryptoService,
messagingService: MessagingService,
stateService: StateService,
passwordGenerationService: PasswordGenerationServiceAbstraction,
platformUtilsService: PlatformUtilsService,
policyApiService: PolicyApiServiceAbstraction,
policyService: PolicyService,
router: Router,
syncService: SyncService,
route: ActivatedRoute,
organizationApiService: OrganizationApiServiceAbstraction,
organizationUserService: OrganizationUserService,
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
dialogService: DialogService,
) {
super(
i18nService,
cryptoService,
messagingService,
passwordGenerationService,
platformUtilsService,
policyApiService,
policyService,
router,
apiService,
syncService,
route,
stateService,
organizationApiService,
organizationUserService,
userDecryptionOptionsService,
ssoLoginService,
dialogService,
);
}
}
export class SetPasswordComponent extends BaseSetPasswordComponent {}

View File

@@ -9,7 +9,9 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -45,7 +47,9 @@ export class SsoComponent extends BaseSsoComponent {
logService: LogService,
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
configService: ConfigService,
protected authService: AuthService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
private authService: AuthService,
@Inject(WINDOW) private win: Window,
) {
super(
@@ -63,6 +67,8 @@ export class SsoComponent extends BaseSsoComponent {
logService,
userDecryptionOptionsService,
configService,
masterPasswordService,
accountService,
);
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {

View File

@@ -11,6 +11,8 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -62,6 +64,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
configService: ConfigService,
ssoLoginService: SsoLoginServiceAbstraction,
private dialogService: DialogService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
@Inject(WINDOW) protected win: Window,
private browserMessagingApi: ZonedMessageListenerService,
) {
@@ -82,6 +86,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
userDecryptionOptionsService,
ssoLoginService,
configService,
masterPasswordService,
accountService,
);
super.onSuccessfulLogin = async () => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@@ -32,6 +32,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
@@ -46,6 +47,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
@@ -242,6 +244,7 @@ export default class MainBackground {
keyGenerationService: KeyGenerationServiceAbstraction;
cryptoService: CryptoServiceAbstraction;
cryptoFunctionService: CryptoFunctionServiceAbstraction;
masterPasswordService: InternalMasterPasswordServiceAbstraction;
tokenService: TokenServiceAbstraction;
appIdService: AppIdServiceAbstraction;
apiService: ApiServiceAbstraction;
@@ -480,8 +483,11 @@ export default class MainBackground {
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
this.masterPasswordService = new MasterPasswordService(this.stateProvider);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
this.cryptoService = new BrowserCryptoService(
this.masterPasswordService,
this.keyGenerationService,
this.cryptoFunctionService,
this.encryptService,
@@ -525,6 +531,8 @@ export default class MainBackground {
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
this.keyConnectorService = new KeyConnectorService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@@ -578,9 +586,10 @@ export default class MainBackground {
this.authRequestService = new AuthRequestService(
this.appIdService,
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.stateService,
);
this.authService = new AuthService(
@@ -597,6 +606,8 @@ export default class MainBackground {
);
this.loginStrategyService = new LoginStrategyService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@@ -672,6 +683,8 @@ export default class MainBackground {
this.userVerificationService = new UserVerificationService(
this.stateService,
this.cryptoService,
this.accountService,
this.masterPasswordService,
this.i18nService,
this.userVerificationApiService,
this.userDecryptionOptionsService,
@@ -694,6 +707,8 @@ export default class MainBackground {
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
this.vaultTimeoutService = new VaultTimeoutService(
this.accountService,
this.masterPasswordService,
this.cipherService,
this.folderService,
this.collectionService,
@@ -729,6 +744,8 @@ export default class MainBackground {
this.providerService = new ProviderService(this.stateProvider);
this.syncService = new SyncService(
this.masterPasswordService,
this.accountService,
this.apiService,
this.domainSettingsService,
this.folderService,
@@ -878,6 +895,8 @@ export default class MainBackground {
this.fido2Service,
);
this.nativeMessagingBackground = new NativeMessagingBackground(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.cryptoFunctionService,
this.runtimeBackground,
@@ -1106,7 +1125,7 @@ export default class MainBackground {
const status = await this.authService.getAuthStatus(userId);
const forcePasswordReset =
(await this.stateService.getForceSetPasswordReason({ userId: userId })) !=
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
ForceSetPasswordReason.None;
await this.systemService.clearPendingClipboard();

View File

@@ -1,6 +1,8 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -71,6 +73,8 @@ export class NativeMessagingBackground {
private validatingFingerprint: boolean;
constructor(
private accountService: AccountService,
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private cryptoService: CryptoService,
private cryptoFunctionService: CryptoFunctionService,
private runtimeBackground: RuntimeBackground,
@@ -336,10 +340,14 @@ export class NativeMessagingBackground {
) as UserKey;
await this.cryptoService.setUserKey(userKey);
} else if (message.keyB64) {
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
// Backwards compatibility to support cases in which the user hasn't updated their desktop app
// TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472)
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey();
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
const encUserKey =
encUserKeyPrim != null
? new EncString(encUserKeyPrim)
: await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId);
if (!encUserKey) {
throw new Error("No encrypted user key found");
}
@@ -348,9 +356,9 @@ export class NativeMessagingBackground {
) as MasterKey;
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(
masterKey,
new EncString(encUserKey),
encUserKey,
);
await this.cryptoService.setMasterKey(masterKey);
await this.masterPasswordService.setMasterKey(masterKey, userId);
await this.cryptoService.setUserKey(userKey);
} else {
throw new Error("No key received");

View File

@@ -1,9 +1,17 @@
import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import {
accountServiceFactory,
AccountServiceInitOptions,
} from "../../auth/background/service-factories/account-service.factory";
import {
authServiceFactory,
AuthServiceInitOptions,
} from "../../auth/background/service-factories/auth-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "../../auth/background/service-factories/master-password-service.factory";
import {
CryptoServiceInitOptions,
cryptoServiceFactory,
@@ -57,6 +65,8 @@ type VaultTimeoutServiceFactoryOptions = FactoryOptions & {
};
export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
AccountServiceInitOptions &
MasterPasswordServiceInitOptions &
CipherServiceInitOptions &
FolderServiceInitOptions &
CollectionServiceInitOptions &
@@ -79,6 +89,8 @@ export function vaultTimeoutServiceFactory(
opts,
async () =>
new VaultTimeoutService(
await accountServiceFactory(cache, opts),
await internalMasterPasswordServiceFactory(cache, opts),
await cipherServiceFactory(cache, opts),
await folderServiceFactory(cache, opts),
await collectionServiceFactory(cache, opts),

View File

@@ -4,6 +4,10 @@ import {
AccountServiceInitOptions,
accountServiceFactory,
} from "../../../auth/background/service-factories/account-service.factory";
import {
internalMasterPasswordServiceFactory,
MasterPasswordServiceInitOptions,
} from "../../../auth/background/service-factories/master-password-service.factory";
import {
StateServiceInitOptions,
stateServiceFactory,
@@ -34,6 +38,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
type CryptoServiceFactoryOptions = FactoryOptions;
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
MasterPasswordServiceInitOptions &
KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions &
EncryptServiceInitOptions &
@@ -53,6 +58,7 @@ export function cryptoServiceFactory(
opts,
async () =>
new BrowserCryptoService(
await internalMasterPasswordServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts),

View File

@@ -1,6 +1,7 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
@@ -17,6 +18,7 @@ import { UserKey } from "@bitwarden/common/types/key";
export class BrowserCryptoService extends CryptoService {
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService,
encryptService: EncryptService,
@@ -28,6 +30,7 @@ export class BrowserCryptoService extends CryptoService {
private biometricStateService: BiometricStateService,
) {
super(
masterPasswordService,
keyGenerationService,
cryptoFunctionService,
encryptService,

View File

@@ -1,6 +1,10 @@
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -18,6 +22,8 @@ import { CliUtils } from "../../utils";
export class UnlockCommand {
constructor(
private accountService: AccountService,
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private cryptoService: CryptoService,
private stateService: StateService,
private cryptoFunctionService: CryptoFunctionService,
@@ -45,11 +51,14 @@ export class UnlockCommand {
const kdf = await this.stateService.getKdfType();
const kdfConfig = await this.stateService.getKdfConfig();
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig);
const storedKeyHash = await this.cryptoService.getMasterKeyHash();
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
const storedMasterKeyHash = await firstValueFrom(
this.masterPasswordService.masterKeyHash$(userId),
);
let passwordValid = false;
if (masterKey != null) {
if (storedKeyHash != null) {
if (storedMasterKeyHash != null) {
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(password, masterKey);
} else {
const serverKeyHash = await this.cryptoService.hashMasterKey(
@@ -67,7 +76,7 @@ export class UnlockCommand {
masterKey,
HashPurpose.LocalAuthorization,
);
await this.cryptoService.setMasterKeyHash(localKeyHash);
await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
} catch {
// Ignore
}
@@ -75,7 +84,8 @@ export class UnlockCommand {
}
if (passwordValid) {
await this.cryptoService.setMasterKey(masterKey);
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
await this.masterPasswordService.setMasterKey(masterKey, userId);
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
await this.cryptoService.setUserKey(userKey);

View File

@@ -28,6 +28,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
@@ -168,6 +169,7 @@ export class Main {
organizationUserService: OrganizationUserService;
collectionService: CollectionService;
vaultTimeoutService: VaultTimeoutService;
masterPasswordService: InternalMasterPasswordServiceAbstraction;
vaultTimeoutSettingsService: VaultTimeoutSettingsService;
syncService: SyncService;
eventCollectionService: EventCollectionServiceAbstraction;
@@ -352,6 +354,7 @@ export class Main {
);
this.cryptoService = new CryptoService(
this.masterPasswordService,
this.keyGenerationService,
this.cryptoFunctionService,
this.encryptService,
@@ -432,6 +435,8 @@ export class Main {
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
this.keyConnectorService = new KeyConnectorService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@@ -471,9 +476,10 @@ export class Main {
this.authRequestService = new AuthRequestService(
this.appIdService,
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.stateService,
);
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
@@ -481,6 +487,8 @@ export class Main {
);
this.loginStrategyService = new LoginStrategyService(
this.accountService,
this.masterPasswordService,
this.cryptoService,
this.apiService,
this.tokenService,
@@ -568,6 +576,8 @@ export class Main {
this.userVerificationService = new UserVerificationService(
this.stateService,
this.cryptoService,
this.accountService,
this.masterPasswordService,
this.i18nService,
this.userVerificationApiService,
this.userDecryptionOptionsService,
@@ -578,6 +588,8 @@ export class Main {
);
this.vaultTimeoutService = new VaultTimeoutService(
this.accountService,
this.masterPasswordService,
this.cipherService,
this.folderService,
this.collectionService,
@@ -596,6 +608,8 @@ export class Main {
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
this.syncService = new SyncService(
this.masterPasswordService,
this.accountService,
this.apiService,
this.domainSettingsService,
this.folderService,

View File

@@ -122,6 +122,8 @@ export class ServeCommand {
this.shareCommand = new ShareCommand(this.main.cipherService);
this.lockCommand = new LockCommand(this.main.vaultTimeoutService);
this.unlockCommand = new UnlockCommand(
this.main.accountService,
this.main.masterPasswordService,
this.main.cryptoService,
this.main.stateService,
this.main.cryptoFunctionService,

View File

@@ -253,6 +253,8 @@ export class Program {
if (!cmd.check) {
await this.exitIfNotAuthed();
const command = new UnlockCommand(
this.main.accountService,
this.main.masterPasswordService,
this.main.cryptoService,
this.main.stateService,
this.main.cryptoFunctionService,
@@ -613,6 +615,8 @@ export class Program {
this.processResponse(response, true);
} else {
const command = new UnlockCommand(
this.main.accountService,
this.main.masterPasswordService,
this.main.cryptoService,
this.main.stateService,
this.main.cryptoFunctionService,

View File

@@ -26,6 +26,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
@@ -120,6 +121,7 @@ export class AppComponent implements OnInit, OnDestroy {
private accountCleanUpInProgress: { [userId: string]: boolean } = {};
constructor(
private masterPasswordService: MasterPasswordServiceAbstraction,
private broadcasterService: BroadcasterService,
private folderService: InternalFolderService,
private syncService: SyncService,
@@ -408,8 +410,9 @@ export class AppComponent implements OnInit, OnDestroy {
(await this.authService.getAuthStatus(message.userId)) ===
AuthenticationStatus.Locked;
const forcedPasswordReset =
(await this.stateService.getForceSetPasswordReason({ userId: message.userId })) !=
ForceSetPasswordReason.None;
(await firstValueFrom(
this.masterPasswordService.forceSetPasswordReason$(message.userId),
)) != ForceSetPasswordReason.None;
if (locked) {
this.messagingService.send("locked", { userId: message.userId });
} else if (forcedPasswordReset) {

View File

@@ -20,6 +20,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaul
import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
@@ -228,6 +229,7 @@ const safeProviders: SafeProvider[] = [
provide: CryptoServiceAbstraction,
useClass: ElectronCryptoService,
deps: [
InternalMasterPasswordServiceAbstraction,
KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction,
EncryptService,

View File

@@ -14,7 +14,9 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@@ -52,6 +54,7 @@ describe("LockComponent", () => {
let broadcasterServiceMock: MockProxy<BroadcasterService>;
let platformUtilsServiceMock: MockProxy<PlatformUtilsService>;
let activatedRouteMock: MockProxy<ActivatedRoute>;
let mockMasterPasswordService: FakeMasterPasswordService;
const mockUserId = Utils.newGuid() as UserId;
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
@@ -67,6 +70,8 @@ describe("LockComponent", () => {
activatedRouteMock = mock<ActivatedRoute>();
activatedRouteMock.queryParams = mock<ActivatedRoute["queryParams"]>();
mockMasterPasswordService = new FakeMasterPasswordService();
biometricStateService.dismissedRequirePasswordOnStartCallout$ = of(false);
biometricStateService.promptAutomatically$ = of(false);
biometricStateService.promptCancelled$ = of(false);
@@ -74,6 +79,7 @@ describe("LockComponent", () => {
await TestBed.configureTestingModule({
declarations: [LockComponent, I18nPipe],
providers: [
{ provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService },
{
provide: I18nService,
useValue: mock<I18nService>(),

View File

@@ -11,6 +11,7 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { DeviceType } from "@bitwarden/common/enums";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
@@ -38,6 +39,7 @@ export class LockComponent extends BaseLockComponent {
private autoPromptBiometric = false;
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
@@ -63,6 +65,7 @@ export class LockComponent extends BaseLockComponent {
accountService: AccountService,
) {
super(
masterPasswordService,
router,
i18nService,
platformUtilsService,

View File

@@ -8,6 +8,8 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -29,6 +31,8 @@ const BroadcasterSubscriptionId = "SetPasswordComponent";
})
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
constructor(
accountService: AccountService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
apiService: ApiService,
i18nService: I18nService,
cryptoService: CryptoService,
@@ -50,6 +54,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
dialogService: DialogService,
) {
super(
accountService,
masterPasswordService,
i18nService,
cryptoService,
messagingService,

View File

@@ -7,6 +7,8 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -39,6 +41,8 @@ export class SsoComponent extends BaseSsoComponent {
logService: LogService,
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
) {
super(
ssoLoginService,
@@ -55,6 +59,8 @@ export class SsoComponent extends BaseSsoComponent {
logService,
userDecryptionOptionsService,
configService,
masterPasswordService,
accountService,
);
super.onSuccessfulLogin = async () => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@@ -11,6 +11,8 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -60,6 +62,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
@Inject(WINDOW) protected win: Window,
) {
super(
@@ -79,6 +83,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
userDecryptionOptionsService,
ssoLoginService,
configService,
masterPasswordService,
accountService,
);
super.onSuccessfulLogin = async () => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@@ -1,6 +1,7 @@
import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider";
import { mock } from "jest-mock-extended";
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
@@ -30,6 +31,7 @@ describe("electronCryptoService", () => {
const platformUtilService = mock<PlatformUtilsService>();
const logService = mock<LogService>();
const stateService = mock<StateService>();
let masterPasswordService: FakeMasterPasswordService;
let accountService: FakeAccountService;
let stateProvider: FakeStateProvider;
const biometricStateService = mock<BiometricStateService>();
@@ -38,9 +40,11 @@ describe("electronCryptoService", () => {
beforeEach(() => {
accountService = mockAccountServiceWith("userId" as UserId);
masterPasswordService = new FakeMasterPasswordService();
stateProvider = new FakeStateProvider(accountService);
sut = new ElectronCryptoService(
masterPasswordService,
keyGenerationService,
cryptoFunctionService,
encryptService,

View File

@@ -1,6 +1,7 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
@@ -20,6 +21,7 @@ import { UserKey, MasterKey } from "@bitwarden/common/types/key";
export class ElectronCryptoService extends CryptoService {
constructor(
masterPasswordService: InternalMasterPasswordServiceAbstraction,
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService,
encryptService: EncryptService,
@@ -31,6 +33,7 @@ export class ElectronCryptoService extends CryptoService {
private biometricStateService: BiometricStateService,
) {
super(
masterPasswordService,
keyGenerationService,
cryptoFunctionService,
encryptService,
@@ -159,12 +162,16 @@ export class ElectronCryptoService extends CryptoService {
const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId });
// decrypt
const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey;
let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey();
encUserKey = encUserKey ?? (await this.stateService.getMasterKeyEncryptedUserKey());
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
const encUserKeyPrim = await this.stateService.getEncryptedCryptoSymmetricKey();
const encUserKey =
encUserKeyPrim != null
? new EncString(encUserKeyPrim)
: await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId);
if (!encUserKey) {
throw new Error("No user key found during biometric migration");
}
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey));
const userKey = await this.decryptUserKeyWithMasterKey(masterKey, encUserKey);
// migrate
await this.storeBiometricKey(userKey, userId);
await this.stateService.setCryptoMasterKeyBiometric(null, { userId });

View File

@@ -1,6 +1,7 @@
import { Injectable, NgZone } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -30,6 +31,7 @@ export class NativeMessagingService {
private sharedSecrets = new Map<string, SymmetricCryptoKey>();
constructor(
private masterPasswordService: MasterPasswordServiceAbstraction,
private cryptoFunctionService: CryptoFunctionService,
private cryptoService: CryptoService,
private platformUtilService: PlatformUtilsService,
@@ -162,7 +164,9 @@ export class NativeMessagingService {
KeySuffixOptions.Biometric,
message.userId,
);
const masterKey = await this.cryptoService.getMasterKey(message.userId);
const masterKey = await firstValueFrom(
this.masterPasswordService.masterKey$(message.userId as UserId),
);
if (userKey != null) {
// we send the master key still for backwards compatibility

View File

@@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
@@ -9,7 +10,6 @@ import { EncryptionType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
@@ -22,6 +22,10 @@ import { Folder } from "@bitwarden/common/vault/models/domain/folder";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import {
FakeAccountService,
mockAccountServiceWith,
} from "../../../../../../libs/common/spec/fake-account-service";
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { StateService } from "../../core";
import { EmergencyAccessService } from "../emergency-access";
@@ -46,8 +50,10 @@ describe("KeyRotationService", () => {
const mockUserId = Utils.newGuid() as UserId;
const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId);
let mockMasterPasswordService: FakeMasterPasswordService = new FakeMasterPasswordService();
beforeAll(() => {
mockMasterPasswordService = new FakeMasterPasswordService();
mockApiService = mock<UserKeyRotationApiService>();
mockCipherService = mock<CipherService>();
mockFolderService = mock<FolderService>();
@@ -61,6 +67,7 @@ describe("KeyRotationService", () => {
mockConfigService = mock<ConfigService>();
keyRotationService = new UserKeyRotationService(
mockMasterPasswordService,
mockApiService,
mockCipherService,
mockFolderService,
@@ -174,7 +181,10 @@ describe("KeyRotationService", () => {
it("saves the master key in state after creation", async () => {
await keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword");
expect(mockCryptoService.setMasterKey).toHaveBeenCalledWith("mockMasterKey" as any);
expect(mockMasterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(
"mockMasterKey" as any,
mockUserId,
);
});
it("uses legacy rotation if feature flag is off", async () => {

View File

@@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -25,6 +26,7 @@ import { UserKeyRotationApiService } from "./user-key-rotation-api.service";
@Injectable()
export class UserKeyRotationService {
constructor(
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private apiService: UserKeyRotationApiService,
private cipherService: CipherService,
private folderService: FolderService,
@@ -61,7 +63,8 @@ export class UserKeyRotationService {
}
// Set master key again in case it was lost (could be lost on refresh)
await this.cryptoService.setMasterKey(masterKey);
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
await this.masterPasswordService.setMasterKey(masterKey, userId);
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(masterKey);
if (!newUserKey || !newEncUserKey) {

View File

@@ -1,80 +1,12 @@
import { Component, NgZone } from "@angular/core";
import { Router } from "@angular/router";
import { Component } from "@angular/core";
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
import { PinCryptoServiceAbstraction } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-lock",
templateUrl: "lock.component.html",
})
export class LockComponent extends BaseLockComponent {
constructor(
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
messagingService: MessagingService,
cryptoService: CryptoService,
vaultTimeoutService: VaultTimeoutService,
vaultTimeoutSettingsService: VaultTimeoutSettingsService,
environmentService: EnvironmentService,
stateService: StateService,
apiService: ApiService,
logService: LogService,
ngZone: NgZone,
policyApiService: PolicyApiServiceAbstraction,
policyService: InternalPolicyService,
passwordStrengthService: PasswordStrengthServiceAbstraction,
dialogService: DialogService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
userVerificationService: UserVerificationService,
pinCryptoService: PinCryptoServiceAbstraction,
biometricStateService: BiometricStateService,
accountService: AccountService,
) {
super(
router,
i18nService,
platformUtilsService,
messagingService,
cryptoService,
vaultTimeoutService,
vaultTimeoutSettingsService,
environmentService,
stateService,
apiService,
logService,
ngZone,
policyApiService,
policyService,
passwordStrengthService,
dialogService,
deviceTrustCryptoService,
userVerificationService,
pinCryptoService,
biometricStateService,
accountService,
);
}
async ngOnInit() {
await super.ngOnInit();
this.onSuccessfulSubmit = async () => {

View File

@@ -10,6 +10,8 @@ import {
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
import { OrganizationDomainSsoDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain-sso-details.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
@@ -46,6 +48,8 @@ export class SsoComponent extends BaseSsoComponent {
private validationService: ValidationService,
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
) {
super(
ssoLoginService,
@@ -62,6 +66,8 @@ export class SsoComponent extends BaseSsoComponent {
logService,
userDecryptionOptionsService,
configService,
masterPasswordService,
accountService,
);
this.redirectUri = window.location.origin + "/sso-connector.html";
this.clientId = "web";

View File

@@ -10,6 +10,8 @@ import {
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -50,6 +52,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
@Inject(WINDOW) protected win: Window,
) {
super(
@@ -69,6 +73,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest
userDecryptionOptionsService,
ssoLoginService,
configService,
masterPasswordService,
accountService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}