mirror of
https://github.com/bitwarden/browser
synced 2026-02-04 18:53:20 +00:00
feat(register): [PM-27085] Account Register Uses New Data Types - Added feature flag.
This commit is contained in:
@@ -9,19 +9,23 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||
import { RegisterFinishV2Request } from "@bitwarden/common/auth/models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
||||
import { OrganizationInvite } from "@bitwarden/common/auth/services/organization-invite/organization-invite";
|
||||
import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
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 { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
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";
|
||||
import { DEFAULT_KDF_CONFIG, KeyService } from "@bitwarden/key-management";
|
||||
import { DEFAULT_KDF_CONFIG, KeyService, KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { WebRegistrationFinishService } from "./web-registration-finish.service";
|
||||
|
||||
@@ -35,6 +39,7 @@ describe("WebRegistrationFinishService", () => {
|
||||
let logService: MockProxy<LogService>;
|
||||
let policyService: MockProxy<PolicyService>;
|
||||
let masterPasswordService: MockProxy<MasterPasswordServiceAbstraction>;
|
||||
let configService: MockProxy<ConfigService>;
|
||||
|
||||
beforeEach(() => {
|
||||
keyService = mock<KeyService>();
|
||||
@@ -43,13 +48,14 @@ describe("WebRegistrationFinishService", () => {
|
||||
policyApiService = mock<PolicyApiServiceAbstraction>();
|
||||
logService = mock<LogService>();
|
||||
policyService = mock<PolicyService>();
|
||||
|
||||
masterPasswordService = mock<MasterPasswordServiceAbstraction>();
|
||||
configService = mock<ConfigService>();
|
||||
|
||||
service = new WebRegistrationFinishService(
|
||||
keyService,
|
||||
accountApiService,
|
||||
masterPasswordService,
|
||||
configService,
|
||||
organizationInviteService,
|
||||
policyApiService,
|
||||
logService,
|
||||
@@ -228,226 +234,296 @@ describe("WebRegistrationFinishService", () => {
|
||||
|
||||
it("throws an error if the user key cannot be created", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([null, null]);
|
||||
configService.getFeatureFlag.mockResolvedValue(false);
|
||||
|
||||
await expect(service.finishRegistration(email, passwordInputResult)).rejects.toThrow(
|
||||
"User key could not be created",
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the user when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
describe("when feature flag is OFF (old API)", () => {
|
||||
beforeEach(() => {
|
||||
configService.getFeatureFlag.mockResolvedValue(false);
|
||||
});
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
it("registers the user with KDF fields when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(
|
||||
FeatureFlag.PM27044_UpdateRegistrationApis,
|
||||
);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
kdf: KdfType.PBKDF2_SHA256,
|
||||
kdfIterations: DEFAULT_KDF_CONFIG.iterations,
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify old API fields are present
|
||||
const registerCall = accountApiService.registerFinish.mock.calls[0][0];
|
||||
expect(registerCall).toBeInstanceOf(RegisterFinishRequest);
|
||||
expect((registerCall as RegisterFinishRequest).kdf).toBeDefined();
|
||||
expect((registerCall as RegisterFinishRequest).kdfIterations).toBeDefined();
|
||||
});
|
||||
|
||||
it("it registers the user with org invite when given an org invite", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(orgInvite);
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult);
|
||||
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
orgInviteToken: orgInvite.token,
|
||||
organizationUserId: orgInvite.organizationUserId,
|
||||
kdf: KdfType.PBKDF2_SHA256,
|
||||
kdfIterations: DEFAULT_KDF_CONFIG.iterations,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the user when given an org sponsored free family plan token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
orgInviteToken: undefined,
|
||||
organizationUserId: undefined,
|
||||
orgSponsoredFreeFamilyPlanToken: undefined,
|
||||
acceptEmergencyAccessInviteToken: undefined,
|
||||
acceptEmergencyAccessId: undefined,
|
||||
providerInviteToken: undefined,
|
||||
providerUserId: undefined,
|
||||
}),
|
||||
);
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
orgSponsoredFreeFamilyPlanToken,
|
||||
);
|
||||
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
orgSponsoredFreeFamilyPlanToken: orgSponsoredFreeFamilyPlanToken,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the user when given an emergency access invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
acceptEmergencyAccessInviteToken,
|
||||
emergencyAccessId,
|
||||
);
|
||||
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
acceptEmergencyAccessInviteToken: acceptEmergencyAccessInviteToken,
|
||||
acceptEmergencyAccessId: emergencyAccessId,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the user when given a provider invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
providerInviteToken,
|
||||
providerUserId,
|
||||
);
|
||||
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
providerInviteToken: providerInviteToken,
|
||||
providerUserId: providerUserId,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("it registers the user when given an org invite", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(orgInvite);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
describe("when feature flag is ON (new API)", () => {
|
||||
beforeEach(() => {
|
||||
configService.getFeatureFlag.mockResolvedValue(true);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
});
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult);
|
||||
it("registers the user with new data types when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(
|
||||
FeatureFlag.PM27044_UpdateRegistrationApis,
|
||||
);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify new API fields are present
|
||||
const registerCall = accountApiService.registerFinish.mock.calls[0][0];
|
||||
expect(registerCall).toBeInstanceOf(RegisterFinishV2Request);
|
||||
expect(
|
||||
(registerCall as RegisterFinishV2Request).masterPasswordAuthentication,
|
||||
).toBeDefined();
|
||||
expect((registerCall as RegisterFinishV2Request).masterPasswordUnlock).toBeDefined();
|
||||
|
||||
// Verify old API fields are NOT present
|
||||
expect((registerCall as any).kdf).toBeUndefined();
|
||||
expect((registerCall as any).kdfIterations).toBeUndefined();
|
||||
});
|
||||
|
||||
it("it registers the user with org invite when given an org invite", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(orgInvite);
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult);
|
||||
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
orgInviteToken: orgInvite.token,
|
||||
organizationUserId: orgInvite.organizationUserId,
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify new API fields are present
|
||||
const registerCall = accountApiService.registerFinish.mock.calls[0][0];
|
||||
expect(registerCall).toBeInstanceOf(RegisterFinishV2Request);
|
||||
});
|
||||
|
||||
it("registers the user when given an org sponsored free family plan token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
orgInviteToken: orgInvite.token,
|
||||
organizationUserId: orgInvite.organizationUserId,
|
||||
orgSponsoredFreeFamilyPlanToken: undefined,
|
||||
acceptEmergencyAccessInviteToken: undefined,
|
||||
acceptEmergencyAccessId: undefined,
|
||||
providerInviteToken: undefined,
|
||||
providerUserId: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
orgSponsoredFreeFamilyPlanToken,
|
||||
);
|
||||
|
||||
it("registers the user when given an org sponsored free family plan token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
orgSponsoredFreeFamilyPlanToken: orgSponsoredFreeFamilyPlanToken,
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
orgSponsoredFreeFamilyPlanToken,
|
||||
);
|
||||
it("registers the user when given an emergency access invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
orgInviteToken: undefined,
|
||||
organizationUserId: undefined,
|
||||
orgSponsoredFreeFamilyPlanToken: orgSponsoredFreeFamilyPlanToken,
|
||||
acceptEmergencyAccessInviteToken: undefined,
|
||||
acceptEmergencyAccessId: undefined,
|
||||
providerInviteToken: undefined,
|
||||
providerUserId: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
acceptEmergencyAccessInviteToken,
|
||||
emergencyAccessId,
|
||||
);
|
||||
|
||||
it("registers the user when given an emergency access invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
acceptEmergencyAccessInviteToken: acceptEmergencyAccessInviteToken,
|
||||
acceptEmergencyAccessId: emergencyAccessId,
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
acceptEmergencyAccessInviteToken,
|
||||
emergencyAccessId,
|
||||
);
|
||||
it("registers the user when given a provider invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
orgInviteToken: undefined,
|
||||
organizationUserId: undefined,
|
||||
orgSponsoredFreeFamilyPlanToken: undefined,
|
||||
acceptEmergencyAccessInviteToken: acceptEmergencyAccessInviteToken,
|
||||
acceptEmergencyAccessId: emergencyAccessId,
|
||||
providerInviteToken: undefined,
|
||||
providerUserId: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
providerInviteToken,
|
||||
providerUserId,
|
||||
);
|
||||
|
||||
it("registers the user when given a provider invite token", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
organizationInviteService.getOrganizationInvite.mockResolvedValue(null);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
|
||||
await service.finishRegistration(
|
||||
email,
|
||||
passwordInputResult,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
providerInviteToken,
|
||||
providerUserId,
|
||||
);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
orgInviteToken: undefined,
|
||||
organizationUserId: undefined,
|
||||
orgSponsoredFreeFamilyPlanToken: undefined,
|
||||
acceptEmergencyAccessInviteToken: undefined,
|
||||
acceptEmergencyAccessId: undefined,
|
||||
providerInviteToken: providerInviteToken,
|
||||
providerUserId: providerUserId,
|
||||
}),
|
||||
);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
providerInviteToken: providerInviteToken,
|
||||
providerUserId: providerUserId,
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||
import { RegisterFinishV2Request } from "@bitwarden/common/auth/models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
||||
import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service";
|
||||
import {
|
||||
@@ -19,6 +20,7 @@ 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 { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { UserKey } from "@bitwarden/common/types/key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
@@ -31,12 +33,13 @@ export class WebRegistrationFinishService
|
||||
protected keyService: KeyService,
|
||||
protected accountApiService: AccountApiService,
|
||||
protected masterPasswordService: MasterPasswordServiceAbstraction,
|
||||
protected configService: ConfigService,
|
||||
private organizationInviteService: OrganizationInviteService,
|
||||
private policyApiService: PolicyApiServiceAbstraction,
|
||||
private logService: LogService,
|
||||
private policyService: PolicyService,
|
||||
) {
|
||||
super(keyService, accountApiService, masterPasswordService);
|
||||
super(keyService, accountApiService, masterPasswordService, configService);
|
||||
}
|
||||
|
||||
override async getOrgNameFromOrgInvite(): Promise<string | null> {
|
||||
@@ -92,7 +95,7 @@ export class WebRegistrationFinishService
|
||||
emergencyAccessId?: string,
|
||||
providerInviteToken?: string,
|
||||
providerUserId?: string,
|
||||
): Promise<RegisterFinishRequest> {
|
||||
): Promise<RegisterFinishRequest | RegisterFinishV2Request> {
|
||||
const registerRequest = await super.buildRegisterRequest(
|
||||
newUserKey,
|
||||
email,
|
||||
|
||||
@@ -290,6 +290,7 @@ const safeProviders: SafeProvider[] = [
|
||||
KeyServiceAbstraction,
|
||||
AccountApiServiceAbstraction,
|
||||
MasterPasswordServiceAbstraction,
|
||||
ConfigService,
|
||||
OrganizationInviteService,
|
||||
PolicyApiServiceAbstraction,
|
||||
LogService,
|
||||
|
||||
@@ -1598,7 +1598,12 @@ const safeProviders: SafeProvider[] = [
|
||||
safeProvider({
|
||||
provide: RegistrationFinishServiceAbstraction,
|
||||
useClass: DefaultRegistrationFinishService,
|
||||
deps: [KeyService, AccountApiServiceAbstraction, MasterPasswordServiceAbstraction],
|
||||
deps: [
|
||||
KeyService,
|
||||
AccountApiServiceAbstraction,
|
||||
MasterPasswordServiceAbstraction,
|
||||
ConfigService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TwoFactorAuthComponentService,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
|
||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||
import { RegisterFinishV2Request } from "@bitwarden/common/auth/models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
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 {
|
||||
@@ -10,10 +13,11 @@ import {
|
||||
MasterPasswordSalt,
|
||||
MasterKeyWrappedUserKey,
|
||||
} from "@bitwarden/common/key-management/master-password/types/master-password.types";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
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";
|
||||
import { DEFAULT_KDF_CONFIG, KeyService } from "@bitwarden/key-management";
|
||||
import { DEFAULT_KDF_CONFIG, KeyService, KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { PasswordInputResult } from "../../input-password/password-input-result";
|
||||
|
||||
@@ -25,16 +29,19 @@ describe("DefaultRegistrationFinishService", () => {
|
||||
let keyService: MockProxy<KeyService>;
|
||||
let accountApiService: MockProxy<AccountApiService>;
|
||||
let masterPasswordService: MockProxy<MasterPasswordServiceAbstraction>;
|
||||
let configService: MockProxy<ConfigService>;
|
||||
|
||||
beforeEach(() => {
|
||||
keyService = mock<KeyService>();
|
||||
accountApiService = mock<AccountApiService>();
|
||||
masterPasswordService = mock<MasterPasswordServiceAbstraction>();
|
||||
configService = mock<ConfigService>();
|
||||
|
||||
service = new DefaultRegistrationFinishService(
|
||||
keyService,
|
||||
accountApiService,
|
||||
masterPasswordService,
|
||||
configService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -89,60 +96,126 @@ describe("DefaultRegistrationFinishService", () => {
|
||||
it("throws an error if the user key cannot be created", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([null, null]);
|
||||
masterPasswordService.emailToSalt.mockReturnValue("salt" as MasterPasswordSalt);
|
||||
configService.getFeatureFlag.mockResolvedValue(false);
|
||||
|
||||
await expect(service.finishRegistration(email, passwordInputResult)).rejects.toThrow(
|
||||
"User key could not be created",
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the user when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
const salt = "salt" as MasterPasswordSalt;
|
||||
const masterPasswordAuthentication: MasterPasswordAuthenticationData = {
|
||||
salt,
|
||||
kdf: DEFAULT_KDF_CONFIG,
|
||||
masterPasswordAuthenticationHash: "authHash" as MasterPasswordAuthenticationHash,
|
||||
};
|
||||
const masterPasswordUnlock = new MasterPasswordUnlockData(
|
||||
salt,
|
||||
DEFAULT_KDF_CONFIG,
|
||||
"wrappedUserKey" as MasterKeyWrappedUserKey,
|
||||
);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
describe("when feature flag is OFF (old API)", () => {
|
||||
beforeEach(() => {
|
||||
configService.getFeatureFlag.mockResolvedValue(false);
|
||||
});
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
it("registers the user with KDF fields when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
// Web only fields should be undefined
|
||||
orgInviteToken: undefined,
|
||||
organizationUserId: undefined,
|
||||
orgSponsoredFreeFamilyPlanToken: undefined,
|
||||
acceptEmergencyAccessInviteToken: undefined,
|
||||
acceptEmergencyAccessId: undefined,
|
||||
providerInviteToken: undefined,
|
||||
providerUserId: undefined,
|
||||
}),
|
||||
);
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(
|
||||
FeatureFlag.PM27044_UpdateRegistrationApis,
|
||||
);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
kdf: KdfType.PBKDF2_SHA256,
|
||||
kdfIterations: DEFAULT_KDF_CONFIG.iterations,
|
||||
kdfMemory: undefined,
|
||||
kdfParallelism: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify old API fields are present
|
||||
const registerCall = accountApiService.registerFinish.mock.calls[0][0];
|
||||
expect(registerCall).toBeInstanceOf(RegisterFinishRequest);
|
||||
expect((registerCall as RegisterFinishRequest).kdf).toBeDefined();
|
||||
expect((registerCall as RegisterFinishRequest).kdfIterations).toBeDefined();
|
||||
|
||||
// Verify new API fields are NOT present
|
||||
expect((registerCall as any).masterPasswordAuthentication).toBeUndefined();
|
||||
expect((registerCall as any).masterPasswordUnlock).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when feature flag is ON (new API)", () => {
|
||||
let salt: MasterPasswordSalt;
|
||||
let masterPasswordAuthentication: MasterPasswordAuthenticationData;
|
||||
let masterPasswordUnlock: MasterPasswordUnlockData;
|
||||
|
||||
beforeEach(() => {
|
||||
configService.getFeatureFlag.mockResolvedValue(true);
|
||||
|
||||
salt = "salt" as MasterPasswordSalt;
|
||||
masterPasswordAuthentication = {
|
||||
salt,
|
||||
kdf: DEFAULT_KDF_CONFIG,
|
||||
masterPasswordAuthenticationHash: "authHash" as MasterPasswordAuthenticationHash,
|
||||
};
|
||||
masterPasswordUnlock = new MasterPasswordUnlockData(
|
||||
salt,
|
||||
DEFAULT_KDF_CONFIG,
|
||||
"wrappedUserKey" as MasterKeyWrappedUserKey,
|
||||
);
|
||||
masterPasswordService.emailToSalt.mockReturnValue(salt);
|
||||
masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue(
|
||||
masterPasswordAuthentication,
|
||||
);
|
||||
masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(masterPasswordUnlock);
|
||||
});
|
||||
|
||||
it("registers the user with new data types when given valid email verification input", async () => {
|
||||
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
|
||||
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
|
||||
accountApiService.registerFinish.mockResolvedValue();
|
||||
|
||||
await service.finishRegistration(email, passwordInputResult, emailVerificationToken);
|
||||
|
||||
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(
|
||||
FeatureFlag.PM27044_UpdateRegistrationApis,
|
||||
);
|
||||
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
encryptedPrivateKey: userKeyPair[1].encryptedString,
|
||||
},
|
||||
masterPasswordAuthentication: masterPasswordAuthentication,
|
||||
masterPasswordUnlock: masterPasswordUnlock,
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify new API fields are present
|
||||
const registerCall = accountApiService.registerFinish.mock.calls[0][0];
|
||||
expect(registerCall).toBeInstanceOf(RegisterFinishV2Request);
|
||||
expect(
|
||||
(registerCall as RegisterFinishV2Request).masterPasswordAuthentication,
|
||||
).toBeDefined();
|
||||
expect((registerCall as RegisterFinishV2Request).masterPasswordUnlock).toBeDefined();
|
||||
|
||||
// Verify old API fields are NOT present
|
||||
expect((registerCall as any).kdf).toBeUndefined();
|
||||
expect((registerCall as any).kdfIterations).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
// @ts-strict-ignore
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||
import { RegisterFinishV2Request } from "@bitwarden/common/auth/models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "@bitwarden/common/auth/models/request/registration/register-finish.request";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
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 { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { UserKey } from "@bitwarden/common/types/key";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { Argon2KdfConfig, KeyService, KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { PasswordInputResult } from "../../input-password/password-input-result";
|
||||
|
||||
@@ -21,6 +24,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
||||
protected keyService: KeyService,
|
||||
protected accountApiService: AccountApiService,
|
||||
protected masterPasswordService: MasterPasswordServiceAbstraction,
|
||||
protected configService: ConfigService,
|
||||
) {}
|
||||
|
||||
getOrgNameFromOrgInvite(): Promise<string | null> {
|
||||
@@ -79,44 +83,72 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
||||
emergencyAccessId?: string, // web only
|
||||
providerInviteToken?: string, // web only
|
||||
providerUserId?: string, // web only
|
||||
): Promise<RegisterFinishRequest> {
|
||||
): Promise<RegisterFinishRequest | RegisterFinishV2Request> {
|
||||
const userAsymmetricKeysRequest = new KeysRequest(
|
||||
userAsymmetricKeys[0],
|
||||
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 useNewApi = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM27044_UpdateRegistrationApis,
|
||||
);
|
||||
|
||||
const masterPasswordAuthentication =
|
||||
await this.masterPasswordService.makeMasterPasswordAuthenticationData(
|
||||
if (useNewApi) {
|
||||
// New API path - use V2 request with new data types
|
||||
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 masterPasswordUnlock = await this.masterPasswordService.makeMasterPasswordUnlockData(
|
||||
passwordInputResult.newPassword,
|
||||
passwordInputResult.kdfConfig,
|
||||
salt,
|
||||
newUserKey,
|
||||
);
|
||||
const registerFinishRequest = new RegisterFinishV2Request(
|
||||
email,
|
||||
passwordInputResult.newServerMasterKeyHash,
|
||||
passwordInputResult.newPasswordHint,
|
||||
encryptedUserKey,
|
||||
userAsymmetricKeysRequest,
|
||||
masterPasswordAuthentication,
|
||||
masterPasswordUnlock,
|
||||
);
|
||||
|
||||
const registerFinishRequest = new RegisterFinishRequest(
|
||||
email,
|
||||
passwordInputResult.newServerMasterKeyHash,
|
||||
passwordInputResult.newPasswordHint,
|
||||
encryptedUserKey,
|
||||
userAsymmetricKeysRequest,
|
||||
masterPasswordAuthentication,
|
||||
masterPasswordUnlock,
|
||||
);
|
||||
if (emailVerificationToken) {
|
||||
registerFinishRequest.emailVerificationToken = emailVerificationToken;
|
||||
}
|
||||
|
||||
if (emailVerificationToken) {
|
||||
registerFinishRequest.emailVerificationToken = emailVerificationToken;
|
||||
return registerFinishRequest;
|
||||
} else {
|
||||
// Old API path - use original request with KDF fields
|
||||
const kdfConfig = passwordInputResult.kdfConfig;
|
||||
|
||||
const registerFinishRequest = new RegisterFinishRequest(
|
||||
email,
|
||||
passwordInputResult.newServerMasterKeyHash,
|
||||
passwordInputResult.newPasswordHint,
|
||||
encryptedUserKey,
|
||||
userAsymmetricKeysRequest,
|
||||
kdfConfig.kdfType,
|
||||
kdfConfig.iterations,
|
||||
kdfConfig.kdfType === KdfType.Argon2id ? (kdfConfig as Argon2KdfConfig).memory : undefined,
|
||||
kdfConfig.kdfType === KdfType.Argon2id
|
||||
? (kdfConfig as Argon2KdfConfig).parallelism
|
||||
: undefined,
|
||||
);
|
||||
|
||||
if (emailVerificationToken) {
|
||||
registerFinishRequest.emailVerificationToken = emailVerificationToken;
|
||||
}
|
||||
|
||||
return registerFinishRequest;
|
||||
}
|
||||
|
||||
return registerFinishRequest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { RegisterFinishV2Request } from "../models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "../models/request/registration/register-finish.request";
|
||||
import { RegisterSendVerificationEmailRequest } from "../models/request/registration/register-send-verification-email.request";
|
||||
import { RegisterVerificationEmailClickedRequest } from "../models/request/registration/register-verification-email-clicked.request";
|
||||
@@ -49,7 +50,7 @@ export abstract class AccountApiService {
|
||||
* with the KDF information used during the process.
|
||||
* @returns A promise that resolves when the registration process is successfully completed.
|
||||
*/
|
||||
abstract registerFinish(request: RegisterFinishRequest): Promise<void>;
|
||||
abstract registerFinish(request: RegisterFinishRequest | RegisterFinishV2Request): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets the [dbo].[User].[VerifyDevices] flag to true or false.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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 RegisterFinishV2Request {
|
||||
constructor(
|
||||
public email: string,
|
||||
|
||||
public masterPasswordHash: string,
|
||||
public masterPasswordHint: string,
|
||||
|
||||
public userSymmetricKey: EncryptedString,
|
||||
public userAsymmetricKeys: KeysRequest,
|
||||
|
||||
public masterPasswordAuthentication: MasterPasswordAuthenticationData,
|
||||
public masterPasswordUnlock: MasterPasswordUnlockData,
|
||||
|
||||
public emailVerificationToken?: string,
|
||||
public orgSponsoredFreeFamilyPlanToken?: string,
|
||||
public acceptEmergencyAccessInviteToken?: string,
|
||||
public acceptEmergencyAccessId?: string,
|
||||
public providerInviteToken?: string,
|
||||
public providerUserId?: string,
|
||||
|
||||
// Org Invite data (only applies on web)
|
||||
public organizationUserId?: string,
|
||||
public orgInviteToken?: string,
|
||||
) {}
|
||||
}
|
||||
@@ -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,8 +15,10 @@ export class RegisterFinishRequest {
|
||||
public userSymmetricKey: EncryptedString,
|
||||
public userAsymmetricKeys: KeysRequest,
|
||||
|
||||
public masterPasswordAuthentication: MasterPasswordAuthenticationData,
|
||||
public masterPasswordUnlock: MasterPasswordUnlockData,
|
||||
public kdf: KdfType,
|
||||
public kdfIterations: number,
|
||||
public kdfMemory?: number,
|
||||
public kdfParallelism?: number,
|
||||
|
||||
public emailVerificationToken?: string,
|
||||
public orgSponsoredFreeFamilyPlanToken?: string,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { AccountApiService } from "../abstractions/account-api.service";
|
||||
import { InternalAccountService } from "../abstractions/account.service";
|
||||
import { UserVerificationService } from "../abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { RegisterFinishV2Request } from "../models/request/registration/register-finish-v2.request";
|
||||
import { RegisterFinishRequest } from "../models/request/registration/register-finish.request";
|
||||
import { RegisterSendVerificationEmailRequest } from "../models/request/registration/register-send-verification-email.request";
|
||||
import { RegisterVerificationEmailClickedRequest } from "../models/request/registration/register-verification-email-clicked.request";
|
||||
@@ -84,7 +85,7 @@ export class AccountApiServiceImplementation implements AccountApiService {
|
||||
}
|
||||
}
|
||||
|
||||
async registerFinish(request: RegisterFinishRequest): Promise<void> {
|
||||
async registerFinish(request: RegisterFinishRequest | RegisterFinishV2Request): Promise<void> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
|
||||
try {
|
||||
|
||||
@@ -46,6 +46,7 @@ export enum FeatureFlag {
|
||||
ConsolidatedSessionTimeoutComponent = "pm-26056-consolidated-session-timeout-component",
|
||||
PM27279_V2RegistrationTdeJit = "pm-27279-v2-registration-tde-jit",
|
||||
EnableAccountEncryptionV2KeyConnectorRegistration = "enable-account-encryption-v2-key-connector-registration",
|
||||
PM27044_UpdateRegistrationApis = "pm-27044-update-registration-apis",
|
||||
|
||||
/* Tools */
|
||||
UseSdkPasswordGenerators = "pm-19976-use-sdk-password-generators",
|
||||
@@ -153,6 +154,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.ConsolidatedSessionTimeoutComponent]: FALSE,
|
||||
[FeatureFlag.PM27279_V2RegistrationTdeJit]: FALSE,
|
||||
[FeatureFlag.EnableAccountEncryptionV2KeyConnectorRegistration]: FALSE,
|
||||
[FeatureFlag.PM27044_UpdateRegistrationApis]: FALSE,
|
||||
|
||||
/* Platform */
|
||||
[FeatureFlag.IpcChannelFramework]: FALSE,
|
||||
|
||||
Reference in New Issue
Block a user