1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-21 11:53:34 +00:00

fix(register): [PM-27085] Account Register Uses New Data Types - Initial changes.

This commit is contained in:
Patrick Pimentel
2026-01-20 20:47:37 -05:00
parent b71ca1a2c0
commit 88fd21c7b7
6 changed files with 69 additions and 14 deletions

View File

@@ -18,7 +18,9 @@ import {
EncryptedString,
EncString,
} from "@bitwarden/common/key-management/crypto/models/enc-string";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
export class WebRegistrationFinishService
@@ -28,12 +30,13 @@ export class WebRegistrationFinishService
constructor(
protected keyService: KeyService,
protected accountApiService: AccountApiService,
protected masterPasswordService: MasterPasswordServiceAbstraction,
private organizationInviteService: OrganizationInviteService,
private policyApiService: PolicyApiServiceAbstraction,
private logService: LogService,
private policyService: PolicyService,
) {
super(keyService, accountApiService);
super(keyService, accountApiService, masterPasswordService);
}
override async getOrgNameFromOrgInvite(): Promise<string | null> {
@@ -78,6 +81,7 @@ export class WebRegistrationFinishService
// Note: the org invite token and email verification are mutually exclusive. Only one will be present.
override async buildRegisterRequest(
newUserKey: UserKey,
email: string,
passwordInputResult: PasswordInputResult,
encryptedUserKey: EncryptedString,
@@ -90,6 +94,7 @@ export class WebRegistrationFinishService
providerUserId?: string,
): Promise<RegisterFinishRequest> {
const registerRequest = await super.buildRegisterRequest(
newUserKey,
email,
passwordInputResult,
encryptedUserKey,

View File

@@ -73,7 +73,10 @@ import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-managemen
import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service";
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import {
InternalMasterPasswordServiceAbstraction,
MasterPasswordServiceAbstraction,
} from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout";
import {
VaultTimeout,
@@ -286,6 +289,7 @@ const safeProviders: SafeProvider[] = [
deps: [
KeyServiceAbstraction,
AccountApiServiceAbstraction,
MasterPasswordServiceAbstraction,
OrganizationInviteService,
PolicyApiServiceAbstraction,
LogService,

View File

@@ -1598,7 +1598,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: RegistrationFinishServiceAbstraction,
useClass: DefaultRegistrationFinishService,
deps: [KeyService, AccountApiServiceAbstraction],
deps: [KeyService, AccountApiServiceAbstraction, MasterPasswordServiceAbstraction],
}),
safeProvider({
provide: TwoFactorAuthComponentService,

View File

@@ -2,6 +2,11 @@ import { MockProxy, mock } from "jest-mock-extended";
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import {
MasterPasswordUnlockData,
MasterPasswordSalt,
} from "@bitwarden/common/key-management/master-password/types/master-password.types";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
@@ -16,12 +21,18 @@ describe("DefaultRegistrationFinishService", () => {
let keyService: MockProxy<KeyService>;
let accountApiService: MockProxy<AccountApiService>;
let masterPasswordService: MockProxy<MasterPasswordServiceAbstraction>;
beforeEach(() => {
keyService = mock<KeyService>();
accountApiService = mock<AccountApiService>();
masterPasswordService = mock<MasterPasswordServiceAbstraction>();
service = new DefaultRegistrationFinishService(keyService, accountApiService);
service = new DefaultRegistrationFinishService(
keyService,
accountApiService,
masterPasswordService,
);
});
it("instantiates", () => {
@@ -74,6 +85,7 @@ describe("DefaultRegistrationFinishService", () => {
it("throws an error if the user key cannot be created", async () => {
keyService.makeUserKey.mockResolvedValue([null, null]);
masterPasswordService.emailToSalt.mockReturnValue("salt" as unknown as MasterPasswordSalt);
await expect(service.finishRegistration(email, passwordInputResult)).rejects.toThrow(
"User key could not be created",
@@ -84,6 +96,19 @@ describe("DefaultRegistrationFinishService", () => {
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
accountApiService.registerFinish.mockResolvedValue();
masterPasswordService.emailToSalt.mockReturnValue("salt" as unknown as MasterPasswordSalt);
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue({
salt: "salt" as unknown as MasterPasswordSalt,
kdf: DEFAULT_KDF_CONFIG,
masterPasswordAuthenticationHash: "authHash" as unknown as string,
});
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(
new MasterPasswordUnlockData(
"salt" as unknown as MasterPasswordSalt,
DEFAULT_KDF_CONFIG,
new EncString("wrapped") as unknown as any,
),
);
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);

View File

@@ -7,7 +7,9 @@ import {
EncryptedString,
EncString,
} from "@bitwarden/common/key-management/crypto/models/enc-string";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
import { UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { PasswordInputResult } from "../../input-password/password-input-result";
@@ -18,6 +20,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
constructor(
protected keyService: KeyService,
protected accountApiService: AccountApiService,
protected masterPasswordService: MasterPasswordServiceAbstraction,
) {}
getOrgNameFromOrgInvite(): Promise<string | null> {
@@ -48,6 +51,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
const userAsymmetricKeys = await this.keyService.makeKeyPair(newUserKey);
const registerRequest = await this.buildRegisterRequest(
newUserKey,
email,
passwordInputResult,
newEncUserKey.encryptedString,
@@ -64,6 +68,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
}
protected async buildRegisterRequest(
newUserKey: UserKey,
email: string,
passwordInputResult: PasswordInputResult,
encryptedUserKey: EncryptedString,
@@ -80,14 +85,32 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
userAsymmetricKeys[1].encryptedString,
);
// Get salt value, for now we derive it from the email but this could change to be random bytes
// in the future once the email and salt are separated.
const salt = this.masterPasswordService.emailToSalt(email);
const masterPasswordAuthentication =
await this.masterPasswordService.makeMasterPasswordAuthenticationData(
passwordInputResult.newPassword,
passwordInputResult.kdfConfig,
salt,
);
const masterPasswordUnlock = await this.masterPasswordService.makeMasterPasswordUnlockData(
passwordInputResult.newPassword,
passwordInputResult.kdfConfig,
salt,
newUserKey,
);
const registerFinishRequest = new RegisterFinishRequest(
email,
passwordInputResult.newServerMasterKeyHash,
passwordInputResult.newPasswordHint,
encryptedUserKey,
userAsymmetricKeysRequest,
passwordInputResult.kdfConfig.kdfType,
passwordInputResult.kdfConfig.iterations,
masterPasswordAuthentication,
masterPasswordUnlock,
);
if (emailVerificationToken) {

View File

@@ -1,8 +1,8 @@
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { KdfType } from "@bitwarden/key-management";
import { EncryptedString } from "../../../../key-management/crypto/models/enc-string";
import {
MasterPasswordAuthenticationData,
MasterPasswordUnlockData,
} from "../../../../key-management/master-password/types/master-password.types";
import { KeysRequest } from "../../../../models/request/keys.request";
export class RegisterFinishRequest {
@@ -15,10 +15,8 @@ export class RegisterFinishRequest {
public userSymmetricKey: EncryptedString,
public userAsymmetricKeys: KeysRequest,
public kdf: KdfType,
public kdfIterations: number,
public kdfMemory?: number,
public kdfParallelism?: number,
public masterPasswordAuthentication: MasterPasswordAuthenticationData,
public masterPasswordUnlock: MasterPasswordUnlockData,
public emailVerificationToken?: string,
public orgSponsoredFreeFamilyPlanToken?: string,