1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 15:23:33 +00:00

Auth/PM-12613 - Registration with Email Verification - Provider Invite Flow (#11635)

* PM-12613 - AcceptProviderComp - Add support for new registration with email verification flow.

* PM-12613 - AcceptProviderComp - Reduce required params for finish registration to minimum

* PM-12613 - RegistrationFinish - Add passthrough logic for provider invite token

* PM-12613 - Update DefaultRegistrationFinishService finishRegistration tests to assert that all web only inputs are undefined on the outgoing request model

* PM-12613 - DefaultRegistrationFinishService - finishRegistration - Add missed mapping of optional properties into buildRegisterRequest

* PM-12613 - WebRegistrationFinishService - Add tests for additional token flows.
This commit is contained in:
Jared Snider
2024-10-24 17:21:06 -04:00
committed by GitHub
parent a0fe4f4ca6
commit 1fb1be56b3
9 changed files with 227 additions and 11 deletions

View File

@@ -20,7 +20,7 @@ import { OrganizationInvite } from "../../../organization-invite/organization-in
import { WebRegistrationFinishService } from "./web-registration-finish.service";
describe("DefaultRegistrationFinishService", () => {
describe("WebRegistrationFinishService", () => {
let service: WebRegistrationFinishService;
let keyService: MockProxy<KeyService>;
@@ -167,6 +167,11 @@ describe("DefaultRegistrationFinishService", () => {
let capchaBypassToken: string;
let orgInvite: OrganizationInvite;
let orgSponsoredFreeFamilyPlanToken: string;
let acceptEmergencyAccessInviteToken: string;
let emergencyAccessId: string;
let providerInviteToken: string;
let providerUserId: string;
beforeEach(() => {
email = "test@email.com";
@@ -190,6 +195,12 @@ describe("DefaultRegistrationFinishService", () => {
orgInvite = new OrganizationInvite();
orgInvite.organizationUserId = "organizationUserId";
orgInvite.token = "orgInviteToken";
orgSponsoredFreeFamilyPlanToken = "orgSponsoredFreeFamilyPlanToken";
acceptEmergencyAccessInviteToken = "acceptEmergencyAccessInviteToken";
emergencyAccessId = "emergencyAccessId";
providerInviteToken = "providerInviteToken";
providerUserId = "providerUserId";
});
it("throws an error if the user key cannot be created", async () => {
@@ -233,6 +244,11 @@ describe("DefaultRegistrationFinishService", () => {
kdfParallelism: undefined,
orgInviteToken: undefined,
organizationUserId: undefined,
orgSponsoredFreeFamilyPlanToken: undefined,
acceptEmergencyAccessInviteToken: undefined,
acceptEmergencyAccessId: undefined,
providerInviteToken: undefined,
providerUserId: undefined,
}),
);
});
@@ -266,6 +282,146 @@ describe("DefaultRegistrationFinishService", () => {
kdfParallelism: undefined,
orgInviteToken: orgInvite.token,
organizationUserId: orgInvite.organizationUserId,
orgSponsoredFreeFamilyPlanToken: undefined,
acceptEmergencyAccessInviteToken: undefined,
acceptEmergencyAccessId: undefined,
providerInviteToken: undefined,
providerUserId: undefined,
}),
);
});
it("registers the user and returns a captcha bypass token when given an org sponsored free family plan token", async () => {
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
accountApiService.registerFinish.mockResolvedValue(capchaBypassToken);
acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null);
const result = await service.finishRegistration(
email,
passwordInputResult,
undefined,
orgSponsoredFreeFamilyPlanToken,
);
expect(result).toEqual(capchaBypassToken);
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
expect.objectContaining({
email,
emailVerificationToken: undefined,
masterPasswordHash: passwordInputResult.masterKeyHash,
masterPasswordHint: passwordInputResult.hint,
userSymmetricKey: userKeyEncString.encryptedString,
userAsymmetricKeys: {
publicKey: userKeyPair[0],
encryptedPrivateKey: userKeyPair[1].encryptedString,
},
kdf: passwordInputResult.kdfConfig.kdfType,
kdfIterations: passwordInputResult.kdfConfig.iterations,
kdfMemory: undefined,
kdfParallelism: undefined,
orgInviteToken: undefined,
organizationUserId: undefined,
orgSponsoredFreeFamilyPlanToken: orgSponsoredFreeFamilyPlanToken,
acceptEmergencyAccessInviteToken: undefined,
acceptEmergencyAccessId: undefined,
providerInviteToken: undefined,
providerUserId: undefined,
}),
);
});
it("registers the user and returns a captcha bypass token when given an emergency access invite token", async () => {
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
accountApiService.registerFinish.mockResolvedValue(capchaBypassToken);
acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null);
const result = await service.finishRegistration(
email,
passwordInputResult,
undefined,
undefined,
acceptEmergencyAccessInviteToken,
emergencyAccessId,
);
expect(result).toEqual(capchaBypassToken);
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
expect.objectContaining({
email,
emailVerificationToken: undefined,
masterPasswordHash: passwordInputResult.masterKeyHash,
masterPasswordHint: passwordInputResult.hint,
userSymmetricKey: userKeyEncString.encryptedString,
userAsymmetricKeys: {
publicKey: userKeyPair[0],
encryptedPrivateKey: userKeyPair[1].encryptedString,
},
kdf: passwordInputResult.kdfConfig.kdfType,
kdfIterations: passwordInputResult.kdfConfig.iterations,
kdfMemory: undefined,
kdfParallelism: undefined,
orgInviteToken: undefined,
organizationUserId: undefined,
orgSponsoredFreeFamilyPlanToken: undefined,
acceptEmergencyAccessInviteToken: acceptEmergencyAccessInviteToken,
acceptEmergencyAccessId: emergencyAccessId,
providerInviteToken: undefined,
providerUserId: undefined,
}),
);
});
it("registers the user and returns a captcha bypass token when given a provider invite token", async () => {
keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]);
keyService.makeKeyPair.mockResolvedValue(userKeyPair);
accountApiService.registerFinish.mockResolvedValue(capchaBypassToken);
acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null);
const result = await service.finishRegistration(
email,
passwordInputResult,
undefined,
undefined,
undefined,
undefined,
providerInviteToken,
providerUserId,
);
expect(result).toEqual(capchaBypassToken);
expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey);
expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey);
expect(accountApiService.registerFinish).toHaveBeenCalledWith(
expect.objectContaining({
email,
emailVerificationToken: undefined,
masterPasswordHash: passwordInputResult.masterKeyHash,
masterPasswordHint: passwordInputResult.hint,
userSymmetricKey: userKeyEncString.encryptedString,
userAsymmetricKeys: {
publicKey: userKeyPair[0],
encryptedPrivateKey: userKeyPair[1].encryptedString,
},
kdf: passwordInputResult.kdfConfig.kdfType,
kdfIterations: passwordInputResult.kdfConfig.iterations,
kdfMemory: undefined,
kdfParallelism: undefined,
orgInviteToken: undefined,
organizationUserId: undefined,
orgSponsoredFreeFamilyPlanToken: undefined,
acceptEmergencyAccessInviteToken: undefined,
acceptEmergencyAccessId: undefined,
providerInviteToken: providerInviteToken,
providerUserId: providerUserId,
}),
);
});

View File

@@ -82,6 +82,8 @@ export class WebRegistrationFinishService
orgSponsoredFreeFamilyPlanToken?: string,
acceptEmergencyAccessInviteToken?: string,
emergencyAccessId?: string,
providerInviteToken?: string,
providerUserId?: string,
): Promise<RegisterFinishRequest> {
const registerRequest = await super.buildRegisterRequest(
email,
@@ -110,6 +112,11 @@ export class WebRegistrationFinishService
registerRequest.acceptEmergencyAccessId = emergencyAccessId;
}
if (providerInviteToken && providerUserId) {
registerRequest.providerInviteToken = providerInviteToken;
registerRequest.providerUserId = providerUserId;
}
return registerRequest;
}
}