mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[SG-656] Use a captcha bypass during registration (#3531)
* Use a captcha bypass during registration The trial initiation flow has a registration step that automatically does a login in the background. This has Captcha problems, namely that it can spawn two captchas in a row - one during registration and one during login. This is not ideal UX, so we've added a bypass token that returns from the registration endpoint that can be used to skip the next captcha. * [review] Introduce ICaptcheProtectedResponse
This commit is contained in:
@@ -21,6 +21,7 @@ import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInC
|
|||||||
import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
|
import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
|
||||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
|
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
|
||||||
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
|
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
|
||||||
|
import { RegisterResponse } from "@bitwarden/common/models/response/authentication/registerResponse";
|
||||||
|
|
||||||
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
@Output() createdAccount = new EventEmitter<string>();
|
@Output() createdAccount = new EventEmitter<string>();
|
||||||
|
|
||||||
showPassword = false;
|
showPassword = false;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<RegisterResponse>;
|
||||||
referenceData: ReferenceEventRequest;
|
referenceData: ReferenceEventRequest;
|
||||||
showTerms = true;
|
showTerms = true;
|
||||||
showErrorSummary = false;
|
showErrorSummary = false;
|
||||||
@@ -70,6 +71,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
|
|
||||||
protected accountCreated = false;
|
protected accountCreated = false;
|
||||||
|
|
||||||
|
protected captchaBypassToken: string = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected formValidationErrorService: FormValidationErrorsService,
|
protected formValidationErrorService: FormValidationErrorsService,
|
||||||
protected formBuilder: UntypedFormBuilder,
|
protected formBuilder: UntypedFormBuilder,
|
||||||
@@ -107,6 +110,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
if (!registerResponse.successful) {
|
if (!registerResponse.successful) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.captchaBypassToken = registerResponse.captchaBypassToken;
|
||||||
this.accountCreated = true;
|
this.accountCreated = true;
|
||||||
}
|
}
|
||||||
if (this.isInTrialFlow) {
|
if (this.isInTrialFlow) {
|
||||||
@@ -117,7 +121,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
this.i18nService.t("trialAccountCreated")
|
this.i18nService.t("trialAccountCreated")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const loginResponse = await this.logIn(email, masterPassword, this.captchaToken);
|
const loginResponse = await this.logIn(email, masterPassword, this.captchaBypassToken);
|
||||||
if (loginResponse.captchaRequired) {
|
if (loginResponse.captchaRequired) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -258,14 +262,14 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
private async registerAccount(
|
private async registerAccount(
|
||||||
request: RegisterRequest,
|
request: RegisterRequest,
|
||||||
showToast: boolean
|
showToast: boolean
|
||||||
): Promise<{ successful: boolean }> {
|
): Promise<{ successful: boolean; captchaBypassToken?: string }> {
|
||||||
if (!(await this.validateRegistration(showToast)).isValid) {
|
if (!(await this.validateRegistration(showToast)).isValid) {
|
||||||
return { successful: false };
|
return { successful: false };
|
||||||
}
|
}
|
||||||
this.formPromise = this.apiService.postRegister(request);
|
this.formPromise = this.apiService.postRegister(request);
|
||||||
try {
|
try {
|
||||||
await this.formPromise;
|
const response = await this.formPromise;
|
||||||
return { successful: true };
|
return { successful: true, captchaBypassToken: response.captchaBypassToken };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.handleCaptchaRequired(e)) {
|
if (this.handleCaptchaRequired(e)) {
|
||||||
return { successful: false };
|
return { successful: false };
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
|
|||||||
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
|
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
|
||||||
import { AttachmentResponse } from "../models/response/attachmentResponse";
|
import { AttachmentResponse } from "../models/response/attachmentResponse";
|
||||||
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
|
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
|
||||||
|
import { RegisterResponse } from "../models/response/authentication/registerResponse";
|
||||||
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
|
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
|
||||||
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
|
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
|
||||||
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
|
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
|
||||||
@@ -189,7 +190,7 @@ export abstract class ApiService {
|
|||||||
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
||||||
getAccountRevisionDate: () => Promise<number>;
|
getAccountRevisionDate: () => Promise<number>;
|
||||||
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
||||||
postRegister: (request: RegisterRequest) => Promise<any>;
|
postRegister: (request: RegisterRequest) => Promise<RegisterResponse>;
|
||||||
postPremium: (data: FormData) => Promise<PaymentResponse>;
|
postPremium: (data: FormData) => Promise<PaymentResponse>;
|
||||||
postIapCheck: (request: IapCheckRequest) => Promise<any>;
|
postIapCheck: (request: IapCheckRequest) => Promise<any>;
|
||||||
postReinstatePremium: () => Promise<any>;
|
postReinstatePremium: () => Promise<any>;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface ICaptchaProtectedResponse {
|
||||||
|
captchaBypassToken: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { BaseResponse } from "../baseResponse";
|
||||||
|
|
||||||
|
import { ICaptchaProtectedResponse } from "./ICaptchaProtectedResponse";
|
||||||
|
|
||||||
|
export class RegisterResponse extends BaseResponse implements ICaptchaProtectedResponse {
|
||||||
|
captchaBypassToken: string;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
this.captchaBypassToken = this.getResponseProperty("CaptchaBypassToken");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,6 +92,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
|
|||||||
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
|
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
|
||||||
import { AttachmentResponse } from "../models/response/attachmentResponse";
|
import { AttachmentResponse } from "../models/response/attachmentResponse";
|
||||||
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
|
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
|
||||||
|
import { RegisterResponse } from "../models/response/authentication/registerResponse";
|
||||||
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
|
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
|
||||||
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
|
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
|
||||||
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
|
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
|
||||||
@@ -337,17 +338,18 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send("POST", "/accounts/password-hint", request, false, false);
|
return this.send("POST", "/accounts/password-hint", request, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postRegister(request: RegisterRequest): Promise<any> {
|
async postRegister(request: RegisterRequest): Promise<RegisterResponse> {
|
||||||
return this.send(
|
const r = await this.send(
|
||||||
"POST",
|
"POST",
|
||||||
"/accounts/register",
|
"/accounts/register",
|
||||||
request,
|
request,
|
||||||
false,
|
false,
|
||||||
false,
|
true,
|
||||||
this.platformUtilsService.isDev()
|
this.platformUtilsService.isDev()
|
||||||
? this.environmentService.getIdentityUrl()
|
? this.environmentService.getIdentityUrl()
|
||||||
: this.environmentService.getApiUrl()
|
: this.environmentService.getApiUrl()
|
||||||
);
|
);
|
||||||
|
return new RegisterResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postPremium(data: FormData): Promise<PaymentResponse> {
|
async postPremium(data: FormData): Promise<PaymentResponse> {
|
||||||
|
|||||||
Reference in New Issue
Block a user