mirror of
https://github.com/bitwarden/browser
synced 2026-02-09 05:00:10 +00:00
add a flow to InputPasswordFlow enum, and a method to verify the flow and presence of a userId
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<div *ngIf="!useTrialStepper">
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[flow]="inputPasswordFlow"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
@@ -11,7 +11,7 @@
|
||||
<app-vertical-stepper #stepper linear (selectionChange)="verticalStepChange($event)">
|
||||
<app-vertical-step label="Create Account" [editable]="false" [subLabel]="email">
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[flow]="inputPasswordFlow"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
|
||||
@@ -51,7 +51,7 @@ export type InitiationPath =
|
||||
export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||
|
||||
InputPasswordFlow = InputPasswordFlow;
|
||||
inputPasswordFlow = InputPasswordFlow.AccountRegistration;
|
||||
|
||||
/** Password Manager or Secrets Manager */
|
||||
product: ProductType;
|
||||
|
||||
@@ -389,7 +389,6 @@ const safeProviders: SafeProvider[] = [
|
||||
provide: ChangePasswordService,
|
||||
useClass: WebChangePasswordService,
|
||||
deps: [
|
||||
AccountService,
|
||||
KeyServiceAbstraction,
|
||||
MasterPasswordApiServiceAbstraction,
|
||||
InternalMasterPasswordServiceAbstraction,
|
||||
|
||||
@@ -1511,7 +1511,6 @@ const safeProviders: SafeProvider[] = [
|
||||
provide: ChangePasswordService,
|
||||
useClass: DefaultChangePasswordService,
|
||||
deps: [
|
||||
AccountServiceAbstraction,
|
||||
KeyService,
|
||||
MasterPasswordApiServiceAbstraction,
|
||||
InternalMasterPasswordServiceAbstraction,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="inputPasswordFlow"
|
||||
[flow]="inputPasswordFlow"
|
||||
[email]="email"
|
||||
[userId]="userId"
|
||||
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions"
|
||||
|
||||
@@ -93,7 +93,7 @@ export class ChangePasswordComponent implements OnInit {
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
if (this.activeAccount == null) {
|
||||
throw new Error("User not found");
|
||||
throw new Error("User or userId not found");
|
||||
}
|
||||
|
||||
await this.changePasswordService.rotateUserKeyMasterPasswordAndEncryptedData(
|
||||
@@ -109,6 +109,7 @@ export class ChangePasswordComponent implements OnInit {
|
||||
passwordInputResult.newPasswordHint,
|
||||
passwordInputResult.newMasterKey,
|
||||
passwordInputResult.newServerMasterKeyHash,
|
||||
this.userId,
|
||||
);
|
||||
|
||||
this.toastService.showToast({
|
||||
@@ -181,6 +182,7 @@ export class ChangePasswordComponent implements OnInit {
|
||||
title: this.i18nService.t("masterPasswordChanged"),
|
||||
message: this.i18nService.t("logBackIn"),
|
||||
});
|
||||
|
||||
this.messagingService.send("logout");
|
||||
} catch {
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
<bit-form-field
|
||||
*ngIf="
|
||||
inputPasswordFlow === InputPasswordFlow.ChangePassword ||
|
||||
inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
flow === InputPasswordFlow.ChangePassword ||
|
||||
flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
"
|
||||
>
|
||||
<bit-label>{{ "currentMasterPass" | i18n }}</bit-label>
|
||||
@@ -90,9 +90,7 @@
|
||||
<bit-label>{{ "checkForBreaches" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
|
||||
<bit-form-control
|
||||
*ngIf="inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation"
|
||||
>
|
||||
<bit-form-control *ngIf="flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation">
|
||||
<input
|
||||
type="checkbox"
|
||||
bitCheckbox
|
||||
|
||||
@@ -34,7 +34,12 @@ import {
|
||||
ToastService,
|
||||
Translation,
|
||||
} from "@bitwarden/components";
|
||||
import { KdfConfig, KdfConfigService, KeyService } from "@bitwarden/key-management";
|
||||
import {
|
||||
DEFAULT_KDF_CONFIG,
|
||||
KdfConfig,
|
||||
KdfConfigService,
|
||||
KeyService,
|
||||
} from "@bitwarden/key-management";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
@@ -45,24 +50,25 @@ import { compareInputs, ValidationGoal } from "../validators/compare-inputs.vali
|
||||
import { PasswordInputResult } from "./password-input-result";
|
||||
|
||||
/**
|
||||
* Determines which form input elements will be displayed in the UI.
|
||||
* Determines which form elements will be displayed in the UI
|
||||
* and which cryptographic keys will be created and emitted.
|
||||
*/
|
||||
export enum InputPasswordFlow {
|
||||
/**
|
||||
* - Input: New password
|
||||
* - Input: New password confirm
|
||||
* - Input: New password hint
|
||||
* - Checkbox: Check for breaches
|
||||
* Form elements displayed:
|
||||
* - [Input] New password
|
||||
* - [Input] New password confirm
|
||||
* - [Input] New password hint
|
||||
* - [Checkbox] Check for breaches
|
||||
*/
|
||||
SetInitialPassword,
|
||||
/**
|
||||
* Everything above, plus:
|
||||
* - Input: Current password (as the first element in the UI)
|
||||
AccountRegistration, // important: this flow does not involve an activeAccount/userId
|
||||
SetInitialPasswordAuthedUser,
|
||||
/*
|
||||
* All form elements above, plus: [Input] Current password (as the first element in the UI)
|
||||
*/
|
||||
ChangePassword,
|
||||
/**
|
||||
* Everything above, plus:
|
||||
* - Checkbox: Rotate account encryption key (as the last element in the UI)
|
||||
* All form elements above, plus: [Checkbox] Rotate account encryption key (as the last element in the UI)
|
||||
*/
|
||||
ChangePasswordWithOptionalUserKeyRotation,
|
||||
}
|
||||
@@ -89,10 +95,10 @@ export class InputPasswordComponent implements OnInit {
|
||||
@Output() onPasswordFormSubmit = new EventEmitter<PasswordInputResult>();
|
||||
@Output() onSecondaryButtonClick = new EventEmitter<void>();
|
||||
|
||||
@Input({ required: true }) inputPasswordFlow!: InputPasswordFlow;
|
||||
@Input({ required: true }) email!: string;
|
||||
@Input({ required: true }) userId!: UserId;
|
||||
@Input({ required: true }) flow!: InputPasswordFlow;
|
||||
@Input({ required: true, transform: (val: string) => val.trim().toLowerCase() }) email!: string;
|
||||
|
||||
@Input() userId?: UserId;
|
||||
@Input() loading = false;
|
||||
@Input() masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null;
|
||||
|
||||
@@ -133,13 +139,24 @@ export class InputPasswordComponent implements OnInit {
|
||||
compareInputs(
|
||||
ValidationGoal.InputsShouldNotMatch,
|
||||
"newPassword",
|
||||
"hint",
|
||||
"newPasswordHint",
|
||||
this.i18nService.t("hintEqualsPassword"),
|
||||
),
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
protected get minPasswordLengthMsg() {
|
||||
if (
|
||||
this.masterPasswordPolicyOptions != null &&
|
||||
this.masterPasswordPolicyOptions.minLength > 0
|
||||
) {
|
||||
return this.i18nService.t("characterMinimum", this.masterPasswordPolicyOptions.minLength);
|
||||
} else {
|
||||
return this.i18nService.t("characterMinimum", this.minPasswordLength);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private auditService: AuditService,
|
||||
private cipherService: CipherService,
|
||||
@@ -155,9 +172,14 @@ export class InputPasswordComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.addFormFieldsIfNecessary();
|
||||
this.setButtonText();
|
||||
}
|
||||
|
||||
private addFormFieldsIfNecessary() {
|
||||
if (
|
||||
this.inputPasswordFlow === InputPasswordFlow.ChangePassword ||
|
||||
this.inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
this.flow === InputPasswordFlow.ChangePassword ||
|
||||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
) {
|
||||
// https://github.com/angular/angular/issues/48794
|
||||
(this.formGroup as FormGroup<any>).addControl(
|
||||
@@ -166,14 +188,16 @@ export class InputPasswordComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
|
||||
if (this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
|
||||
// https://github.com/angular/angular/issues/48794
|
||||
(this.formGroup as FormGroup<any>).addControl(
|
||||
"rotateUserKey",
|
||||
this.formBuilder.control<boolean>(false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private setButtonText() {
|
||||
if (this.primaryButtonText) {
|
||||
this.primaryButtonTextStr = this.i18nService.t(
|
||||
this.primaryButtonText.key,
|
||||
@@ -189,22 +213,9 @@ export class InputPasswordComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
get minPasswordLengthMsg() {
|
||||
if (
|
||||
this.masterPasswordPolicyOptions != null &&
|
||||
this.masterPasswordPolicyOptions.minLength > 0
|
||||
) {
|
||||
return this.i18nService.t("characterMinimum", this.masterPasswordPolicyOptions.minLength);
|
||||
} else {
|
||||
return this.i18nService.t("characterMinimum", this.minPasswordLength);
|
||||
}
|
||||
}
|
||||
|
||||
getPasswordStrengthScore(score: PasswordStrengthScore) {
|
||||
this.passwordStrengthScore = score;
|
||||
}
|
||||
|
||||
protected submit = async () => {
|
||||
this.verifyFlowAndUserId();
|
||||
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
@@ -216,45 +227,50 @@ export class InputPasswordComponent implements OnInit {
|
||||
throw new Error("Email is required to create master key.");
|
||||
}
|
||||
|
||||
this.kdfConfig = await firstValueFrom(this.kdfConfigService.getKdfConfig$(this.userId));
|
||||
if (this.kdfConfig == null) {
|
||||
throw new Error("KdfConfig is required to create master key.");
|
||||
}
|
||||
|
||||
const currentPassword = this.formGroup.get("currentPassword")?.value || "";
|
||||
const newPassword = this.formGroup.controls.newPassword.value;
|
||||
const newPasswordHint = this.formGroup.controls.newPasswordHint.value;
|
||||
const checkForBreaches = this.formGroup.controls.checkForBreaches.value;
|
||||
|
||||
// 1. Verify current password is correct (if necessary)
|
||||
// 1. Determine kdfConfig
|
||||
if (this.flow === InputPasswordFlow.AccountRegistration) {
|
||||
this.kdfConfig = DEFAULT_KDF_CONFIG;
|
||||
} else {
|
||||
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.inputPasswordFlow === InputPasswordFlow.ChangePassword ||
|
||||
this.inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
this.flow === InputPasswordFlow.ChangePassword ||
|
||||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
) {
|
||||
const currentPasswordIsCorrect = await this.verifyCurrentPassword(
|
||||
const currentPasswordVerified = await this.verifyCurrentPassword(
|
||||
currentPassword,
|
||||
this.userId,
|
||||
this.kdfConfig,
|
||||
);
|
||||
if (!currentPasswordIsCorrect) {
|
||||
if (!currentPasswordVerified) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Evaluate new password
|
||||
const newPasswordEvaluatedSuccessfully = await this.evaluateNewPassword(
|
||||
// 3. Verify new password
|
||||
const newPasswordVerified = await this.verifyNewPassword(
|
||||
newPassword,
|
||||
this.passwordStrengthScore,
|
||||
checkForBreaches,
|
||||
);
|
||||
if (!newPasswordEvaluatedSuccessfully) {
|
||||
if (!newPasswordVerified) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Create cryptographic keys
|
||||
// 4. Create cryptographic keys and build a PasswordInputResult object
|
||||
const newMasterKey = await this.keyService.makeMasterKey(
|
||||
newPassword,
|
||||
this.email.trim().toLowerCase(),
|
||||
this.email,
|
||||
this.kdfConfig,
|
||||
);
|
||||
|
||||
@@ -280,12 +296,12 @@ export class InputPasswordComponent implements OnInit {
|
||||
};
|
||||
|
||||
if (
|
||||
this.inputPasswordFlow === InputPasswordFlow.ChangePassword ||
|
||||
this.inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
this.flow === InputPasswordFlow.ChangePassword ||
|
||||
this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation
|
||||
) {
|
||||
const currentMasterKey = await this.keyService.makeMasterKey(
|
||||
currentPassword,
|
||||
this.email.trim().toLowerCase(),
|
||||
this.email,
|
||||
this.kdfConfig,
|
||||
);
|
||||
|
||||
@@ -307,31 +323,66 @@ export class InputPasswordComponent implements OnInit {
|
||||
passwordInputResult.currentLocalMasterKeyHash = currentLocalMasterKeyHash;
|
||||
}
|
||||
|
||||
if (this.inputPasswordFlow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
|
||||
if (this.flow === InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation) {
|
||||
passwordInputResult.rotateUserKey = this.formGroup.get("rotateUserKey")?.value;
|
||||
}
|
||||
|
||||
// 4. Emit cryptographic keys and other password related properties
|
||||
// 5. Emit cryptographic keys and other password related properties
|
||||
this.onPasswordFormSubmit.emit(passwordInputResult);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the current password is correct, false otherwise
|
||||
* This method prevents a dev from passing down the wrong `InputPasswordFlow`
|
||||
* from the parent component or from failing to pass down a `userId` for flows
|
||||
* that require it.
|
||||
*
|
||||
* We cannot mark the `userId` `@Input` as required because in an account registration
|
||||
* flow we will not have an active account `userId` to pass down.
|
||||
*/
|
||||
private verifyFlowAndUserId() {
|
||||
/**
|
||||
* There can be no active account (and thus no userId) in an account registration
|
||||
* flow. If there is a userId, it means the dev passed down the wrong InputPasswordFlow
|
||||
* from the parent component.
|
||||
*/
|
||||
if (this.flow === InputPasswordFlow.AccountRegistration) {
|
||||
if (this.userId) {
|
||||
throw new Error(
|
||||
"There can be no userId in an account registration flow. Please pass down the appropriate InputPasswordFlow from the parent component.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There MUST be an active account (and thus a userId) in all other flows.
|
||||
* If no userId is passed down, it means the dev either:
|
||||
* (a) passed down the wrong InputPasswordFlow, or
|
||||
* (b) passed down the correct InputPasswordFlow but failed to pass down a userId
|
||||
*/
|
||||
if (this.flow !== InputPasswordFlow.AccountRegistration) {
|
||||
if (!this.userId) {
|
||||
throw new Error("The selected InputPasswordFlow requires that a userId be passed down");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the current password is correct (it can be used to successfully decrypt
|
||||
* the masterKeyEncrypedUserKey), `false` otherwise
|
||||
*/
|
||||
private async verifyCurrentPassword(
|
||||
currentPassword: string,
|
||||
userId: UserId,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<boolean> {
|
||||
const currentMasterKey = await this.keyService.makeMasterKey(
|
||||
currentPassword,
|
||||
this.email.trim().toLowerCase(),
|
||||
this.email,
|
||||
kdfConfig,
|
||||
);
|
||||
|
||||
const decryptedUserKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
|
||||
currentMasterKey,
|
||||
userId,
|
||||
this.userId,
|
||||
);
|
||||
|
||||
if (decryptedUserKey == null) {
|
||||
@@ -348,9 +399,10 @@ export class InputPasswordComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the new password passes all checks, false otherwise
|
||||
* Returns `true` if the new password is not weak or breached and it passes
|
||||
* any enforced org policy options, `false` otherwise
|
||||
*/
|
||||
private async evaluateNewPassword(
|
||||
private async verifyNewPassword(
|
||||
newPassword: string,
|
||||
passwordStrengthScore: PasswordStrengthScore,
|
||||
checkForBreaches: boolean,
|
||||
@@ -414,7 +466,7 @@ export class InputPasswordComponent implements OnInit {
|
||||
return true;
|
||||
}
|
||||
|
||||
async rotateUserKeyClicked() {
|
||||
protected async rotateUserKeyClicked() {
|
||||
const rotateUserKeyCtrl = this.formGroup.get(
|
||||
"rotateUserKey",
|
||||
) as unknown as FormControl<boolean>;
|
||||
@@ -468,4 +520,8 @@ export class InputPasswordComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected getPasswordStrengthScore(score: PasswordStrengthScore) {
|
||||
this.passwordStrengthScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<auth-input-password
|
||||
*ngIf="!loading"
|
||||
[email]="email"
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[flow]="inputPasswordFlow"
|
||||
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions"
|
||||
[loading]="submitting"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
|
||||
@@ -39,8 +39,7 @@ import { RegistrationFinishService } from "./registration-finish.service";
|
||||
export class RegistrationFinishComponent implements OnInit, OnDestroy {
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
InputPasswordFlow = InputPasswordFlow;
|
||||
|
||||
inputPasswordFlow = InputPasswordFlow.AccountRegistration;
|
||||
loading = true;
|
||||
submitting = false;
|
||||
email: string;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</app-callout>
|
||||
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[flow]="inputPasswordFlow"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
[email]="email"
|
||||
[loading]="submitting"
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
imports: [CommonModule, InputPasswordComponent, JslibModule],
|
||||
})
|
||||
export class SetPasswordJitComponent implements OnInit {
|
||||
protected InputPasswordFlow = InputPasswordFlow;
|
||||
protected inputPasswordFlow = InputPasswordFlow.SetInitialPasswordAuthedUser;
|
||||
protected email: string;
|
||||
protected masterPasswordPolicyOptions: MasterPasswordPolicyOptions;
|
||||
protected orgId: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { MasterKey } from "@bitwarden/common/types/key";
|
||||
|
||||
export abstract class ChangePasswordService {
|
||||
@@ -20,5 +21,6 @@ export abstract class ChangePasswordService {
|
||||
newPasswordHint: string,
|
||||
newMasterKey: MasterKey,
|
||||
newServerMasterKeyHash: string,
|
||||
userId: UserId,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
|
||||
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { MasterKey } from "@bitwarden/common/types/key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
@@ -12,7 +10,6 @@ import { ChangePasswordService } from "../../abstractions";
|
||||
|
||||
export class DefaultChangePasswordService implements ChangePasswordService {
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private keyService: KeyService,
|
||||
private masterPasswordApiService: MasterPasswordApiService,
|
||||
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
@@ -40,8 +37,11 @@ export class DefaultChangePasswordService implements ChangePasswordService {
|
||||
newPasswordHint: string,
|
||||
newMasterKey: MasterKey,
|
||||
newServerMasterKeyHash: string,
|
||||
userId: UserId,
|
||||
) {
|
||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||
if (!userId) {
|
||||
throw new Error("The change password process requires a userId");
|
||||
}
|
||||
|
||||
const decryptedUserKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(
|
||||
currentMasterKey,
|
||||
|
||||
Reference in New Issue
Block a user