mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-22 11:45:59 +01:00
[SG-656] Fix Trial Initiation Captcha Issue (#3481)
* [refactor] Isolate form validation logic * [refactor] Relocate a few input scrubbing lines * [refactor] Isolate RegisterRequest object construction logic * [refactor] Isolate account registration logic * [refactor] Isolate login logic * [fix] Check for captchas during login from trial initiation * [fix] Avoid a duplicated toast if the account was already created
This commit is contained in:
parent
d4581b0ba3
commit
65641a38b7
@ -113,16 +113,21 @@
|
||||
</div>
|
||||
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<bit-submit-button [loading]="form.loading">{{ "createAccount" | i18n }}</bit-submit-button>
|
||||
<a
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
routerLink="/login"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
<i class="bwi bwi-sign-in tw-mr-2"></i>
|
||||
{{ "logIn" | i18n }}
|
||||
</a>
|
||||
<ng-container *ngIf="!accountCreated">
|
||||
<bit-submit-button [loading]="form.loading">{{ "createAccount" | i18n }}</bit-submit-button>
|
||||
<a
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
routerLink="/login"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
<i class="bwi bwi-sign-in tw-mr-2"></i>
|
||||
{{ "logIn" | i18n }}
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="accountCreated">
|
||||
<bit-submit-button [loading]="form.loading">{{ "logIn" | i18n }}</bit-submit-button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<bit-error-summary *ngIf="showErrorSummary" [formGroup]="formGroup"></bit-error-summary>
|
||||
</div>
|
||||
|
@ -68,6 +68,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
|
||||
protected successRoute = "login";
|
||||
|
||||
protected accountCreated = false;
|
||||
|
||||
constructor(
|
||||
protected formValidationErrorService: FormValidationErrorsService,
|
||||
protected formBuilder: UntypedFormBuilder,
|
||||
@ -92,100 +94,33 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
|
||||
async submit(showToast = true) {
|
||||
let email = this.formGroup.get("email")?.value;
|
||||
let name = this.formGroup.get("name")?.value;
|
||||
const masterPassword = this.formGroup.get("masterPassword")?.value;
|
||||
const hint = this.formGroup.get("hint")?.value;
|
||||
|
||||
this.formGroup.markAllAsTouched();
|
||||
this.showErrorSummary = true;
|
||||
|
||||
if (this.formGroup.get("acceptPolicies").hasError("required")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("acceptPoliciesRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//web
|
||||
if (this.formGroup.invalid && !showToast) {
|
||||
return;
|
||||
}
|
||||
|
||||
//desktop, browser
|
||||
if (this.formGroup.invalid && showToast) {
|
||||
const errorText = this.getErrorToastMessage();
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errorText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) {
|
||||
const result = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("weakMasterPasswordDesc"),
|
||||
this.i18nService.t("weakMasterPassword"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
name = name === "" ? null : name;
|
||||
email = email.trim().toLowerCase();
|
||||
const kdf = DEFAULT_KDF_TYPE;
|
||||
const kdfIterations = DEFAULT_KDF_ITERATIONS;
|
||||
const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations);
|
||||
const encKey = await this.cryptoService.makeEncKey(key);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
|
||||
const request = new RegisterRequest(
|
||||
email,
|
||||
name,
|
||||
hashedPassword,
|
||||
hint,
|
||||
encKey[1].encryptedString,
|
||||
kdf,
|
||||
kdfIterations,
|
||||
this.referenceData,
|
||||
this.captchaToken
|
||||
);
|
||||
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
|
||||
const orgInvite = await this.stateService.getOrganizationInvitation();
|
||||
if (orgInvite != null && orgInvite.token != null && orgInvite.organizationUserId != null) {
|
||||
request.token = orgInvite.token;
|
||||
request.organizationUserId = orgInvite.organizationUserId;
|
||||
}
|
||||
|
||||
let name = this.formGroup.get("name")?.value;
|
||||
name = name === "" ? null : name; // Why do we do this?
|
||||
const masterPassword = this.formGroup.get("masterPassword")?.value;
|
||||
try {
|
||||
this.formPromise = this.apiService.postRegister(request);
|
||||
try {
|
||||
await this.formPromise;
|
||||
} catch (e) {
|
||||
if (this.handleCaptchaRequired(e)) {
|
||||
if (!this.accountCreated) {
|
||||
const registerResponse = await this.registerAccount(
|
||||
await this.buildRegisterRequest(email, masterPassword, name),
|
||||
showToast
|
||||
);
|
||||
if (registerResponse.captchaRequired) {
|
||||
return;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
this.accountCreated = true;
|
||||
}
|
||||
|
||||
if (this.isInTrialFlow) {
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("trialAccountCreated")
|
||||
);
|
||||
//login user here
|
||||
const credentials = new PasswordLogInCredentials(
|
||||
email,
|
||||
masterPassword,
|
||||
this.captchaToken,
|
||||
null
|
||||
);
|
||||
await this.authService.logIn(credentials);
|
||||
|
||||
if (!this.accountCreated) {
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("trialAccountCreated")
|
||||
);
|
||||
}
|
||||
const loginResponse = await this.logIn(email, masterPassword, this.captchaToken);
|
||||
if (loginResponse.captchaRequired) {
|
||||
return;
|
||||
}
|
||||
this.createdAccount.emit(this.formGroup.get("email")?.value);
|
||||
} else {
|
||||
this.platformUtilsService.showToast(
|
||||
@ -247,4 +182,111 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
return !ctrlValue && this.showTerms ? { required: true } : null;
|
||||
};
|
||||
}
|
||||
|
||||
private async validateRegistration(showToast: boolean) {
|
||||
this.formGroup.markAllAsTouched();
|
||||
this.showErrorSummary = true;
|
||||
|
||||
if (this.formGroup.get("acceptPolicies").hasError("required")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("acceptPoliciesRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//web
|
||||
if (this.formGroup.invalid && !showToast) {
|
||||
return;
|
||||
}
|
||||
|
||||
//desktop, browser
|
||||
if (this.formGroup.invalid && showToast) {
|
||||
const errorText = this.getErrorToastMessage();
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errorText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) {
|
||||
const result = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("weakMasterPasswordDesc"),
|
||||
this.i18nService.t("weakMasterPassword"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildRegisterRequest(
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
name: string
|
||||
): Promise<RegisterRequest> {
|
||||
const hint = this.formGroup.get("hint")?.value;
|
||||
const kdf = DEFAULT_KDF_TYPE;
|
||||
const kdfIterations = DEFAULT_KDF_ITERATIONS;
|
||||
const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations);
|
||||
const encKey = await this.cryptoService.makeEncKey(key);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
|
||||
const request = new RegisterRequest(
|
||||
email,
|
||||
name,
|
||||
hashedPassword,
|
||||
hint,
|
||||
encKey[1].encryptedString,
|
||||
kdf,
|
||||
kdfIterations,
|
||||
this.referenceData,
|
||||
this.captchaToken
|
||||
);
|
||||
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
|
||||
const orgInvite = await this.stateService.getOrganizationInvitation();
|
||||
if (orgInvite != null && orgInvite.token != null && orgInvite.organizationUserId != null) {
|
||||
request.token = orgInvite.token;
|
||||
request.organizationUserId = orgInvite.organizationUserId;
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private async registerAccount(
|
||||
request: RegisterRequest,
|
||||
showToast: boolean
|
||||
): Promise<{ captchaRequired: boolean }> {
|
||||
await this.validateRegistration(showToast);
|
||||
this.formPromise = this.apiService.postRegister(request);
|
||||
try {
|
||||
await this.formPromise;
|
||||
return { captchaRequired: false };
|
||||
} catch (e) {
|
||||
if (this.handleCaptchaRequired(e)) {
|
||||
return { captchaRequired: true };
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async logIn(
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
captchaBypassToken: string
|
||||
): Promise<{ captchaRequired: boolean }> {
|
||||
const credentials = new PasswordLogInCredentials(
|
||||
email,
|
||||
masterPassword,
|
||||
captchaBypassToken,
|
||||
null
|
||||
);
|
||||
const loginResponse = await this.authService.logIn(credentials);
|
||||
if (this.handleCaptchaRequired(loginResponse)) {
|
||||
return { captchaRequired: true };
|
||||
}
|
||||
return { captchaRequired: false };
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user