1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-19 02:44:01 +00:00

refactor(input-password-flows) [Auth/PM-27086] Use new KM Data Types in InputPasswordComponent flows - TDE & Permission User (#18400)

Updates the SetInitialPasswordService TDE + Permission user flow to use the new KM data types:
- `MasterPasswordAuthenticationData`
- `MasterPasswordUnlockData`
This allows us to move away from the deprecated `makeMasterKey()` method (which takes email as salt) as we seek to eventually separate the email from the salt.

The new `setInitialPasswordTdeUserWithPermission()` method essentially takes the existing deprecated `setInitialPassword()` method and:
- Removes logic that is specific to a `JIT_PROVISIONED_MP_ORG_USER` case. This way the method only handles `TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP` cases.
- Updates the logic to use `MasterPasswordAuthenticationData` and `MasterPasswordUnlockData`

Behind feature flag: `pm-27086-update-authentication-apis-for-input-password`
This commit is contained in:
rr-bw
2026-02-17 10:44:21 -08:00
committed by GitHub
parent 3715ed1441
commit 4a651fbfb3
6 changed files with 568 additions and 3 deletions

View File

@@ -7,6 +7,7 @@ import {
InitializeJitPasswordCredentials,
SetInitialPasswordCredentials,
SetInitialPasswordService,
SetInitialPasswordTdeUserWithPermissionCredentials,
SetInitialPasswordUserType,
} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction";
import {
@@ -30,6 +31,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { newGuid } from "@bitwarden/guid";
import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management";
import { DesktopSetInitialPasswordService } from "./desktop-set-initial-password.service";
@@ -224,4 +226,68 @@ describe("DesktopSetInitialPasswordService", () => {
superSpy.mockRestore();
});
});
describe("setInitialPasswordTdeUserWithPermission()", () => {
let credentials: SetInitialPasswordTdeUserWithPermissionCredentials;
let userId: UserId;
let superSpy: jest.SpyInstance;
beforeEach(() => {
credentials = {
newPassword: "newPassword123!",
salt: "user@example.com" as MasterPasswordSalt,
kdfConfig: DEFAULT_KDF_CONFIG,
newPasswordHint: "newPasswordHint",
orgSsoIdentifier: "orgSsoIdentifier",
orgId: "orgId" as OrganizationId,
resetPasswordAutoEnroll: false,
};
userId = newGuid() as UserId;
superSpy = jest
.spyOn(
DefaultSetInitialPasswordService.prototype,
"setInitialPasswordTdeUserWithPermission",
)
.mockResolvedValue(undefined); // undefined = successful
});
afterEach(() => {
superSpy.mockRestore();
});
it("should call the setInitialPasswordTdeUserWithPermission() method on the default service", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(superSpy).toHaveBeenCalledWith(credentials, userId);
});
describe("given the initial password was successfully set", () => {
it("should send a 'redrawMenu' message", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(messagingService.send).toHaveBeenCalledTimes(1);
expect(messagingService.send).toHaveBeenCalledWith("redrawMenu");
});
});
describe("given the initial password was NOT successfully set (due an error on the default service)", () => {
it("should NOT send a 'redrawMenu' message", async () => {
// Arrange
const error = new Error("error on DefaultSetInitialPasswordService");
superSpy.mockRejectedValue(error);
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow(error);
expect(messagingService.send).not.toHaveBeenCalled();
});
});
});
});

View File

@@ -4,6 +4,7 @@ import {
InitializeJitPasswordCredentials,
SetInitialPasswordCredentials,
SetInitialPasswordService,
SetInitialPasswordTdeUserWithPermissionCredentials,
SetInitialPasswordUserType,
} from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction";
import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
@@ -75,4 +76,13 @@ export class DesktopSetInitialPasswordService
this.messagingService.send("redrawMenu");
}
override async setInitialPasswordTdeUserWithPermission(
credentials: SetInitialPasswordTdeUserWithPermissionCredentials,
userId: UserId,
) {
await super.setInitialPasswordTdeUserWithPermission(credentials, userId);
this.messagingService.send("redrawMenu");
}
}

View File

@@ -15,11 +15,13 @@ import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/ma
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request";
import { UpdateTdeOffboardingPasswordRequest } from "@bitwarden/common/auth/models/request/update-tde-offboarding-password.request";
import { assertNonNullish, assertTruthy } from "@bitwarden/common/auth/utils";
import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import {
MasterPasswordAuthenticationData,
MasterPasswordSalt,
MasterPasswordUnlockData,
} from "@bitwarden/common/key-management/master-password/types/master-password.types";
@@ -45,6 +47,7 @@ import {
SetInitialPasswordService,
SetInitialPasswordTdeOffboardingCredentials,
SetInitialPasswordUserType,
SetInitialPasswordTdeUserWithPermissionCredentials,
} from "./set-initial-password.service.abstraction";
export class DefaultSetInitialPasswordService implements SetInitialPasswordService {
@@ -212,7 +215,7 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
await this.masterPasswordService.setMasterKeyHash(newLocalMasterKeyHash, userId);
if (resetPasswordAutoEnroll) {
await this.handleResetPasswordAutoEnroll(newServerMasterKeyHash, orgId, userId);
await this.handleResetPasswordAutoEnrollOld(newServerMasterKeyHash, orgId, userId);
}
}
@@ -336,6 +339,86 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
);
}
async setInitialPasswordTdeUserWithPermission(
credentials: SetInitialPasswordTdeUserWithPermissionCredentials,
userId: UserId,
): Promise<void> {
const ctx =
"Could not set initial password for TDE user with Manage Account Recovery permission.";
assertTruthy(credentials.newPassword, "newPassword", ctx);
assertTruthy(credentials.salt, "salt", ctx);
assertNonNullish(credentials.kdfConfig, "kdfConfig", ctx);
assertNonNullish(credentials.newPasswordHint, "newPasswordHint", ctx); // can have an empty string as a valid value, so check non-nullish
assertTruthy(credentials.orgSsoIdentifier, "orgSsoIdentifier", ctx);
assertTruthy(credentials.orgId, "orgId", ctx);
assertNonNullish(credentials.resetPasswordAutoEnroll, "resetPasswordAutoEnroll", ctx); // can have `false` as a valid value, so check non-nullish
assertTruthy(userId, "userId", ctx);
const {
newPassword,
salt,
kdfConfig,
newPasswordHint,
orgSsoIdentifier,
orgId,
resetPasswordAutoEnroll,
} = credentials;
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
if (!userKey) {
throw new Error("userKey not found.");
}
const authenticationData: MasterPasswordAuthenticationData =
await this.masterPasswordService.makeMasterPasswordAuthenticationData(
newPassword,
kdfConfig,
salt,
);
const unlockData: MasterPasswordUnlockData =
await this.masterPasswordService.makeMasterPasswordUnlockData(
newPassword,
kdfConfig,
salt,
userKey,
);
const request = SetPasswordRequest.newConstructor(
authenticationData,
unlockData,
newPasswordHint,
orgSsoIdentifier,
null, // no KeysRequest for TDE user because they already have a key pair
);
await this.masterPasswordApiService.setPassword(request);
// Clear force set password reason to allow navigation back to vault.
await this.masterPasswordService.setForceSetPasswordReason(ForceSetPasswordReason.None, userId);
// User now has a password so update decryption state
await this.masterPasswordService.setMasterPasswordUnlockData(unlockData, userId);
await this.updateLegacyState(
newPassword,
unlockData.kdf,
new EncString(unlockData.masterKeyWrappedUserKey),
userId,
unlockData,
);
if (resetPasswordAutoEnroll) {
await this.handleResetPasswordAutoEnroll(
authenticationData.masterPasswordAuthenticationHash,
orgId,
userId,
userKey,
);
}
}
/**
* @deprecated To be removed in PM-28143
*/
@@ -441,7 +524,19 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
await this.masterPasswordService.setMasterPasswordUnlockData(masterPasswordUnlockData, userId);
}
private async handleResetPasswordAutoEnroll(
/**
* @deprecated To be removed in PM-28143
*
* This method is now deprecated because it is used with the deprecated `setInitialPassword()` method,
* which handles both JIT MP and TDE + Permission user flows.
*
* Since these methods can handle the JIT MP flow - which creates a new user key and sets it to state - we
* must retreive that user key here in this method.
*
* But the new handleResetPasswordAutoEnroll() method is only used in the TDE + Permission user case, in which
* case we already have the user key and can simply pass it through via method parameter ( @see handleResetPasswordAutoEnroll )
*/
private async handleResetPasswordAutoEnrollOld(
masterKeyHash: string,
orgId: string,
userId: UserId,
@@ -483,4 +578,43 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
enrollmentRequest,
);
}
private async handleResetPasswordAutoEnroll(
masterKeyHash: string,
orgId: string,
userId: UserId,
userKey: UserKey,
) {
const organizationKeys = await this.organizationApiService.getKeys(orgId);
if (organizationKeys == null) {
throw new Error(
"Organization keys response is null. Could not handle reset password auto enroll.",
);
}
const orgPublicKey = Utils.fromB64ToArray(organizationKeys.publicKey);
// RSA encrypt user key with organization public key
const orgPublicKeyEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
userKey,
orgPublicKey,
);
if (orgPublicKeyEncryptedUserKey == null || !orgPublicKeyEncryptedUserKey.encryptedString) {
throw new Error(
"orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.",
);
}
const enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest();
enrollmentRequest.masterPasswordHash = masterKeyHash;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString;
await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment(
orgId,
userId,
enrollmentRequest,
);
}
}

View File

@@ -31,6 +31,9 @@ import {
} from "@bitwarden/common/key-management/crypto/models/enc-string";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import {
MasterKeyWrappedUserKey,
MasterPasswordAuthenticationData,
MasterPasswordAuthenticationHash,
MasterPasswordSalt,
MasterPasswordUnlockData,
} from "@bitwarden/common/key-management/master-password/types/master-password.types";
@@ -62,6 +65,7 @@ import {
SetInitialPasswordCredentials,
SetInitialPasswordService,
SetInitialPasswordTdeOffboardingCredentials,
SetInitialPasswordTdeUserWithPermissionCredentials,
SetInitialPasswordUserType,
} from "./set-initial-password.service.abstraction";
@@ -237,7 +241,7 @@ describe("DefaultSetInitialPasswordService", () => {
}
}
// Mock handleResetPasswordAutoEnroll() values
// Mock handleResetPasswordAutoEnrollOld() values
if (config.resetPasswordAutoEnroll) {
organizationApiService.getKeys.mockResolvedValue(organizationKeys);
encryptService.encapsulateKeyUnsigned.mockResolvedValue(orgPublicKeyEncryptedUserKey);
@@ -1104,4 +1108,285 @@ describe("DefaultSetInitialPasswordService", () => {
await expect(promise).rejects.toThrow("Unexpected V2 account cryptographic state");
});
});
describe("setInitialPasswordTdeUserWithPermission()", () => {
// Mock method parameters
let credentials: SetInitialPasswordTdeUserWithPermissionCredentials;
// Mock method data
let authenticationData: MasterPasswordAuthenticationData;
let unlockData: MasterPasswordUnlockData;
let setPasswordRequest: SetPasswordRequest;
let userDecryptionOptions: UserDecryptionOptions;
beforeEach(() => {
// Mock method parameters
credentials = {
newPassword: "newPassword123!",
salt: "user@example.com" as MasterPasswordSalt,
kdfConfig: DEFAULT_KDF_CONFIG,
newPasswordHint: "newPasswordHint",
orgSsoIdentifier: "orgSsoIdentifier",
orgId: "orgId" as OrganizationId,
resetPasswordAutoEnroll: false,
};
// Mock method data
userKey = makeSymmetricCryptoKey(64) as UserKey;
keyService.userKey$.mockReturnValue(of(userKey));
authenticationData = {
salt: credentials.salt,
kdf: credentials.kdfConfig,
masterPasswordAuthenticationHash:
"masterPasswordAuthenticationHash" as MasterPasswordAuthenticationHash,
};
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
authenticationData,
);
unlockData = {
salt: credentials.salt,
kdf: credentials.kdfConfig,
masterKeyWrappedUserKey: "masterKeyWrappedUserKey" as MasterKeyWrappedUserKey,
} as MasterPasswordUnlockData;
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(unlockData);
setPasswordRequest = SetPasswordRequest.newConstructor(
authenticationData,
unlockData,
credentials.newPasswordHint,
credentials.orgSsoIdentifier,
null, // no KeysRequest for TDE user because they already have a key pair
);
userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: false });
userDecryptionOptionsService.userDecryptionOptionsById$.mockReturnValue(
of(userDecryptionOptions),
);
});
describe("general error handling", () => {
["newPassword", "salt", "orgSsoIdentifier", "orgId"].forEach((key) => {
it(`should throw if ${key} is an empty string (falsy) on the SetInitialPasswordTdeUserWithPermissionCredentials object`, async () => {
// Arrange
const invalidCredentials: SetInitialPasswordTdeUserWithPermissionCredentials = {
...credentials,
[key]: "",
};
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(invalidCredentials, userId);
// Assert
await expect(promise).rejects.toThrow(
`${key} is falsy. Could not set initial password for TDE user with Manage Account Recovery permission.`,
);
});
});
["kdfConfig", "newPasswordHint", "resetPasswordAutoEnroll"].forEach((key) => {
it(`should throw if ${key} is null on the SetInitialPasswordTdeUserWithPermissionCredentials object`, async () => {
// Arrange
const invalidCredentials: SetInitialPasswordTdeUserWithPermissionCredentials = {
...credentials,
[key]: null,
};
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(invalidCredentials, userId);
// Assert
await expect(promise).rejects.toThrow(
`${key} is null or undefined. Could not set initial password for TDE user with Manage Account Recovery permission.`,
);
});
});
it("should throw if userId is not given", async () => {
// Arrange
userId = null;
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow(
"userId is falsy. Could not set initial password for TDE user with Manage Account Recovery permission.",
);
});
});
it("should throw if the userKey is not found", async () => {
// Arrange
keyService.userKey$.mockReturnValue(of(null));
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow("userKey not found.");
});
it("should call makeMasterPasswordAuthenticationData and makeMasterPasswordUnlockData with the correct parameters", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(masterPasswordService.makeMasterPasswordAuthenticationData).toHaveBeenCalledWith(
credentials.newPassword,
credentials.kdfConfig,
credentials.salt,
);
expect(masterPasswordService.makeMasterPasswordUnlockData).toHaveBeenCalledWith(
credentials.newPassword,
credentials.kdfConfig,
credentials.salt,
userKey,
);
});
it("should call the API method to set a master password", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(masterPasswordApiService.setPassword).toHaveBeenCalledTimes(1);
expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest);
});
describe("given the initial password has been successfully set", () => {
it("should clear the ForceSetPasswordReason by setting it to None", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(masterPasswordService.setForceSetPasswordReason).toHaveBeenCalledWith(
ForceSetPasswordReason.None,
userId,
);
});
it("should set MasterPasswordUnlockData to state", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(masterPasswordService.setMasterPasswordUnlockData).toHaveBeenCalledWith(
unlockData,
userId,
);
});
it("should update legacy state", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(userDecryptionOptionsService.setUserDecryptionOptionsById).toHaveBeenCalledWith(
userId,
expect.objectContaining({ hasMasterPassword: true }),
);
expect(kdfConfigService.setKdfConfig).toHaveBeenCalledWith(userId, credentials.kdfConfig);
expect(masterPasswordService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(
new EncString(unlockData.masterKeyWrappedUserKey),
userId,
);
expect(masterPasswordService.setLegacyMasterKeyFromUnlockData).toHaveBeenCalledWith(
credentials.newPassword,
unlockData,
userId,
);
});
describe("given resetPasswordAutoEnroll is false", () => {
it("should NOT handle reset password (account recovery) auto enroll", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(
organizationUserApiService.putOrganizationUserResetPasswordEnrollment,
).not.toHaveBeenCalled();
});
});
describe("given resetPasswordAutoEnroll is true", () => {
let organizationKeys: OrganizationKeysResponse;
let orgPublicKeyEncryptedUserKey: EncString;
let enrollmentRequest: OrganizationUserResetPasswordEnrollmentRequest;
beforeEach(() => {
credentials.resetPasswordAutoEnroll = true;
organizationKeys = {
privateKey: "orgPrivateKey",
publicKey: "orgPublicKey",
} as OrganizationKeysResponse;
organizationApiService.getKeys.mockResolvedValue(organizationKeys);
orgPublicKeyEncryptedUserKey = new EncString("orgPublicKeyEncryptedUserKey");
encryptService.encapsulateKeyUnsigned.mockResolvedValue(orgPublicKeyEncryptedUserKey);
enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest();
enrollmentRequest.masterPasswordHash =
authenticationData.masterPasswordAuthenticationHash;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString;
});
it("should throw if organization keys are not found", async () => {
// Arrange
organizationApiService.getKeys.mockResolvedValue(null);
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow(
"Organization keys response is null. Could not handle reset password auto enroll.",
);
});
it("should throw if orgPublicKeyEncryptedUserKey is not found", async () => {
// Arrange
encryptService.encapsulateKeyUnsigned.mockResolvedValue(null);
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow(
"orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.",
);
});
it("should throw if orgPublicKeyEncryptedUserKey.encryptedString is not found", async () => {
// Arrange
orgPublicKeyEncryptedUserKey.encryptedString = null;
// Act
const promise = sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
await expect(promise).rejects.toThrow(
"orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.",
);
});
it("should call the API method to handle reset password (account recovery) auto enroll", async () => {
// Act
await sut.setInitialPasswordTdeUserWithPermission(credentials, userId);
// Assert
expect(
organizationUserApiService.putOrganizationUserResetPasswordEnrollment,
).toHaveBeenCalledTimes(1);
expect(
organizationUserApiService.putOrganizationUserResetPasswordEnrollment,
).toHaveBeenCalledWith(credentials.orgId, userId, enrollmentRequest);
});
});
});
});
});

View File

@@ -47,6 +47,7 @@ import {
SetInitialPasswordCredentials,
SetInitialPasswordService,
SetInitialPasswordTdeOffboardingCredentials,
SetInitialPasswordTdeUserWithPermissionCredentials,
SetInitialPasswordUserType,
} from "./set-initial-password.service.abstraction";
@@ -183,7 +184,13 @@ export class SetInitialPasswordComponent implements OnInit {
break;
}
case SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP:
if (passwordInputResult.newApisWithInputPasswordFlagEnabled) {
await this.setInitialPasswordTdeUserWithPermission(passwordInputResult);
return; // EARLY RETURN for flagged logic
}
await this.setInitialPassword(passwordInputResult);
break;
case SetInitialPasswordUserType.OFFBOARDED_TDE_ORG_USER:
await this.setInitialPasswordTdeOffboarding(passwordInputResult);
@@ -382,6 +389,46 @@ export class SetInitialPasswordComponent implements OnInit {
}
}
private async setInitialPasswordTdeUserWithPermission(passwordInputResult: PasswordInputResult) {
const ctx =
"Could not set initial password for TDE user with Manage Account Recovery permission.";
assertTruthy(passwordInputResult.newPassword, "newPassword", ctx);
assertTruthy(passwordInputResult.salt, "salt", ctx);
assertNonNullish(passwordInputResult.kdfConfig, "kdfConfig", ctx);
assertNonNullish(passwordInputResult.newPasswordHint, "newPasswordHint", ctx); // can have an empty string as a valid value, so check non-nullish
assertTruthy(this.orgSsoIdentifier, "orgSsoIdentifier", ctx);
assertTruthy(this.orgId, "orgId", ctx);
assertNonNullish(this.resetPasswordAutoEnroll, "resetPasswordAutoEnroll", ctx); // can have `false` as a valid value, so check non-nullish
assertTruthy(this.userId, "userId", ctx);
try {
const credentials: SetInitialPasswordTdeUserWithPermissionCredentials = {
newPassword: passwordInputResult.newPassword,
salt: passwordInputResult.salt,
kdfConfig: passwordInputResult.kdfConfig,
newPasswordHint: passwordInputResult.newPasswordHint,
orgSsoIdentifier: this.orgSsoIdentifier,
orgId: this.orgId as OrganizationId,
resetPasswordAutoEnroll: this.resetPasswordAutoEnroll,
};
await this.setInitialPasswordService.setInitialPasswordTdeUserWithPermission(
credentials,
this.userId,
);
this.showSuccessToastByUserType();
this.submitting = false;
await this.router.navigate(["vault"]);
} catch (e) {
this.logService.error("Error setting initial password", e);
this.validationService.showError(e);
this.submitting = false;
}
}
private async setInitialPasswordTdeOffboarding(passwordInputResult: PasswordInputResult) {
const ctx = "Could not set initial password.";
assertTruthy(passwordInputResult.newMasterKey, "newMasterKey", ctx);

View File

@@ -55,6 +55,16 @@ export interface SetInitialPasswordCredentials {
salt: MasterPasswordSalt;
}
export interface SetInitialPasswordTdeUserWithPermissionCredentials {
newPassword: string;
salt: MasterPasswordSalt;
kdfConfig: KdfConfig;
newPasswordHint: string;
orgSsoIdentifier: string;
orgId: OrganizationId;
resetPasswordAutoEnroll: boolean;
}
export interface SetInitialPasswordTdeOffboardingCredentials {
newMasterKey: MasterKey;
newServerMasterKeyHash: string;
@@ -103,6 +113,19 @@ export abstract class SetInitialPasswordService {
userId: UserId,
) => Promise<void>;
/**
* Sets an initial password for an existing authed TDE user who has been given the
* Manage Account Recovery permission:
* - {@link SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP}
*
* @param credentials An object of the credentials needed to set the initial password
* @throws If any property on the `credentials` object not found, or if userKey is not found
*/
abstract setInitialPasswordTdeUserWithPermission: (
credentials: SetInitialPasswordTdeUserWithPermissionCredentials,
userId: UserId,
) => Promise<void>;
/**
* Sets an initial password for a user who logs in after their org offboarded from
* trusted device encryption and is now a master-password-encryption org: