Skip to content

Commit

Permalink
[SG-656] Use a captcha bypass during registration (#3531)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
addisonbeck authored Sep 15, 2022
1 parent 734f052 commit 1fcba78
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 9 deletions.
14 changes: 9 additions & 5 deletions libs/angular/src/components/register.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInC
import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
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";

Expand All @@ -32,7 +33,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
@Output() createdAccount = new EventEmitter<string>();

showPassword = false;
formPromise: Promise<any>;
formPromise: Promise<RegisterResponse>;
referenceData: ReferenceEventRequest;
showTerms = true;
showErrorSummary = false;
Expand Down Expand Up @@ -70,6 +71,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn

protected accountCreated = false;

protected captchaBypassToken: string = null;

constructor(
protected formValidationErrorService: FormValidationErrorsService,
protected formBuilder: UntypedFormBuilder,
Expand Down Expand Up @@ -107,6 +110,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
if (!registerResponse.successful) {
return;
}
this.captchaBypassToken = registerResponse.captchaBypassToken;
this.accountCreated = true;
}
if (this.isInTrialFlow) {
Expand All @@ -117,7 +121,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
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) {
return;
}
Expand Down Expand Up @@ -258,14 +262,14 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
private async registerAccount(
request: RegisterRequest,
showToast: boolean
): Promise<{ successful: boolean }> {
): Promise<{ successful: boolean; captchaBypassToken?: string }> {
if (!(await this.validateRegistration(showToast)).isValid) {
return { successful: false };
}
this.formPromise = this.apiService.postRegister(request);
try {
await this.formPromise;
return { successful: true };
const response = await this.formPromise;
return { successful: true, captchaBypassToken: response.captchaBypassToken };
} catch (e) {
if (this.handleCaptchaRequired(e)) {
return { successful: false };
Expand Down
3 changes: 2 additions & 1 deletion libs/common/src/abstractions/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
import { AttachmentResponse } from "../models/response/attachmentResponse";
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
import { RegisterResponse } from "../models/response/authentication/registerResponse";
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
Expand Down Expand Up @@ -189,7 +190,7 @@ export abstract class ApiService {
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
getAccountRevisionDate: () => Promise<number>;
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
postRegister: (request: RegisterRequest) => Promise<any>;
postRegister: (request: RegisterRequest) => Promise<RegisterResponse>;
postPremium: (data: FormData) => Promise<PaymentResponse>;
postIapCheck: (request: IapCheckRequest) => Promise<any>;
postReinstatePremium: () => Promise<any>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ICaptchaProtectedResponse {
captchaBypassToken: string;
}
12 changes: 12 additions & 0 deletions libs/common/src/models/response/authentication/registerResponse.ts
Original file line number Diff line number Diff line change
@@ -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");
}
}
8 changes: 5 additions & 3 deletions libs/common/src/services/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
import { AttachmentResponse } from "../models/response/attachmentResponse";
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
import { RegisterResponse } from "../models/response/authentication/registerResponse";
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
Expand Down Expand Up @@ -337,17 +338,18 @@ export class ApiService implements ApiServiceAbstraction {
return this.send("POST", "/accounts/password-hint", request, false, false);
}

postRegister(request: RegisterRequest): Promise<any> {
return this.send(
async postRegister(request: RegisterRequest): Promise<RegisterResponse> {
const r = await this.send(
"POST",
"/accounts/register",
request,
false,
false,
true,
this.platformUtilsService.isDev()
? this.environmentService.getIdentityUrl()
: this.environmentService.getApiUrl()
);
return new RegisterResponse(r);
}

async postPremium(data: FormData): Promise<PaymentResponse> {
Expand Down

0 comments on commit 1fcba78

Please sign in to comment.