1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 04:03:29 +00:00

[PM-18721] update handling of cross-component async actions

This commit is contained in:
rr-bw
2025-05-24 16:52:20 -07:00
parent 1fa2a3325e
commit 6fa17bdd39

View File

@@ -13,6 +13,7 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { HashPurpose } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid";
@@ -193,6 +194,7 @@ export class InputPasswordComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private policyService: PolicyService,
private toastService: ToastService,
private validationService: ValidationService,
) {}
ngOnInit(): void {
@@ -263,136 +265,138 @@ export class InputPasswordComponent implements OnInit {
}
submit = async () => {
this.submittingBehaviorSubject.next(true);
try {
this.submittingBehaviorSubject.next(true);
this.verifyFlow();
this.verifyFlow();
this.formGroup.markAllAsTouched();
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
this.showErrorSummary = true;
this.submittingBehaviorSubject.next(false);
return;
}
const currentPassword = this.formGroup.controls.currentPassword?.value ?? "";
const newPassword = this.formGroup.controls.newPassword.value;
const newPasswordHint = this.formGroup.controls.newPasswordHint?.value ?? "";
const checkForBreaches = this.formGroup.controls.checkForBreaches?.value ?? true;
if (this.flow === InputPasswordFlow.ChangePasswordDelegation) {
await this.handleChangePasswordDelegationFlow(newPassword);
this.submittingBehaviorSubject.next(false);
return;
}
if (!this.email) {
throw new Error("Email is required to create master key.");
}
// 1. Determine kdfConfig
if (this.flow === InputPasswordFlow.SetInitialPasswordAccountRegistration) {
this.kdfConfig = DEFAULT_KDF_CONFIG;
} else {
if (!this.userId) {
throw new Error("userId not passed down");
}
this.kdfConfig = await firstValueFrom(this.kdfConfigService.getKdfConfig$(this.userId));
}
if (this.kdfConfig == null) {
throw new Error("KdfConfig is required to create master key.");
}
// 2. Verify current password is correct (if necessary)
if (
this.flow === InputPasswordFlow.ChangePassword ||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
) {
const currentPasswordVerified = await this.verifyCurrentPassword(
currentPassword,
this.kdfConfig,
);
if (!currentPasswordVerified) {
if (this.formGroup.invalid) {
this.showErrorSummary = true;
this.submittingBehaviorSubject.next(false);
return;
}
}
// 3. Verify new password
const newPasswordVerified = await this.verifyNewPassword(
newPassword,
this.passwordStrengthScore,
checkForBreaches,
);
if (!newPasswordVerified) {
this.submittingBehaviorSubject.next(false);
return;
}
const currentPassword = this.formGroup.controls.currentPassword?.value ?? "";
const newPassword = this.formGroup.controls.newPassword.value;
const newPasswordHint = this.formGroup.controls.newPasswordHint?.value ?? "";
const checkForBreaches = this.formGroup.controls.checkForBreaches?.value ?? true;
// 4. Create cryptographic keys and build a PasswordInputResult object
const newMasterKey = await this.keyService.makeMasterKey(
newPassword,
this.email,
this.kdfConfig,
);
if (this.flow === InputPasswordFlow.ChangePasswordDelegation) {
await this.handleChangePasswordDelegationFlow(newPassword);
return;
}
const newServerMasterKeyHash = await this.keyService.hashMasterKey(
newPassword,
newMasterKey,
HashPurpose.ServerAuthorization,
);
if (!this.email) {
throw new Error("Email is required to create master key.");
}
const newLocalMasterKeyHash = await this.keyService.hashMasterKey(
newPassword,
newMasterKey,
HashPurpose.LocalAuthorization,
);
// 1. Determine kdfConfig
if (this.flow === InputPasswordFlow.SetInitialPasswordAccountRegistration) {
this.kdfConfig = DEFAULT_KDF_CONFIG;
} else {
if (!this.userId) {
throw new Error("userId not passed down");
}
this.kdfConfig = await firstValueFrom(this.kdfConfigService.getKdfConfig$(this.userId));
}
const passwordInputResult: PasswordInputResult = {
newPassword,
newMasterKey,
newServerMasterKeyHash,
newLocalMasterKeyHash,
newPasswordHint,
kdfConfig: this.kdfConfig,
};
if (this.kdfConfig == null) {
throw new Error("KdfConfig is required to create master key.");
}
if (
this.flow === InputPasswordFlow.ChangePassword ||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
) {
const currentMasterKey = await this.keyService.makeMasterKey(
currentPassword,
// 2. Verify current password is correct (if necessary)
if (
this.flow === InputPasswordFlow.ChangePassword ||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
) {
const currentPasswordVerified = await this.verifyCurrentPassword(
currentPassword,
this.kdfConfig,
);
if (!currentPasswordVerified) {
return;
}
}
// 3. Verify new password
const newPasswordVerified = await this.verifyNewPassword(
newPassword,
this.passwordStrengthScore,
checkForBreaches,
);
if (!newPasswordVerified) {
return;
}
// 4. Create cryptographic keys and build a PasswordInputResult object
const newMasterKey = await this.keyService.makeMasterKey(
newPassword,
this.email,
this.kdfConfig,
);
const currentServerMasterKeyHash = await this.keyService.hashMasterKey(
currentPassword,
currentMasterKey,
const newServerMasterKeyHash = await this.keyService.hashMasterKey(
newPassword,
newMasterKey,
HashPurpose.ServerAuthorization,
);
const currentLocalMasterKeyHash = await this.keyService.hashMasterKey(
currentPassword,
currentMasterKey,
const newLocalMasterKeyHash = await this.keyService.hashMasterKey(
newPassword,
newMasterKey,
HashPurpose.LocalAuthorization,
);
passwordInputResult.currentPassword = currentPassword;
passwordInputResult.currentMasterKey = currentMasterKey;
passwordInputResult.currentServerMasterKeyHash = currentServerMasterKeyHash;
passwordInputResult.currentLocalMasterKeyHash = currentLocalMasterKeyHash;
}
const passwordInputResult: PasswordInputResult = {
newPassword,
newMasterKey,
newServerMasterKeyHash,
newLocalMasterKeyHash,
newPasswordHint,
kdfConfig: this.kdfConfig,
};
if (this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
passwordInputResult.rotateUserKey = this.formGroup.controls.rotateUserKey?.value;
}
if (
this.flow === InputPasswordFlow.ChangePassword ||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
) {
const currentMasterKey = await this.keyService.makeMasterKey(
currentPassword,
this.email,
this.kdfConfig,
);
// 5. Emit cryptographic keys and other password related properties
this.onPasswordFormSubmit.emit(passwordInputResult);
this.submittingBehaviorSubject.next(false);
const currentServerMasterKeyHash = await this.keyService.hashMasterKey(
currentPassword,
currentMasterKey,
HashPurpose.ServerAuthorization,
);
const currentLocalMasterKeyHash = await this.keyService.hashMasterKey(
currentPassword,
currentMasterKey,
HashPurpose.LocalAuthorization,
);
passwordInputResult.currentPassword = currentPassword;
passwordInputResult.currentMasterKey = currentMasterKey;
passwordInputResult.currentServerMasterKeyHash = currentServerMasterKeyHash;
passwordInputResult.currentLocalMasterKeyHash = currentLocalMasterKeyHash;
}
if (this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
passwordInputResult.rotateUserKey = this.formGroup.controls.rotateUserKey?.value;
}
// 5. Emit cryptographic keys and other password related properties
this.onPasswordFormSubmit.emit(passwordInputResult);
} catch (e) {
this.validationService.showError(e);
} finally {
this.submittingBehaviorSubject.next(false);
}
};
/**