mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
chore(captcha): [PM-15162] Remove handling of captcha enforcement and bypass token
* Removed captcha references. * Removed connectors from webpack * Fixed extra parameter. * Resolve merge conflicts. * Fixed extra argument. * Fixed failing tests. * Fixed failing test. * Accessibility cookie cleanup * Cleaned up accessibility component. * Deleted old registration endpoint * Remove unused register request object. * Fixed merge error that changed font family. * Fixed formatting from merge. * Linting
This commit is contained in:
@@ -61,13 +61,11 @@ import { UpdateTwoFactorYubikeyOtpRequest } from "../auth/models/request/update-
|
||||
import { ApiKeyResponse } from "../auth/models/response/api-key.response";
|
||||
import { AuthRequestResponse } from "../auth/models/response/auth-request.response";
|
||||
import { DeviceVerificationResponse } from "../auth/models/response/device-verification.response";
|
||||
import { IdentityCaptchaResponse } from "../auth/models/response/identity-captcha.response";
|
||||
import { IdentityDeviceVerificationResponse } from "../auth/models/response/identity-device-verification.response";
|
||||
import { IdentityTokenResponse } from "../auth/models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../auth/models/response/identity-two-factor.response";
|
||||
import { KeyConnectorUserKeyResponse } from "../auth/models/response/key-connector-user-key.response";
|
||||
import { PreloginResponse } from "../auth/models/response/prelogin.response";
|
||||
import { RegisterResponse } from "../auth/models/response/register.response";
|
||||
import { SsoPreValidateResponse } from "../auth/models/response/sso-pre-validate.response";
|
||||
import { TwoFactorAuthenticatorResponse } from "../auth/models/response/two-factor-authenticator.response";
|
||||
import { TwoFactorDuoResponse } from "../auth/models/response/two-factor-duo.response";
|
||||
@@ -95,7 +93,6 @@ import { EventRequest } from "../models/request/event.request";
|
||||
import { KdfRequest } from "../models/request/kdf.request";
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { PreloginRequest } from "../models/request/prelogin.request";
|
||||
import { RegisterRequest } from "../models/request/register.request";
|
||||
import { StorageRequest } from "../models/request/storage.request";
|
||||
import { UpdateAvatarRequest } from "../models/request/update-avatar.request";
|
||||
import { UpdateDomainsRequest } from "../models/request/update-domains.request";
|
||||
@@ -147,10 +144,7 @@ export abstract class ApiService {
|
||||
| UserApiTokenRequest
|
||||
| WebAuthnLoginTokenRequest,
|
||||
) => Promise<
|
||||
| IdentityTokenResponse
|
||||
| IdentityTwoFactorResponse
|
||||
| IdentityCaptchaResponse
|
||||
| IdentityDeviceVerificationResponse
|
||||
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
|
||||
>;
|
||||
refreshIdentityToken: () => Promise<any>;
|
||||
|
||||
@@ -167,7 +161,6 @@ export abstract class ApiService {
|
||||
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
||||
getAccountRevisionDate: () => Promise<number>;
|
||||
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
||||
postRegister: (request: RegisterRequest) => Promise<RegisterResponse>;
|
||||
postPremium: (data: FormData) => Promise<PaymentResponse>;
|
||||
postReinstatePremium: () => Promise<any>;
|
||||
postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>;
|
||||
|
||||
@@ -47,10 +47,9 @@ export abstract class AccountApiService {
|
||||
* @param request - The request object containing the user's email verification token,
|
||||
* the email, hashed MP, newly created user key, and new asymmetric user key pair along
|
||||
* with the KDF information used during the process.
|
||||
* @returns A promise that resolves to a string captcha bypass token when the
|
||||
* registration process is successfully completed.
|
||||
* @returns A promise that resolves when the registration process is successfully completed.
|
||||
*/
|
||||
abstract registerFinish(request: RegisterFinishRequest): Promise<string>;
|
||||
abstract registerFinish(request: RegisterFinishRequest): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets the [dbo].[User].[VerifyDevices] flag to true or false.
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { I18nService } from "../platform/abstractions/i18n.service";
|
||||
|
||||
import { IFrameComponent } from "./iframe-component";
|
||||
|
||||
// TODO: PM-15162 - captcha is deprecated as part of UI refresh work
|
||||
export class CaptchaIFrame extends IFrameComponent {
|
||||
constructor(
|
||||
win: Window,
|
||||
webVaultUrl: string,
|
||||
private i18nService: I18nService,
|
||||
successCallback: (message: string) => any,
|
||||
errorCallback: (message: string) => any,
|
||||
infoCallback: (message: string) => any,
|
||||
) {
|
||||
super(
|
||||
win,
|
||||
webVaultUrl,
|
||||
"captcha-connector.html",
|
||||
"hcaptcha_iframe",
|
||||
successCallback,
|
||||
errorCallback,
|
||||
(message: string) => {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
if (typeof parsedMessage !== "string") {
|
||||
this.iframe.height = parsedMessage.height.toString();
|
||||
this.iframe.width = parsedMessage.width.toString();
|
||||
} else {
|
||||
infoCallback(parsedMessage);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
init(siteKey: string): void {
|
||||
super.initComponent(
|
||||
this.createParams({ siteKey: siteKey, locale: this.i18nService.translationLocale }, 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
export abstract class IFrameComponent {
|
||||
iframe: HTMLIFrameElement;
|
||||
private connectorLink: HTMLAnchorElement;
|
||||
private parseFunction = this.parseMessage.bind(this);
|
||||
|
||||
constructor(
|
||||
private win: Window,
|
||||
protected webVaultUrl: string,
|
||||
private path: string,
|
||||
private iframeId: string,
|
||||
public successCallback?: (message: string) => any,
|
||||
public errorCallback?: (message: string) => any,
|
||||
public infoCallback?: (message: string) => any,
|
||||
) {
|
||||
this.connectorLink = win.document.createElement("a");
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.sendMessage("stop");
|
||||
}
|
||||
|
||||
start() {
|
||||
this.sendMessage("start");
|
||||
}
|
||||
|
||||
sendMessage(message: any) {
|
||||
if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iframe.contentWindow.postMessage(message, this.iframe.src);
|
||||
}
|
||||
|
||||
base64Encode(str: string): string {
|
||||
return btoa(
|
||||
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => {
|
||||
return String.fromCharCode(("0x" + p1) as any);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.win.removeEventListener("message", this.parseFunction, false);
|
||||
}
|
||||
|
||||
protected createParams(data: any, version: number) {
|
||||
return new URLSearchParams({
|
||||
data: this.base64Encode(JSON.stringify(data)),
|
||||
parent: encodeURIComponent(this.win.document.location.href),
|
||||
v: version.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
protected initComponent(params: URLSearchParams): void {
|
||||
this.connectorLink.href = `${this.webVaultUrl}/${this.path}?${params}`;
|
||||
this.iframe = this.win.document.getElementById(this.iframeId) as HTMLIFrameElement;
|
||||
this.iframe.src = this.connectorLink.href;
|
||||
|
||||
this.win.addEventListener("message", this.parseFunction, false);
|
||||
}
|
||||
|
||||
private parseMessage(event: MessageEvent) {
|
||||
if (!this.validMessage(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parts: string[] = event.data.split("|");
|
||||
if (parts[0] === "success" && this.successCallback) {
|
||||
this.successCallback(parts[1]);
|
||||
} else if (parts[0] === "error" && this.errorCallback) {
|
||||
this.errorCallback(parts[1]);
|
||||
} else if (parts[0] === "info" && this.infoCallback) {
|
||||
this.infoCallback(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private validMessage(event: MessageEvent) {
|
||||
if (
|
||||
event.origin == null ||
|
||||
event.origin === "" ||
|
||||
event.origin !== (this.connectorLink as any).origin ||
|
||||
event.data == null ||
|
||||
typeof event.data !== "string"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
event.data.indexOf("success|") === 0 ||
|
||||
event.data.indexOf("error|") === 0 ||
|
||||
event.data.indexOf("info|") === 0
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { TwoFactorProviderType } from "../../enums/two-factor-provider-type";
|
||||
|
||||
export class AuthResult {
|
||||
userId: UserId;
|
||||
captchaSiteKey = "";
|
||||
// TODO: PM-3287 - Remove this after 3 releases of backwards compatibility. - Target release 2023.12 for removal
|
||||
/**
|
||||
* @deprecated
|
||||
@@ -21,10 +19,6 @@ export class AuthResult {
|
||||
requiresEncryptionKeyMigration: boolean;
|
||||
requiresDeviceVerification: boolean;
|
||||
|
||||
get requiresCaptcha() {
|
||||
return !Utils.isNullOrWhitespace(this.captchaSiteKey);
|
||||
}
|
||||
|
||||
get requiresTwoFactor() {
|
||||
return this.twoFactorProviders != null;
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
export abstract class CaptchaProtectedRequest {
|
||||
captchaResponse: string = null;
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import { ClientType } from "../../../../enums";
|
||||
import { Utils } from "../../../../platform/misc/utils";
|
||||
import { CaptchaProtectedRequest } from "../captcha-protected.request";
|
||||
|
||||
import { DeviceRequest } from "./device.request";
|
||||
import { TokenTwoFactorRequest } from "./token-two-factor.request";
|
||||
import { TokenRequest } from "./token.request";
|
||||
|
||||
export class PasswordTokenRequest extends TokenRequest implements CaptchaProtectedRequest {
|
||||
export class PasswordTokenRequest extends TokenRequest {
|
||||
constructor(
|
||||
public email: string,
|
||||
public masterPasswordHash: string,
|
||||
public captchaResponse: string,
|
||||
protected twoFactor: TokenTwoFactorRequest,
|
||||
device?: DeviceRequest,
|
||||
public newDeviceOtp?: string,
|
||||
@@ -25,10 +23,6 @@ export class PasswordTokenRequest extends TokenRequest implements CaptchaProtect
|
||||
obj.username = this.email;
|
||||
obj.password = this.masterPasswordHash;
|
||||
|
||||
if (this.captchaResponse != null) {
|
||||
obj.captchaResponse = this.captchaResponse;
|
||||
}
|
||||
|
||||
if (this.newDeviceOtp) {
|
||||
obj.newDeviceOtp = this.newDeviceOtp;
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface ICaptchaProtectedResponse {
|
||||
captchaBypassToken: string;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class IdentityCaptchaResponse extends BaseResponse {
|
||||
siteKey: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.siteKey = this.getResponseProperty("HCaptcha_SiteKey");
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,9 @@ import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||
|
||||
export class IdentityDeviceVerificationResponse extends BaseResponse {
|
||||
deviceVerified: boolean;
|
||||
captchaToken: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.deviceVerified = this.getResponseProperty("DeviceVerified") ?? false;
|
||||
|
||||
this.captchaToken = this.getResponseProperty("CaptchaBypassToken");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ export class IdentityTwoFactorResponse extends BaseResponse {
|
||||
twoFactorProviders: TwoFactorProviderType[];
|
||||
// a map of two-factor providers to necessary data for completion
|
||||
twoFactorProviders2: Record<TwoFactorProviderType, Record<string, string>>;
|
||||
captchaToken: string;
|
||||
ssoEmail2faSessionToken: string;
|
||||
email?: string;
|
||||
masterPasswordPolicy?: MasterPasswordPolicyResponse;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.captchaToken = this.getResponseProperty("CaptchaBypassToken");
|
||||
this.twoFactorProviders = this.getResponseProperty("TwoFactorProviders");
|
||||
this.twoFactorProviders2 = this.getResponseProperty("TwoFactorProviders2");
|
||||
this.masterPasswordPolicy = new MasterPasswordPolicyResponse(
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
import { ICaptchaProtectedResponse } from "./captcha-protected.response";
|
||||
|
||||
export class RegisterResponse extends BaseResponse implements ICaptchaProtectedResponse {
|
||||
captchaBypassToken: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.captchaBypassToken = this.getResponseProperty("CaptchaBypassToken");
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ export class AccountApiServiceImplementation implements AccountApiService {
|
||||
}
|
||||
}
|
||||
|
||||
async registerFinish(request: RegisterFinishRequest): Promise<string> {
|
||||
async registerFinish(request: RegisterFinishRequest): Promise<void> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { CaptchaProtectedRequest } from "../../auth/models/request/captcha-protected.request";
|
||||
|
||||
import { KeysRequest } from "./keys.request";
|
||||
import { ReferenceEventRequest } from "./reference-event.request";
|
||||
|
||||
export class RegisterRequest implements CaptchaProtectedRequest {
|
||||
masterPasswordHint: string;
|
||||
keys: KeysRequest;
|
||||
token: string;
|
||||
organizationUserId: string;
|
||||
|
||||
constructor(
|
||||
public email: string,
|
||||
public name: string,
|
||||
public masterPasswordHash: string,
|
||||
masterPasswordHint: string,
|
||||
public key: string,
|
||||
public referenceData: ReferenceEventRequest,
|
||||
public captchaResponse: string,
|
||||
public kdf: KdfType,
|
||||
public kdfIterations: number,
|
||||
public kdfMemory?: number,
|
||||
public kdfParallelism?: number,
|
||||
) {
|
||||
this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
|
||||
import { BaseResponse } from "./base.response";
|
||||
|
||||
export class ErrorResponse extends BaseResponse {
|
||||
message: string;
|
||||
validationErrors: { [key: string]: string[] };
|
||||
statusCode: number;
|
||||
captchaRequired: boolean;
|
||||
captchaSiteKey: string;
|
||||
|
||||
constructor(response: any, status: number, identityResponse?: boolean) {
|
||||
super(response);
|
||||
@@ -28,8 +24,6 @@ export class ErrorResponse extends BaseResponse {
|
||||
} else if (errorModel) {
|
||||
this.message = this.getResponseProperty("Message", errorModel);
|
||||
this.validationErrors = this.getResponseProperty("ValidationErrors", errorModel);
|
||||
this.captchaSiteKey = this.validationErrors?.HCaptcha_SiteKey?.[0];
|
||||
this.captchaRequired = !Utils.isNullOrWhitespace(this.captchaSiteKey);
|
||||
}
|
||||
this.statusCode = status;
|
||||
}
|
||||
|
||||
@@ -69,13 +69,11 @@ import { UpdateTwoFactorYubikeyOtpRequest } from "../auth/models/request/update-
|
||||
import { ApiKeyResponse } from "../auth/models/response/api-key.response";
|
||||
import { AuthRequestResponse } from "../auth/models/response/auth-request.response";
|
||||
import { DeviceVerificationResponse } from "../auth/models/response/device-verification.response";
|
||||
import { IdentityCaptchaResponse } from "../auth/models/response/identity-captcha.response";
|
||||
import { IdentityDeviceVerificationResponse } from "../auth/models/response/identity-device-verification.response";
|
||||
import { IdentityTokenResponse } from "../auth/models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../auth/models/response/identity-two-factor.response";
|
||||
import { KeyConnectorUserKeyResponse } from "../auth/models/response/key-connector-user-key.response";
|
||||
import { PreloginResponse } from "../auth/models/response/prelogin.response";
|
||||
import { RegisterResponse } from "../auth/models/response/register.response";
|
||||
import { SsoPreValidateResponse } from "../auth/models/response/sso-pre-validate.response";
|
||||
import { TwoFactorAuthenticatorResponse } from "../auth/models/response/two-factor-authenticator.response";
|
||||
import { TwoFactorDuoResponse } from "../auth/models/response/two-factor-duo.response";
|
||||
@@ -106,7 +104,6 @@ import { EventRequest } from "../models/request/event.request";
|
||||
import { KdfRequest } from "../models/request/kdf.request";
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { PreloginRequest } from "../models/request/prelogin.request";
|
||||
import { RegisterRequest } from "../models/request/register.request";
|
||||
import { StorageRequest } from "../models/request/storage.request";
|
||||
import { UpdateAvatarRequest } from "../models/request/update-avatar.request";
|
||||
import { UpdateDomainsRequest } from "../models/request/update-domains.request";
|
||||
@@ -200,10 +197,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
| SsoTokenRequest
|
||||
| WebAuthnLoginTokenRequest,
|
||||
): Promise<
|
||||
| IdentityTokenResponse
|
||||
| IdentityTwoFactorResponse
|
||||
| IdentityCaptchaResponse
|
||||
| IdentityDeviceVerificationResponse
|
||||
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
|
||||
> {
|
||||
const headers = new Headers({
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
||||
@@ -246,12 +240,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
Object.keys(responseJson.TwoFactorProviders2).length
|
||||
) {
|
||||
return new IdentityTwoFactorResponse(responseJson);
|
||||
} else if (
|
||||
response.status === 400 &&
|
||||
responseJson.HCaptcha_SiteKey &&
|
||||
Object.keys(responseJson.HCaptcha_SiteKey).length
|
||||
) {
|
||||
return new IdentityCaptchaResponse(responseJson);
|
||||
} else if (
|
||||
response.status === 400 &&
|
||||
responseJson?.ErrorModel?.Message === ApiService.NEW_DEVICE_VERIFICATION_REQUIRED_MESSAGE
|
||||
@@ -369,19 +357,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return this.send("POST", "/accounts/password-hint", request, false, false);
|
||||
}
|
||||
|
||||
async postRegister(request: RegisterRequest): Promise<RegisterResponse> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const r = await this.send(
|
||||
"POST",
|
||||
"/accounts/register",
|
||||
request,
|
||||
false,
|
||||
true,
|
||||
env.getIdentityUrl(),
|
||||
);
|
||||
return new RegisterResponse(r);
|
||||
}
|
||||
|
||||
async postPremium(data: FormData): Promise<PaymentResponse> {
|
||||
const r = await this.send("POST", "/accounts/premium", data, true, true);
|
||||
return new PaymentResponse(r);
|
||||
|
||||
Reference in New Issue
Block a user