mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-22 11:45:59 +01:00
Auth/PM-11945 - Registration with Email Verification - Fix Org Sponsored Free Family Plan not working (#11012)
* PM-11945 - AcceptOrg - fix inaccurate comment. * PM-11945 - Refactor new registration process to pass along orgSponsoredFreeFamilyPlanToken * PM-11945 - RegistrationFinishComponent - wire up passing of orgSponsoredFreeFamilyPlanToken to submit method * PM-11945 - Add todo
This commit is contained in:
parent
fc2c83f0d3
commit
b6cde7e3ef
@ -4,6 +4,11 @@ import { firstValueFrom } from "rxjs";
|
|||||||
|
|
||||||
import { BaseAcceptComponent } from "../../../common/base.accept.component";
|
import { BaseAcceptComponent } from "../../../common/base.accept.component";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This component is responsible for handling the acceptance of a families plan sponsorship invite.
|
||||||
|
* "Bitwarden allows all members of Enterprise Organizations to redeem a complimentary Families Plan with their
|
||||||
|
* personal email address." - https://bitwarden.com/learning/free-families-plan-for-enterprise/
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-accept-family-sponsorship",
|
selector: "app-accept-family-sponsorship",
|
||||||
templateUrl: "accept-family-sponsorship.component.html",
|
templateUrl: "accept-family-sponsorship.component.html",
|
||||||
@ -26,9 +31,32 @@ export class AcceptFamilySponsorshipComponent extends BaseAcceptComponent {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.router.navigate(["/login"], { queryParams: { email: qParams.email } });
|
this.router.navigate(["/login"], { queryParams: { email: qParams.email } });
|
||||||
} else {
|
} else {
|
||||||
// TODO: remove when email verification flag is removed
|
// TODO: update logic when email verification flag is removed
|
||||||
const registerRoute = await firstValueFrom(this.registerRoute$);
|
let queryParams: Params;
|
||||||
await this.router.navigate([registerRoute], { queryParams: { email: qParams.email } });
|
let registerRoute = await firstValueFrom(this.registerRoute$);
|
||||||
|
if (registerRoute === "/register") {
|
||||||
|
queryParams = {
|
||||||
|
email: qParams.email,
|
||||||
|
};
|
||||||
|
} else if (registerRoute === "/signup") {
|
||||||
|
// We have to override the base component route as we don't need users to
|
||||||
|
// complete email verification if they are coming directly an emailed invite.
|
||||||
|
|
||||||
|
// TODO: in the future, to allow users to enter a name, consider sending all invite users to
|
||||||
|
// start registration page with prefilled email and a named token to be passed directly
|
||||||
|
// along to the finish-signup page without requiring email verification as
|
||||||
|
// we can treat the existence of the token as a form of email verification.
|
||||||
|
|
||||||
|
registerRoute = "/finish-signup";
|
||||||
|
queryParams = {
|
||||||
|
email: qParams.email,
|
||||||
|
orgSponsoredFreeFamilyPlanToken: qParams.token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.router.navigate([registerRoute], {
|
||||||
|
queryParams: queryParams,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,17 +66,18 @@ export class WebRegistrationFinishService
|
|||||||
// Note: the org invite token and email verification are mutually exclusive. Only one will be present.
|
// Note: the org invite token and email verification are mutually exclusive. Only one will be present.
|
||||||
override async buildRegisterRequest(
|
override async buildRegisterRequest(
|
||||||
email: string,
|
email: string,
|
||||||
emailVerificationToken: string,
|
|
||||||
passwordInputResult: PasswordInputResult,
|
passwordInputResult: PasswordInputResult,
|
||||||
encryptedUserKey: EncryptedString,
|
encryptedUserKey: EncryptedString,
|
||||||
userAsymmetricKeys: [string, EncString],
|
userAsymmetricKeys: [string, EncString],
|
||||||
|
emailVerificationToken?: string,
|
||||||
|
orgSponsoredFreeFamilyPlanToken?: string,
|
||||||
): Promise<RegisterFinishRequest> {
|
): Promise<RegisterFinishRequest> {
|
||||||
const registerRequest = await super.buildRegisterRequest(
|
const registerRequest = await super.buildRegisterRequest(
|
||||||
email,
|
email,
|
||||||
emailVerificationToken,
|
|
||||||
passwordInputResult,
|
passwordInputResult,
|
||||||
encryptedUserKey,
|
encryptedUserKey,
|
||||||
userAsymmetricKeys,
|
userAsymmetricKeys,
|
||||||
|
emailVerificationToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
// web specific logic
|
// web specific logic
|
||||||
@ -89,6 +90,10 @@ export class WebRegistrationFinishService
|
|||||||
}
|
}
|
||||||
// Invite is accepted after login (on deep link redirect).
|
// Invite is accepted after login (on deep link redirect).
|
||||||
|
|
||||||
|
if (orgSponsoredFreeFamilyPlanToken) {
|
||||||
|
registerRequest.orgSponsoredFreeFamilyPlanToken = orgSponsoredFreeFamilyPlanToken;
|
||||||
|
}
|
||||||
|
|
||||||
return registerRequest;
|
return registerRequest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +99,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
|
|||||||
email: invite.email,
|
email: invite.email,
|
||||||
};
|
};
|
||||||
} else if (registerRoute === "/signup") {
|
} else if (registerRoute === "/signup") {
|
||||||
// We have to override the base component route b/c it is correct for other components
|
// We have to override the base component route as we don't need users to complete email verification
|
||||||
// that extend the base accept comp. We don't need users to complete email verification
|
|
||||||
// if they are coming directly from an emailed org invite.
|
// if they are coming directly from an emailed org invite.
|
||||||
registerRoute = "/finish-signup";
|
registerRoute = "/finish-signup";
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
@ -23,6 +23,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
|||||||
email: string,
|
email: string,
|
||||||
passwordInputResult: PasswordInputResult,
|
passwordInputResult: PasswordInputResult,
|
||||||
emailVerificationToken?: string,
|
emailVerificationToken?: string,
|
||||||
|
orgSponsoredFreeFamilyPlanToken?: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(
|
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(
|
||||||
passwordInputResult.masterKey,
|
passwordInputResult.masterKey,
|
||||||
@ -35,10 +36,11 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
|||||||
|
|
||||||
const registerRequest = await this.buildRegisterRequest(
|
const registerRequest = await this.buildRegisterRequest(
|
||||||
email,
|
email,
|
||||||
emailVerificationToken,
|
|
||||||
passwordInputResult,
|
passwordInputResult,
|
||||||
newEncUserKey.encryptedString,
|
newEncUserKey.encryptedString,
|
||||||
userAsymmetricKeys,
|
userAsymmetricKeys,
|
||||||
|
emailVerificationToken,
|
||||||
|
orgSponsoredFreeFamilyPlanToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest);
|
const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest);
|
||||||
@ -48,19 +50,19 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
|||||||
|
|
||||||
protected async buildRegisterRequest(
|
protected async buildRegisterRequest(
|
||||||
email: string,
|
email: string,
|
||||||
emailVerificationToken: string,
|
|
||||||
passwordInputResult: PasswordInputResult,
|
passwordInputResult: PasswordInputResult,
|
||||||
encryptedUserKey: EncryptedString,
|
encryptedUserKey: EncryptedString,
|
||||||
userAsymmetricKeys: [string, EncString],
|
userAsymmetricKeys: [string, EncString],
|
||||||
|
emailVerificationToken?: string,
|
||||||
|
orgSponsoredFreeFamilyPlanToken?: string, // web only
|
||||||
): Promise<RegisterFinishRequest> {
|
): Promise<RegisterFinishRequest> {
|
||||||
const userAsymmetricKeysRequest = new KeysRequest(
|
const userAsymmetricKeysRequest = new KeysRequest(
|
||||||
userAsymmetricKeys[0],
|
userAsymmetricKeys[0],
|
||||||
userAsymmetricKeys[1].encryptedString,
|
userAsymmetricKeys[1].encryptedString,
|
||||||
);
|
);
|
||||||
|
|
||||||
return new RegisterFinishRequest(
|
const registerFinishRequest = new RegisterFinishRequest(
|
||||||
email,
|
email,
|
||||||
emailVerificationToken,
|
|
||||||
passwordInputResult.masterKeyHash,
|
passwordInputResult.masterKeyHash,
|
||||||
passwordInputResult.hint,
|
passwordInputResult.hint,
|
||||||
encryptedUserKey,
|
encryptedUserKey,
|
||||||
@ -68,5 +70,11 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
|
|||||||
passwordInputResult.kdfConfig.kdfType,
|
passwordInputResult.kdfConfig.kdfType,
|
||||||
passwordInputResult.kdfConfig.iterations,
|
passwordInputResult.kdfConfig.iterations,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (emailVerificationToken) {
|
||||||
|
registerFinishRequest.emailVerificationToken = emailVerificationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return registerFinishRequest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,17 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
|
|||||||
submitting = false;
|
submitting = false;
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
// Note: this token is the email verification token. It is always supplied as a query param, but
|
// Note: this token is the email verification token. When it is supplied as a query param,
|
||||||
// it either comes from the email verification email or, if email verification is disabled server side
|
// it either comes from the email verification email or, if email verification is disabled server side
|
||||||
// via global settings, it comes directly from the registration-start component directly.
|
// via global settings, it comes directly from the registration-start component directly.
|
||||||
|
// It is not provided when the user is coming from another emailed invite (ex: org invite or enterprise
|
||||||
|
// org sponsored free family plan invite).
|
||||||
emailVerificationToken: string;
|
emailVerificationToken: string;
|
||||||
|
|
||||||
|
// this token is provided when the user is coming from an emailed invite to
|
||||||
|
// setup a free family plan sponsored by an organization but they don't have an account yet.
|
||||||
|
orgSponsoredFreeFamilyPlanToken: string;
|
||||||
|
|
||||||
masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null;
|
masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -69,6 +75,10 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
|
|||||||
if (qParams.token != null) {
|
if (qParams.token != null) {
|
||||||
this.emailVerificationToken = qParams.token;
|
this.emailVerificationToken = qParams.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qParams.orgSponsoredFreeFamilyPlanToken != null) {
|
||||||
|
this.orgSponsoredFreeFamilyPlanToken = qParams.orgSponsoredFreeFamilyPlanToken;
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
switchMap((qParams: Params) => {
|
switchMap((qParams: Params) => {
|
||||||
if (
|
if (
|
||||||
@ -100,6 +110,7 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
|
|||||||
this.email,
|
this.email,
|
||||||
passwordInputResult,
|
passwordInputResult,
|
||||||
this.emailVerificationToken,
|
this.emailVerificationToken,
|
||||||
|
this.orgSponsoredFreeFamilyPlanToken,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.validationService.showError(e);
|
this.validationService.showError(e);
|
||||||
|
@ -14,12 +14,14 @@ export abstract class RegistrationFinishService {
|
|||||||
*
|
*
|
||||||
* @param email The email address of the user.
|
* @param email The email address of the user.
|
||||||
* @param passwordInputResult The password input result.
|
* @param passwordInputResult The password input result.
|
||||||
* @param emailVerificationToken The optional email verification token. Not present in org invite scenarios.
|
* @param emailVerificationToken The optional email verification token. Not present in emailed invite scenarios (ex: org invite).
|
||||||
|
* @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token.
|
||||||
* Returns a promise which resolves to the captcha bypass token string upon a successful account creation.
|
* Returns a promise which resolves to the captcha bypass token string upon a successful account creation.
|
||||||
*/
|
*/
|
||||||
abstract finishRegistration(
|
abstract finishRegistration(
|
||||||
email: string,
|
email: string,
|
||||||
passwordInputResult: PasswordInputResult,
|
passwordInputResult: PasswordInputResult,
|
||||||
emailVerificationToken?: string,
|
emailVerificationToken?: string,
|
||||||
|
orgSponsoredFreeFamilyPlanToken?: string,
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import { EncryptedString } from "../../../../platform/models/domain/enc-string";
|
|||||||
export class RegisterFinishRequest {
|
export class RegisterFinishRequest {
|
||||||
constructor(
|
constructor(
|
||||||
public email: string,
|
public email: string,
|
||||||
public emailVerificationToken: string,
|
|
||||||
|
|
||||||
public masterPasswordHash: string,
|
public masterPasswordHash: string,
|
||||||
public masterPasswordHint: string,
|
public masterPasswordHint: string,
|
||||||
@ -18,6 +17,9 @@ export class RegisterFinishRequest {
|
|||||||
public kdfMemory?: number,
|
public kdfMemory?: number,
|
||||||
public kdfParallelism?: number,
|
public kdfParallelism?: number,
|
||||||
|
|
||||||
|
public emailVerificationToken?: string,
|
||||||
|
public orgSponsoredFreeFamilyPlanToken?: string,
|
||||||
|
|
||||||
// Org Invite data (only applies on web)
|
// Org Invite data (only applies on web)
|
||||||
public organizationUserId?: string,
|
public organizationUserId?: string,
|
||||||
public orgInviteToken?: string,
|
public orgInviteToken?: string,
|
||||||
|
Loading…
Reference in New Issue
Block a user