From 4191bb9533d454ad895cf240dcce9467c00792d8 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Fri, 9 May 2025 10:44:11 -0400 Subject: [PATCH] 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 --- apps/browser/src/popup/scss/misc.scss | 6 - apps/cli/src/auth/commands/login.command.ts | 69 +------- apps/desktop/src/app/app-routing.module.ts | 2 - apps/desktop/src/app/app.module.ts | 2 - .../auth/accessibility-cookie.component.html | 34 ---- .../auth/accessibility-cookie.component.ts | 76 --------- apps/desktop/src/auth/preload.ts | 3 - apps/desktop/src/locales/en/messages.json | 31 ---- apps/desktop/src/scss/misc.scss | 6 - apps/desktop/src/scss/pages.scss | 2 - .../electron-main-messaging.service.ts | 4 - .../web-registration-finish.service.spec.ts | 46 ++--- .../app/auth/recover-two-factor.component.ts | 7 +- .../complete-trial-initiation.component.ts | 20 +-- apps/web/src/connectors/captcha-mobile.html | 23 --- apps/web/src/connectors/captcha-mobile.scss | 1 - apps/web/src/connectors/captcha.html | 17 -- apps/web/src/connectors/captcha.scss | 8 - apps/web/src/connectors/captcha.ts | 158 ------------------ apps/web/src/scss/plugins.scss | 6 - apps/web/webpack.config.js | 11 -- .../components/captcha-protected.component.ts | 67 -------- .../src/directives/api-action.directive.ts | 6 - ...efault-registration-finish.service.spec.ts | 14 +- .../default-registration-finish.service.ts | 6 +- .../registration-finish.component.ts | 10 +- .../registration-finish.service.ts | 4 +- .../two-factor-auth.component.spec.ts | 1 - .../two-factor-auth.component.ts | 1 - .../abstractions/login-strategy.service.ts | 11 +- .../auth-request-login.strategy.ts | 8 +- .../login-strategies/login.strategy.spec.ts | 31 ---- .../common/login-strategies/login.strategy.ts | 22 +-- .../password-login.strategy.spec.ts | 14 +- .../password-login.strategy.ts | 19 +-- .../login-strategies/sso-login.strategy.ts | 1 - .../user-api-login.strategy.ts | 1 - .../webauthn-login.strategy.spec.ts | 2 - .../webauthn-login.strategy.ts | 1 - .../common/models/domain/login-credentials.ts | 2 - .../login-strategy.service.spec.ts | 4 +- .../login-strategy.service.ts | 9 +- .../login-strategy.state.spec.ts | 3 +- libs/common/src/abstractions/api.service.ts | 9 +- .../auth/abstractions/account-api.service.ts | 5 +- libs/common/src/auth/captcha-iframe.ts | 39 ----- libs/common/src/auth/iframe-component.ts | 96 ----------- .../src/auth/models/domain/auth-result.ts | 6 - .../request/captcha-protected.request.ts | 5 - .../identity-token/password-token.request.ts | 8 +- .../response/captcha-protected.response.ts | 3 - .../response/identity-captcha.response.ts | 10 -- .../identity-device-verification.response.ts | 3 - .../response/identity-two-factor.response.ts | 2 - .../auth/models/response/register.response.ts | 12 -- .../src/auth/services/account-api.service.ts | 2 +- .../src/models/request/register.request.ts | 31 ---- .../src/models/response/error.response.ts | 6 - libs/common/src/services/api.service.ts | 27 +-- 59 files changed, 56 insertions(+), 977 deletions(-) delete mode 100644 apps/desktop/src/auth/accessibility-cookie.component.html delete mode 100644 apps/desktop/src/auth/accessibility-cookie.component.ts delete mode 100644 apps/web/src/connectors/captcha-mobile.html delete mode 100644 apps/web/src/connectors/captcha-mobile.scss delete mode 100644 apps/web/src/connectors/captcha.html delete mode 100644 apps/web/src/connectors/captcha.scss delete mode 100644 apps/web/src/connectors/captcha.ts delete mode 100644 libs/angular/src/auth/components/captcha-protected.component.ts delete mode 100644 libs/common/src/auth/captcha-iframe.ts delete mode 100644 libs/common/src/auth/iframe-component.ts delete mode 100644 libs/common/src/auth/models/request/captcha-protected.request.ts delete mode 100644 libs/common/src/auth/models/response/captcha-protected.response.ts delete mode 100644 libs/common/src/auth/models/response/identity-captcha.response.ts delete mode 100644 libs/common/src/auth/models/response/register.response.ts delete mode 100644 libs/common/src/models/request/register.request.ts diff --git a/apps/browser/src/popup/scss/misc.scss b/apps/browser/src/popup/scss/misc.scss index 8aace90d0a6..006e1d35f6a 100644 --- a/apps/browser/src/popup/scss/misc.scss +++ b/apps/browser/src/popup/scss/misc.scss @@ -211,12 +211,6 @@ p.lead { } } -#hcaptcha_iframe { - width: 100%; - border: none; - transition: height 0.25s linear; -} - body.linux-webauthn { width: 485px !important; #web-authn-frame { diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index b26ce94207d..3ad71c62e66 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -33,7 +33,6 @@ import { ClientType } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -222,7 +221,7 @@ export class LoginCommand { ); } else { response = await this.loginStrategyService.logIn( - new PasswordLoginCredentials(email, password, null, twoFactor), + new PasswordLoginCredentials(email, password, twoFactor), ); } if (response.requiresEncryptionKeyMigration) { @@ -230,17 +229,6 @@ export class LoginCommand { "Encryption key migration required. Please login through the web vault to update your encryption key.", ); } - if (response.captchaSiteKey) { - const credentials = new PasswordLoginCredentials(email, password); - const handledResponse = await this.handleCaptchaRequired(twoFactor, credentials); - - // Error Response - if (handledResponse instanceof Response) { - return handledResponse; - } else { - response = handledResponse; - } - } if (response.requiresTwoFactor) { const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null); if (twoFactorProviders.length === 0) { @@ -312,7 +300,6 @@ export class LoginCommand { response = await this.loginStrategyService.logInTwoFactor( new TokenTwoFactorRequest(selectedProvider.type, twoFactorToken), - null, ); } @@ -336,18 +323,6 @@ export class LoginCommand { response = await this.loginStrategyService.logInNewDeviceVerification(newDeviceToken); } - if (response.captchaSiteKey) { - const twoFactorRequest = new TokenTwoFactorRequest(selectedProvider.type, twoFactorToken); - const handledResponse = await this.handleCaptchaRequired(twoFactorRequest); - - // Error Response - if (handledResponse instanceof Response) { - return handledResponse; - } else { - response = handledResponse; - } - } - if (response.requiresTwoFactor) { return Response.error("Login failed."); } @@ -629,48 +604,6 @@ export class LoginCommand { return { newPasswordHash, newUserKey: newUserKey, hint: masterPasswordHint }; } - private async handleCaptchaRequired( - twoFactorRequest: TokenTwoFactorRequest, - credentials: PasswordLoginCredentials = null, - ): Promise { - const badCaptcha = Response.badRequest( - "Your authentication request has been flagged and will require user interaction to proceed.\n" + - "Please use your API key to validate this request and ensure BW_CLIENTSECRET is correct, if set.\n" + - "(https://bitwarden.com/help/cli-auth-challenges)", - ); - - try { - const captchaClientSecret = await this.apiClientSecret(true); - if (Utils.isNullOrWhitespace(captchaClientSecret)) { - return badCaptcha; - } - - let authResultResponse: AuthResult = null; - if (credentials != null) { - credentials.captchaToken = captchaClientSecret; - credentials.twoFactor = twoFactorRequest; - authResultResponse = await this.loginStrategyService.logIn(credentials); - } else { - authResultResponse = await this.loginStrategyService.logInTwoFactor( - twoFactorRequest, - captchaClientSecret, - ); - } - - return authResultResponse; - } catch (e) { - if ( - e instanceof ErrorResponse || - (e.constructor.name === ErrorResponse.name && - (e as ErrorResponse).message.includes("Captcha is invalid")) - ) { - return badCaptcha; - } else { - return Response.error(e); - } - } - } - private async apiClientId(): Promise { let clientId: string = null; diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 70c40ee8f6f..938edafddd4 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -44,7 +44,6 @@ import { import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { LockComponent } from "@bitwarden/key-management-ui"; -import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { SetPasswordComponent } from "../auth/set-password.component"; import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component"; @@ -111,7 +110,6 @@ const routes: Routes = [ canActivate: [authGuard], }, }), - { path: "accessibility-cookie", component: AccessibilityCookieComponent }, { path: "set-password", component: SetPasswordComponent }, { path: "send", diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index 15ab4350bbc..9b2472106dd 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -10,7 +10,6 @@ import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password- import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; import { CalloutModule, DialogModule } from "@bitwarden/components"; -import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component"; import { DeleteAccountComponent } from "../auth/delete-account.component"; import { LoginModule } from "../auth/login/login.module"; import { SetPasswordComponent } from "../auth/set-password.component"; @@ -59,7 +58,6 @@ import { SharedModule } from "./shared/shared.module"; VaultV2Component, ], declarations: [ - AccessibilityCookieComponent, AccountSwitcherComponent, AddEditComponent, AddEditCustomFieldsComponent, diff --git a/apps/desktop/src/auth/accessibility-cookie.component.html b/apps/desktop/src/auth/accessibility-cookie.component.html deleted file mode 100644 index e81f754cd74..00000000000 --- a/apps/desktop/src/auth/accessibility-cookie.component.html +++ /dev/null @@ -1,34 +0,0 @@ - diff --git a/apps/desktop/src/auth/accessibility-cookie.component.ts b/apps/desktop/src/auth/accessibility-cookie.component.ts deleted file mode 100644 index ccaacfe757c..00000000000 --- a/apps/desktop/src/auth/accessibility-cookie.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, NgZone } from "@angular/core"; -import { UntypedFormControl, UntypedFormGroup, Validators } from "@angular/forms"; -import { Router } from "@angular/router"; - -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { ToastService } from "@bitwarden/components"; - -@Component({ - selector: "app-accessibility-cookie", - templateUrl: "accessibility-cookie.component.html", -}) -export class AccessibilityCookieComponent { - listenForCookie = false; - hCaptchaWindow: Window; - - accessibilityForm = new UntypedFormGroup({ - link: new UntypedFormControl("", Validators.required), - }); - - constructor( - protected router: Router, - protected platformUtilsService: PlatformUtilsService, - protected environmentService: EnvironmentService, - protected i18nService: I18nService, - protected ngZone: NgZone, - private toastService: ToastService, - ) {} - - registerhCaptcha() { - this.platformUtilsService.launchUri("https://www.hcaptcha.com/accessibility"); - } - - async close() { - const [cookie] = await ipc.auth.getHcaptchaAccessibilityCookie(); - if (cookie) { - this.onCookieSavedSuccess(); - } else { - this.onCookieSavedFailure(); - } - await this.router.navigate(["/login"]); - } - - onCookieSavedSuccess() { - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("accessibilityCookieSaved"), - }); - } - - onCookieSavedFailure() { - this.toastService.showToast({ - variant: "error", - title: null, - message: this.i18nService.t("noAccessibilityCookieSaved"), - }); - } - - async submit() { - if (Utils.getHostname(this.accessibilityForm.value.link) !== "accounts.hcaptcha.com") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("invalidUrl"), - }); - return; - } - this.listenForCookie = true; - window.open(this.accessibilityForm.value.link, "_blank", "noopener noreferrer"); - } -} diff --git a/apps/desktop/src/auth/preload.ts b/apps/desktop/src/auth/preload.ts index 91321d62d82..7c213934659 100644 --- a/apps/desktop/src/auth/preload.ts +++ b/apps/desktop/src/auth/preload.ts @@ -1,9 +1,6 @@ import { ipcRenderer } from "electron"; export default { - getHcaptchaAccessibilityCookie: (): Promise<[string]> => - ipcRenderer.invoke("getCookie", { url: "https://www.hcaptcha.com/", name: "hc_accessibility" }), - loginRequest: (alertTitle: string, alertBody: string, buttonText: string): Promise => ipcRenderer.invoke("loginRequest", { alertTitle, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 306a0432233..681a0e3147c 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1725,40 +1725,9 @@ "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "“File password” and “Confirm file password“ do not match." }, - "hCaptchaUrl": { - "message": "hCaptcha Url", - "description": "hCaptcha is the name of a website, should not be translated" - }, - "loadAccessibilityCookie": { - "message": "Load accessibility cookie" - }, - "registerAccessibilityUser": { - "message": "Register as an accessibility user at", - "description": "ex. Register as an accessibility user at hcaptcha.com" - }, - "copyPasteLink": { - "message": "Copy and paste the link sent to your email below" - }, - "enterhCaptchaUrl": { - "message": "Enter URL to load accessibility cookie for hCaptcha", - "description": "hCaptcha is the name of a website, should not be translated" - }, - "hCaptchaUrlRequired": { - "message": "hCaptcha Url is required", - "description": "hCaptcha is the name of a website, should not be translated" - }, - "invalidUrl": { - "message": "Invalid Url" - }, "done": { "message": "Done" }, - "accessibilityCookieSaved": { - "message": "Accessibility cookie saved!" - }, - "noAccessibilityCookieSaved": { - "message": "No accessibility cookie saved" - }, "warning": { "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index 885040cc6f9..ce03406dd24 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -252,12 +252,6 @@ p.lead { } } -#hcaptcha_iframe { - width: 100%; - border: none; - transition: height 0.25s linear; -} - form, .form { .form-group { diff --git a/apps/desktop/src/scss/pages.scss b/apps/desktop/src/scss/pages.scss index 155ffaf0ad1..4098ad860dd 100644 --- a/apps/desktop/src/scss/pages.scss +++ b/apps/desktop/src/scss/pages.scss @@ -23,7 +23,6 @@ } } -#accessibility-cookie-page, #register-page, #hint-page, #update-temp-password-page, @@ -43,7 +42,6 @@ } } -#accessibility-cookie-page, #register-page, #hint-page, #lock-page, diff --git a/apps/desktop/src/services/electron-main-messaging.service.ts b/apps/desktop/src/services/electron-main-messaging.service.ts index 150890bf56f..f8b363d390e 100644 --- a/apps/desktop/src/services/electron-main-messaging.service.ts +++ b/apps/desktop/src/services/electron-main-messaging.service.ts @@ -52,10 +52,6 @@ export class ElectronMainMessagingService implements MessageSender { return windowMain.win?.isVisible(); }); - ipcMain.handle("getCookie", async (event, options) => { - return await this.windowMain.session.cookies.get(options); - }); - ipcMain.handle("loginRequest", async (event, options) => { const alert = new Notification({ title: options.alertTitle, diff --git a/apps/web/src/app/auth/core/services/registration/web-registration-finish.service.spec.ts b/apps/web/src/app/auth/core/services/registration/web-registration-finish.service.spec.ts index aa02e28b3b3..edce551342e 100644 --- a/apps/web/src/app/auth/core/services/registration/web-registration-finish.service.spec.ts +++ b/apps/web/src/app/auth/core/services/registration/web-registration-finish.service.spec.ts @@ -172,7 +172,6 @@ describe("WebRegistrationFinishService", () => { let userKey: UserKey; let userKeyEncString: EncString; let userKeyPair: [string, EncString]; - let capchaBypassToken: string; let orgInvite: OrganizationInvite; let orgSponsoredFreeFamilyPlanToken: string; @@ -198,7 +197,6 @@ describe("WebRegistrationFinishService", () => { userKeyEncString = new EncString("userKeyEncrypted"); userKeyPair = ["publicKey", new EncString("privateKey")]; - capchaBypassToken = "capchaBypassToken"; orgInvite = new OrganizationInvite(); orgInvite.organizationUserId = "organizationUserId"; @@ -219,19 +217,13 @@ describe("WebRegistrationFinishService", () => { ); }); - it("registers the user and returns a captcha bypass token when given valid email verification input", async () => { + it("registers the user when given valid email verification input", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null); - const result = await service.finishRegistration( - email, - passwordInputResult, - emailVerificationToken, - ); - - expect(result).toEqual(capchaBypassToken); + await service.finishRegistration(email, passwordInputResult, emailVerificationToken); expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); @@ -261,15 +253,13 @@ describe("WebRegistrationFinishService", () => { ); }); - it("it registers the user and returns a captcha bypass token when given an org invite", async () => { + it("it registers the user when given an org invite", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(orgInvite); - const result = await service.finishRegistration(email, passwordInputResult); - - expect(result).toEqual(capchaBypassToken); + await service.finishRegistration(email, passwordInputResult); expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); @@ -299,21 +289,19 @@ describe("WebRegistrationFinishService", () => { ); }); - it("registers the user and returns a captcha bypass token when given an org sponsored free family plan token", async () => { + it("registers the user when given an org sponsored free family plan token", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null); - const result = await service.finishRegistration( + await service.finishRegistration( email, passwordInputResult, undefined, orgSponsoredFreeFamilyPlanToken, ); - expect(result).toEqual(capchaBypassToken); - expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); expect(accountApiService.registerFinish).toHaveBeenCalledWith( @@ -342,13 +330,13 @@ describe("WebRegistrationFinishService", () => { ); }); - it("registers the user and returns a captcha bypass token when given an emergency access invite token", async () => { + it("registers the user when given an emergency access invite token", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null); - const result = await service.finishRegistration( + await service.finishRegistration( email, passwordInputResult, undefined, @@ -357,8 +345,6 @@ describe("WebRegistrationFinishService", () => { emergencyAccessId, ); - expect(result).toEqual(capchaBypassToken); - expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); expect(accountApiService.registerFinish).toHaveBeenCalledWith( @@ -387,13 +373,13 @@ describe("WebRegistrationFinishService", () => { ); }); - it("registers the user and returns a captcha bypass token when given a provider invite token", async () => { + it("registers the user when given a provider invite token", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null); - const result = await service.finishRegistration( + await service.finishRegistration( email, passwordInputResult, undefined, @@ -404,8 +390,6 @@ describe("WebRegistrationFinishService", () => { providerUserId, ); - expect(result).toEqual(capchaBypassToken); - expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); expect(accountApiService.registerFinish).toHaveBeenCalledWith( diff --git a/apps/web/src/app/auth/recover-two-factor.component.ts b/apps/web/src/app/auth/recover-two-factor.component.ts index 08881b64f37..106cd32a945 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.ts @@ -80,12 +80,7 @@ export class RecoverTwoFactorComponent implements OnInit { remember: false, }; - const credentials = new PasswordLoginCredentials( - email, - this.masterPassword, - "", - twoFactorRequest, - ); + const credentials = new PasswordLoginCredentials(email, this.masterPassword, twoFactorRequest); try { const authResult = await this.loginStrategyService.logIn(credentials); diff --git a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.ts b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.ts index 418fd67affb..90df6e513c4 100644 --- a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.ts +++ b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.ts @@ -366,14 +366,9 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy { return; } - const captchaToken = await this.finishRegistration(passwordInputResult); + await this.finishRegistration(passwordInputResult); - if (captchaToken == null) { - this.submitting = false; - return; - } - - await this.logIn(passwordInputResult.newPassword, captchaToken); + await this.logIn(passwordInputResult.newPassword); this.submitting = false; @@ -389,14 +384,9 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy { } } - /** Logs the user in based using the token received by the `finishRegistration` method */ - private async logIn(masterPassword: string, captchaBypassToken: string): Promise { - const credentials = new PasswordLoginCredentials( - this.email, - masterPassword, - captchaBypassToken, - null, - ); + /** Logs the user in */ + private async logIn(masterPassword: string): Promise { + const credentials = new PasswordLoginCredentials(this.email, masterPassword); await this.loginStrategyService.logIn(credentials); } diff --git a/apps/web/src/connectors/captcha-mobile.html b/apps/web/src/connectors/captcha-mobile.html deleted file mode 100644 index 51c4335777c..00000000000 --- a/apps/web/src/connectors/captcha-mobile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - Bitwarden Captcha Connector - - - -
-
- -

Captcha Required

-
-
-
- - diff --git a/apps/web/src/connectors/captcha-mobile.scss b/apps/web/src/connectors/captcha-mobile.scss deleted file mode 100644 index a4c7f9b25b7..00000000000 --- a/apps/web/src/connectors/captcha-mobile.scss +++ /dev/null @@ -1 +0,0 @@ -@import "../scss/styles.scss"; diff --git a/apps/web/src/connectors/captcha.html b/apps/web/src/connectors/captcha.html deleted file mode 100644 index c9df2a62aed..00000000000 --- a/apps/web/src/connectors/captcha.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Bitwarden Captcha Connector - - - -
- - diff --git a/apps/web/src/connectors/captcha.scss b/apps/web/src/connectors/captcha.scss deleted file mode 100644 index d044c06ff22..00000000000 --- a/apps/web/src/connectors/captcha.scss +++ /dev/null @@ -1,8 +0,0 @@ -body { - min-width: 0px !important; - padding: 0; - margin: 0; - background: transparent; - display: flex; - justify-content: center; -} diff --git a/apps/web/src/connectors/captcha.ts b/apps/web/src/connectors/captcha.ts deleted file mode 100644 index aad6eaa3d47..00000000000 --- a/apps/web/src/connectors/captcha.ts +++ /dev/null @@ -1,158 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { b64Decode, getQsParam } from "./common"; - -declare let hcaptcha: any; - -if (window.location.pathname.includes("mobile")) { - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-require-imports - require("./captcha-mobile.scss"); -} else { - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-require-imports - require("./captcha.scss"); -} - -document.addEventListener("DOMContentLoaded", () => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - init(); -}); - -(window as any).captchaSuccess = captchaSuccess; -(window as any).captchaError = captchaError; - -let parentUrl: string = null; -let parentOrigin: string = null; -let mobileResponse: boolean = null; -let sentSuccess = false; - -async function init() { - await start(); - onMessage(); -} - -async function start() { - sentSuccess = false; - - const data = getQsParam("data"); - if (!data) { - error("No data."); - return; - } - - parentUrl = getQsParam("parent"); - if (!parentUrl) { - error("No parent."); - return; - } else { - parentUrl = decodeURIComponent(parentUrl); - parentOrigin = new URL(parentUrl).origin; - } - - let decodedData: any; - try { - decodedData = JSON.parse(b64Decode(data, true)); - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - error("Cannot parse data."); - return; - } - mobileResponse = decodedData.callbackUri != null || decodedData.mobile === true; - - let src = "https://hcaptcha.com/1/api.js?render=explicit"; - - // Set language code - if (decodedData.locale) { - src += `&hl=${encodeURIComponent(decodedData.locale) ?? "en"}`; - } - - // Set captchaRequired subtitle for mobile - const subtitleEl = document.getElementById("captchaRequired"); - if (decodedData.captchaRequiredText && subtitleEl) { - subtitleEl.textContent = decodedData.captchaRequiredText; - } - - const script = document.createElement("script"); - script.src = src; - script.async = true; - script.defer = true; - script.addEventListener("load", () => { - hcaptcha.render("captcha", { - sitekey: encodeURIComponent(decodedData.siteKey), - callback: "captchaSuccess", - "error-callback": "captchaError", - }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - watchHeight(); - }); - document.head.appendChild(script); -} - -function captchaSuccess(response: string) { - if (mobileResponse) { - document.location.replace("bitwarden://captcha-callback?token=" + encodeURIComponent(response)); - } else { - success(response); - } -} - -function captchaError() { - error("An error occurred with the captcha. Try again."); -} - -function onMessage() { - window.addEventListener( - "message", - (event) => { - if (!event.origin || event.origin === "" || event.origin !== parentOrigin) { - return; - } - - if (event.data === "start") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - start(); - } - }, - false, - ); -} - -function error(message: string) { - parent.postMessage("error|" + message, parentUrl); -} - -function success(data: string) { - if (sentSuccess) { - return; - } - parent.postMessage("success|" + data, parentUrl); - sentSuccess = true; -} - -function info(message: string | object) { - parent.postMessage("info|" + JSON.stringify(message), parentUrl); -} - -async function watchHeight() { - const imagesDiv = document.body.lastChild as HTMLElement; - // eslint-disable-next-line - while (true) { - info({ - height: - imagesDiv.style.visibility === "hidden" - ? document.documentElement.offsetHeight - : document.documentElement.scrollHeight, - width: document.documentElement.scrollWidth, - }); - await sleep(100); - } -} - -async function sleep(ms: number) { - await new Promise((r) => setTimeout(r, ms)); -} diff --git a/apps/web/src/scss/plugins.scss b/apps/web/src/scss/plugins.scss index d150a4367d5..721c2420187 100644 --- a/apps/web/src/scss/plugins.scss +++ b/apps/web/src/scss/plugins.scss @@ -24,12 +24,6 @@ } } -#hcaptcha_iframe { - border: none; - transition: height 0.25s linear; - width: 100%; -} - @each $mfaType in $mfaTypes { .mfaType#{$mfaType} { content: url("../images/two-factor/" + $mfaType + ".png"); diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index a4ac3322200..d8b9fd3dbee 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -129,16 +129,6 @@ const plugins = [ filename: "redirect-connector.html", chunks: ["connectors/redirect", "styles"], }), - new HtmlWebpackPlugin({ - template: "./src/connectors/captcha.html", - filename: "captcha-connector.html", - chunks: ["connectors/captcha"], - }), - new HtmlWebpackPlugin({ - template: "./src/connectors/captcha-mobile.html", - filename: "captcha-mobile-connector.html", - chunks: ["connectors/captcha"], - }), new HtmlWebpackPlugin({ template: "./src/connectors/duo-redirect.html", filename: "duo-redirect-connector.html", @@ -344,7 +334,6 @@ const webpackConfig = { "connectors/webauthn": "./src/connectors/webauthn.ts", "connectors/webauthn-fallback": "./src/connectors/webauthn-fallback.ts", "connectors/sso": "./src/connectors/sso.ts", - "connectors/captcha": "./src/connectors/captcha.ts", "connectors/duo-redirect": "./src/connectors/duo-redirect.ts", "connectors/redirect": "./src/connectors/redirect.ts", styles: ["./src/scss/styles.scss", "./src/scss/tailwind.css"], diff --git a/libs/angular/src/auth/components/captcha-protected.component.ts b/libs/angular/src/auth/components/captcha-protected.component.ts deleted file mode 100644 index 76e62a38b4d..00000000000 --- a/libs/angular/src/auth/components/captcha-protected.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, Input } from "@angular/core"; -import { firstValueFrom } from "rxjs"; - -import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { ToastService } from "@bitwarden/components"; - -@Directive() -export abstract class CaptchaProtectedComponent { - @Input() captchaSiteKey: string = null; - captchaToken: string = null; - captcha: CaptchaIFrame; - - constructor( - protected environmentService: EnvironmentService, - protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, - protected toastService: ToastService, - ) {} - - async setupCaptcha() { - const env = await firstValueFrom(this.environmentService.environment$); - const webVaultUrl = env.getWebVaultUrl(); - - this.captcha = new CaptchaIFrame( - window, - webVaultUrl, - this.i18nService, - (token: string) => { - this.captchaToken = token; - }, - (error: string) => { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: error, - }); - }, - (info: string) => { - this.toastService.showToast({ - variant: "info", - title: this.i18nService.t("info"), - message: info, - }); - }, - ); - } - - showCaptcha() { - return !Utils.isNullOrWhitespace(this.captchaSiteKey); - } - - protected handleCaptchaRequired(response: { captchaSiteKey: string }): boolean { - if (Utils.isNullOrWhitespace(response.captchaSiteKey)) { - return false; - } - - this.captchaSiteKey = response.captchaSiteKey; - this.captcha.init(response.captchaSiteKey); - return true; - } -} diff --git a/libs/angular/src/directives/api-action.directive.ts b/libs/angular/src/directives/api-action.directive.ts index e71af9cdffe..a07ab7d0413 100644 --- a/libs/angular/src/directives/api-action.directive.ts +++ b/libs/angular/src/directives/api-action.directive.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { Directive, ElementRef, Input, OnChanges } from "@angular/core"; -import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; @@ -39,11 +38,6 @@ export class ApiActionDirective implements OnChanges { }, (e: any) => { this.el.nativeElement.loading = false; - - if ((e as ErrorResponse).captchaRequired) { - this.logService.error("Captcha required error response: " + e.getSingleMessage()); - return; - } this.logService?.error(`Received API exception:`, e); this.validationService.showError(e); }, diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts index c288f30d812..8977338424b 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts @@ -52,7 +52,6 @@ describe("DefaultRegistrationFinishService", () => { let userKey: UserKey; let userKeyEncString: EncString; let userKeyPair: [string, EncString]; - let capchaBypassToken: string; beforeEach(() => { email = "test@email.com"; @@ -71,7 +70,6 @@ describe("DefaultRegistrationFinishService", () => { userKeyEncString = new EncString("userKeyEncrypted"); userKeyPair = ["publicKey", new EncString("privateKey")]; - capchaBypassToken = "capchaBypassToken"; }); it("throws an error if the user key cannot be created", async () => { @@ -82,18 +80,12 @@ describe("DefaultRegistrationFinishService", () => { ); }); - it("registers the user and returns a captcha bypass token when given valid email verification input", async () => { + it("registers the user when given valid email verification input", async () => { keyService.makeUserKey.mockResolvedValue([userKey, userKeyEncString]); keyService.makeKeyPair.mockResolvedValue(userKeyPair); - accountApiService.registerFinish.mockResolvedValue(capchaBypassToken); + accountApiService.registerFinish.mockResolvedValue(); - const result = await service.finishRegistration( - email, - passwordInputResult, - emailVerificationToken, - ); - - expect(result).toEqual(capchaBypassToken); + await service.finishRegistration(email, passwordInputResult, emailVerificationToken); expect(keyService.makeUserKey).toHaveBeenCalledWith(masterKey); expect(keyService.makeKeyPair).toHaveBeenCalledWith(userKey); diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts index 7d844ce8cb0..d1820eabe25 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts @@ -34,7 +34,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi emergencyAccessId?: string, providerInviteToken?: string, providerUserId?: string, - ): Promise { + ): Promise { const [newUserKey, newEncUserKey] = await this.keyService.makeUserKey( passwordInputResult.masterKey, ); @@ -57,9 +57,7 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi providerUserId, ); - const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest); - - return capchaBypassToken; + return await this.accountApiService.registerFinish(registerRequest); } protected async buildRegisterRequest( diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts index 506b7475db1..db8fd045f30 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts @@ -152,9 +152,8 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) { this.submitting = true; - let captchaBypassToken: string = null; try { - captchaBypassToken = await this.registrationFinishService.finishRegistration( + await this.registrationFinishService.finishRegistration( this.email, passwordInputResult, this.emailVerificationToken, @@ -179,12 +178,7 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { // login with the new account try { - const credentials = new PasswordLoginCredentials( - this.email, - passwordInputResult.newPassword, - captchaBypassToken, - null, - ); + const credentials = new PasswordLoginCredentials(this.email, passwordInputResult.newPassword); const authenticationResult = await this.loginStrategyService.logIn(credentials); diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts index 3746e37b84a..5f3c04e5155 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts @@ -27,7 +27,7 @@ export abstract class RegistrationFinishService { * @param emergencyAccessId The optional emergency access id which is required to validate the emergency access invite token. * @param providerInviteToken The optional provider invite token. * @param providerUserId The optional provider user id which is required to validate the provider invite token. - * @returns a promise which resolves to the captcha bypass token string upon a successful account creation. + * @returns a promise which resolves upon a successful account creation. */ abstract finishRegistration( email: string, @@ -38,5 +38,5 @@ export abstract class RegistrationFinishService { emergencyAccessId?: string, providerInviteToken?: string, providerUserId?: string, - ): Promise; + ): Promise; } diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts index d48d725e251..de3fa7a3321 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.spec.ts @@ -260,7 +260,6 @@ describe("TwoFactorAuthComponent", () => { // Assert expect(mockLoginStrategyService.logInTwoFactor).toHaveBeenCalledWith( new TokenTwoFactorRequest(component.selectedProviderType, token, remember), - "", ); }); diff --git a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts index e7e62260b49..6db56c8e484 100644 --- a/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts +++ b/libs/auth/src/angular/two-factor-auth/two-factor-auth.component.ts @@ -335,7 +335,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy { try { this.formPromise = this.loginStrategyService.logInTwoFactor( new TokenTwoFactorRequest(this.selectedProviderType, tokenValue, rememberValue), - "", // TODO: PM-15162 - deprecate captchaResponse ); const authResult: AuthResult = await this.formPromise; this.logService.info("Successfully submitted two factor token"); diff --git a/libs/auth/src/common/abstractions/login-strategy.service.ts b/libs/auth/src/common/abstractions/login-strategy.service.ts index e9fa780b0fe..b0fffae2ab4 100644 --- a/libs/auth/src/common/abstractions/login-strategy.service.ts +++ b/libs/auth/src/common/abstractions/login-strategy.service.ts @@ -59,16 +59,11 @@ export abstract class LoginStrategyServiceAbstraction { | WebAuthnLoginCredentials, ) => Promise; /** - * Sends a token request to the server with the provided two factor token - * and captcha response. This uses data stored from {@link LoginStrategyServiceAbstraction.logIn}, - * so that must be called first. + * Sends a token request to the server with the provided two factor token. + * This uses data stored from {@link LoginStrategyServiceAbstraction.logIn}, so that must be called first. * Returns an error if no session data is found. */ - logInTwoFactor: ( - twoFactor: TokenTwoFactorRequest, - // TODO: PM-15162 - deprecate captchaResponse - captchaResponse: string, - ) => Promise; + logInTwoFactor: (twoFactor: TokenTwoFactorRequest) => Promise; /** * Creates a master key from the provided master password and email. */ diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts index 3d91adf35cf..8581ca74465 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts @@ -17,7 +17,6 @@ import { LoginStrategy, LoginStrategyData } from "./login.strategy"; export class AuthRequestLoginStrategyData implements LoginStrategyData { tokenRequest: PasswordTokenRequest; - captchaBypassToken: string; authRequestCredentials: AuthRequestLoginCredentials; static fromJSON(obj: Jsonify): AuthRequestLoginStrategyData { @@ -54,7 +53,6 @@ export class AuthRequestLoginStrategy extends LoginStrategy { data.tokenRequest = new PasswordTokenRequest( credentials.email, credentials.accessCode, - null, await this.buildTwoFactor(credentials.twoFactor, credentials.email), await this.buildDeviceRequest(), ); @@ -66,12 +64,8 @@ export class AuthRequestLoginStrategy extends LoginStrategy { return authResult; } - override async logInTwoFactor( - twoFactor: TokenTwoFactorRequest, - captchaResponse: string, - ): Promise { + override async logInTwoFactor(twoFactor: TokenTwoFactorRequest): Promise { const data = this.cache.value; - data.tokenRequest.captchaResponse = captchaResponse ?? data.captchaBypassToken; this.cache.next(data); return super.logInTwoFactor(twoFactor); diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index 5a5a9dc2575..a0ccba649b6 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -11,7 +11,6 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { IdentityCaptchaResponse } from "@bitwarden/common/auth/models/response/identity-captcha.response"; import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; @@ -59,7 +58,6 @@ const accessToken = "ACCESS_TOKEN"; const refreshToken = "REFRESH_TOKEN"; const userKey = "USER_KEY"; const privateKey = "PRIVATE_KEY"; -const captchaSiteKey = "CAPTCHA_SITE_KEY"; const kdf = 0; const kdfIterations = 10000; const userId = Utils.newGuid() as UserId; @@ -298,7 +296,6 @@ describe("LoginStrategy", () => { expected.userId = userId; expected.resetMasterPassword = true; expected.twoFactorProviders = null; - expected.captchaSiteKey = ""; expect(result).toEqual(expected); }); @@ -314,7 +311,6 @@ describe("LoginStrategy", () => { expected.userId = userId; expected.resetMasterPassword = false; expected.twoFactorProviders = null; - expected.captchaSiteKey = ""; expect(result).toEqual(expected); expect(masterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith( @@ -323,28 +319,6 @@ describe("LoginStrategy", () => { ); }); - it("rejects login if CAPTCHA is required", async () => { - // Sample CAPTCHA response - const tokenResponse = new IdentityCaptchaResponse({ - error: "invalid_grant", - error_description: "Captcha required.", - HCaptcha_SiteKey: captchaSiteKey, - }); - - apiService.postIdentityToken.mockResolvedValue(tokenResponse); - masterPasswordService.masterKeySubject.next(masterKey); - masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(userKey); - - const result = await passwordLoginStrategy.logIn(credentials); - - expect(stateService.addAccount).not.toHaveBeenCalled(); - expect(messagingService.send).not.toHaveBeenCalled(); - - const expected = new AuthResult(); - expected.captchaSiteKey = captchaSiteKey; - expect(result).toEqual(expected); - }); - it("makes a new public and private key for an old account", async () => { const tokenResponse = identityTokenResponseFactory(); tokenResponse.privateKey = null; @@ -492,7 +466,6 @@ describe("LoginStrategy", () => { cache.tokenRequest = new PasswordTokenRequest( email, masterPasswordHash, - "", new TokenTwoFactorRequest(), ); @@ -524,7 +497,6 @@ describe("LoginStrategy", () => { await passwordLoginStrategy.logInTwoFactor( new TokenTwoFactorRequest(twoFactorProviderType, twoFactorToken, twoFactorRemember), - "", ); expect(apiService.postIdentityToken).toHaveBeenCalledWith( @@ -541,13 +513,11 @@ describe("LoginStrategy", () => { describe("Device verification", () => { it("processes device verification response", async () => { - const captchaToken = "test-captcha-token"; const deviceVerificationResponse = new IdentityDeviceVerificationResponse({ error: "invalid_grant", error_description: "Device verification required.", email: "test@bitwarden.com", deviceVerificationRequest: true, - captchaToken: captchaToken, }); apiService.postIdentityToken.mockResolvedValue(deviceVerificationResponse); @@ -556,7 +526,6 @@ describe("LoginStrategy", () => { cache.tokenRequest = new PasswordTokenRequest( email, masterPasswordHash, - "", new TokenTwoFactorRequest(), ); diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts index e252d50a5ab..6e66d65b654 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.ts @@ -13,7 +13,6 @@ import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity- import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; import { UserApiTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/user-api-token.request"; import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request"; -import { IdentityCaptchaResponse } from "@bitwarden/common/auth/models/response/identity-captcha.response"; import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; @@ -56,7 +55,6 @@ import { CacheData } from "../services/login-strategies/login-strategy.state"; type IdentityResponse = | IdentityTokenResponse | IdentityTwoFactorResponse - | IdentityCaptchaResponse | IdentityDeviceVerificationResponse; export abstract class LoginStrategyData { @@ -66,7 +64,6 @@ export abstract class LoginStrategyData { | SsoTokenRequest | WebAuthnLoginTokenRequest | undefined; - captchaBypassToken?: string; /** User's entered email obtained pre-login. */ abstract userEnteredEmail?: string; @@ -108,10 +105,7 @@ export abstract class LoginStrategy { | WebAuthnLoginCredentials, ): Promise; - async logInTwoFactor( - twoFactor: TokenTwoFactorRequest, - captchaResponse: string | null = null, - ): Promise { + async logInTwoFactor(twoFactor: TokenTwoFactorRequest): Promise { const data = this.cache.value; if (!data.tokenRequest) { throw new Error("Token request is undefined"); @@ -133,8 +127,6 @@ export abstract class LoginStrategy { if (response instanceof IdentityTwoFactorResponse) { return [await this.processTwoFactorResponse(response), response]; - } else if (response instanceof IdentityCaptchaResponse) { - return [await this.processCaptchaResponse(response), response]; } else if (response instanceof IdentityTokenResponse) { return [await this.processTokenResponse(response), response]; } else if (response instanceof IdentityDeviceVerificationResponse) { @@ -362,7 +354,6 @@ export abstract class LoginStrategy { result.twoFactorProviders = response.twoFactorProviders2; await this.twoFactorService.setProviders(response); - this.cache.next({ ...this.cache.value, captchaBypassToken: response.captchaToken ?? null }); result.ssoEmail2FaSessionToken = response.ssoEmail2faSessionToken; result.email = response.email ?? ""; @@ -379,12 +370,6 @@ export abstract class LoginStrategy { } } - private async processCaptchaResponse(response: IdentityCaptchaResponse): Promise { - const result = new AuthResult(); - result.captchaSiteKey = response.siteKey; - return result; - } - /** * Verifies that the active account is set after initialization. * Note: In browser there is a slight delay between when active account emits in background, @@ -407,7 +392,7 @@ export abstract class LoginStrategy { /** * Handles the response from the server when a device verification is required. - * It sets the requiresDeviceVerification flag to true and caches the captcha token if it came back. + * It sets the requiresDeviceVerification flag to true. * * @param {IdentityDeviceVerificationResponse} response - The response from the server indicating that device verification is required. * @returns {Promise} - A promise that resolves to an AuthResult object @@ -417,9 +402,6 @@ export abstract class LoginStrategy { ): Promise { const result = new AuthResult(); result.requiresDeviceVerification = true; - - // Extend cached data with captcha bypass token if it came back. - this.cache.next({ ...this.cache.value, captchaBypassToken: response.captchaToken ?? null }); return result; } } diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts index 2923908fb7b..38829974c4e 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts @@ -184,7 +184,6 @@ describe("PasswordLoginStrategy", () => { provider: null, token: null, }), - captchaResponse: undefined, }), ); }); @@ -260,14 +259,11 @@ describe("PasswordLoginStrategy", () => { apiService.postIdentityToken.mockResolvedValueOnce( identityTokenResponseFactory(masterPasswordPolicy), ); - await passwordLoginStrategy.logInTwoFactor( - { - provider: TwoFactorProviderType.Authenticator, - token: "123456", - remember: false, - }, - "", - ); + await passwordLoginStrategy.logInTwoFactor({ + provider: TwoFactorProviderType.Authenticator, + token: "123456", + remember: false, + }); // Second login attempt should save the force password reset options expect(masterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith( diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.ts b/libs/auth/src/common/login-strategies/password-login.strategy.ts index 6af9d8dbb6b..7671269a85f 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.ts @@ -9,7 +9,6 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { IdentityCaptchaResponse } from "@bitwarden/common/auth/models/response/identity-captcha.response"; import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response"; @@ -30,8 +29,6 @@ export class PasswordLoginStrategyData implements LoginStrategyData { /** User's entered email obtained pre-login. Always present in MP login. */ userEnteredEmail: string; - /** If 2fa is required, token is returned to bypass captcha */ - captchaBypassToken?: string; /** The local version of the user's master key hash */ localMasterKeyHash: string; /** The user's master key */ @@ -79,7 +76,7 @@ export class PasswordLoginStrategy extends LoginStrategy { } override async logIn(credentials: PasswordLoginCredentials) { - const { email, masterPassword, captchaToken, twoFactor } = credentials; + const { email, masterPassword, twoFactor } = credentials; const data = new PasswordLoginStrategyData(); data.masterKey = await this.loginStrategyService.makePreloginKey(masterPassword, email); @@ -96,7 +93,6 @@ export class PasswordLoginStrategy extends LoginStrategy { data.tokenRequest = new PasswordTokenRequest( email, serverMasterKeyHash, - captchaToken, await this.buildTwoFactor(twoFactor, email), await this.buildDeviceRequest(), ); @@ -105,23 +101,12 @@ export class PasswordLoginStrategy extends LoginStrategy { const [authResult, identityResponse] = await this.startLogIn(); - if (identityResponse instanceof IdentityCaptchaResponse) { - return authResult; - } - await this.evaluateMasterPasswordIfRequired(identityResponse, credentials, authResult); return authResult; } - override async logInTwoFactor( - twoFactor: TokenTwoFactorRequest, - captchaResponse: string, - ): Promise { - const data = this.cache.value; - data.tokenRequest.captchaResponse = captchaResponse ?? data.captchaBypassToken; - this.cache.next(data); - + override async logInTwoFactor(twoFactor: TokenTwoFactorRequest): Promise { const result = await super.logInTwoFactor(twoFactor); return result; diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.ts index d81284a960e..92add18059c 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.ts @@ -22,7 +22,6 @@ import { CacheData } from "../services/login-strategies/login-strategy.state"; import { LoginStrategyData, LoginStrategy } from "./login.strategy"; export class SsoLoginStrategyData implements LoginStrategyData { - captchaBypassToken: string; tokenRequest: SsoTokenRequest; /** * User's entered email obtained pre-login. Present in most SSO flows, but not CLI + SSO Flow. diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts index 32cd5ceaf40..7e7ecee0385 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts @@ -16,7 +16,6 @@ import { LoginStrategy, LoginStrategyData } from "./login.strategy"; export class UserApiLoginStrategyData implements LoginStrategyData { tokenRequest: UserApiTokenRequest; - captchaBypassToken: string; static fromJSON(obj: Jsonify): UserApiLoginStrategyData { return Object.assign(new UserApiLoginStrategyData(), obj, { diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts index fb4cbd55ad9..e3c2d2da27f 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts @@ -208,11 +208,9 @@ describe("WebAuthnLoginStrategy", () => { expect(authResult).toBeInstanceOf(AuthResult); expect(authResult).toMatchObject({ - captchaSiteKey: "", resetMasterPassword: false, twoFactorProviders: null, requiresTwoFactor: false, - requiresCaptcha: false, }); }); diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts index 182d44195c7..1d817c57009 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts @@ -17,7 +17,6 @@ import { LoginStrategy, LoginStrategyData } from "./login.strategy"; export class WebAuthnLoginStrategyData implements LoginStrategyData { tokenRequest: WebAuthnLoginTokenRequest; - captchaBypassToken?: string; credentials: WebAuthnLoginCredentials; static fromJSON(obj: Jsonify): WebAuthnLoginStrategyData { diff --git a/libs/auth/src/common/models/domain/login-credentials.ts b/libs/auth/src/common/models/domain/login-credentials.ts index cc21e5b2505..bce8ce54de5 100644 --- a/libs/auth/src/common/models/domain/login-credentials.ts +++ b/libs/auth/src/common/models/domain/login-credentials.ts @@ -14,8 +14,6 @@ export class PasswordLoginCredentials { constructor( public email: string, public masterPassword: string, - // TODO: PM-15162 - captcha is deprecated as part of UI refresh work - public captchaToken?: string, public twoFactor?: TokenTwoFactorRequest, ) {} } diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts index 1dc05cafa00..981f5592621 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts @@ -248,7 +248,7 @@ describe("LoginStrategyService", () => { premium: false, }); - const result = await sut.logInTwoFactor(twoFactorToken, "CAPTCHA"); + const result = await sut.logInTwoFactor(twoFactorToken); expect(result).toBeInstanceOf(AuthResult); }); @@ -285,7 +285,7 @@ describe("LoginStrategyService", () => { true, ); - await expect(sut.logInTwoFactor(twoFactorToken, "CAPTCHA")).rejects.toThrow(); + await expect(sut.logInTwoFactor(twoFactorToken)).rejects.toThrow(); }); it("throw error on too low kdf config", async () => { diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index 2adea85a9ec..a9b7ef250bc 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -242,10 +242,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { return result; } - async logInTwoFactor( - twoFactor: TokenTwoFactorRequest, - captchaResponse: string, - ): Promise { + async logInTwoFactor(twoFactor: TokenTwoFactorRequest): Promise { if (!(await this.isSessionValid())) { throw new Error(this.i18nService.t("sessionTimeout")); } @@ -256,10 +253,10 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { } try { - const result = await strategy.logInTwoFactor(twoFactor, captchaResponse); + const result = await strategy.logInTwoFactor(twoFactor); // Only clear cache if 2FA token has been accepted, otherwise we need to be able to try again - if (result != null && !result.requiresTwoFactor && !result.requiresCaptcha) { + if (result != null && !result.requiresTwoFactor) { await this.clearCache(); } return result; diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts index ea9d1f80e3c..32c5fdcc4d5 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts @@ -47,7 +47,6 @@ describe("LOGIN_STRATEGY_CACHE_KEY", () => { actual.password.tokenRequest = new PasswordTokenRequest( "EMAIL", "LOCAL_PASSWORD_HASH", - "CAPTCHA_TOKEN", twoFactorRequest, deviceRequest, ); @@ -116,7 +115,7 @@ describe("LOGIN_STRATEGY_CACHE_KEY", () => { deviceResponse, deviceRequest, ); - actual.webAuthn.captchaBypassToken = "CAPTCHA_BYPASS_TOKEN"; + actual.webAuthn.tokenRequest.setTwoFactor( new TokenTwoFactorRequest(TwoFactorProviderType.Email, "TOKEN", false), ); diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index dcd326b2812..1e13a3064f4 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -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; @@ -167,7 +161,6 @@ export abstract class ApiService { postSecurityStamp: (request: SecretVerificationRequest) => Promise; getAccountRevisionDate: () => Promise; postPasswordHint: (request: PasswordHintRequest) => Promise; - postRegister: (request: RegisterRequest) => Promise; postPremium: (data: FormData) => Promise; postReinstatePremium: () => Promise; postAccountStorage: (request: StorageRequest) => Promise; diff --git a/libs/common/src/auth/abstractions/account-api.service.ts b/libs/common/src/auth/abstractions/account-api.service.ts index 61fdd4f9d68..f1773e33e6a 100644 --- a/libs/common/src/auth/abstractions/account-api.service.ts +++ b/libs/common/src/auth/abstractions/account-api.service.ts @@ -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; + abstract registerFinish(request: RegisterFinishRequest): Promise; /** * Sets the [dbo].[User].[VerifyDevices] flag to true or false. diff --git a/libs/common/src/auth/captcha-iframe.ts b/libs/common/src/auth/captcha-iframe.ts deleted file mode 100644 index 94859003e16..00000000000 --- a/libs/common/src/auth/captcha-iframe.ts +++ /dev/null @@ -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), - ); - } -} diff --git a/libs/common/src/auth/iframe-component.ts b/libs/common/src/auth/iframe-component.ts deleted file mode 100644 index 814dc9642da..00000000000 --- a/libs/common/src/auth/iframe-component.ts +++ /dev/null @@ -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 - ); - } -} diff --git a/libs/common/src/auth/models/domain/auth-result.ts b/libs/common/src/auth/models/domain/auth-result.ts index 5177363ac89..a61a35eeb1d 100644 --- a/libs/common/src/auth/models/domain/auth-result.ts +++ b/libs/common/src/auth/models/domain/auth-result.ts @@ -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; } diff --git a/libs/common/src/auth/models/request/captcha-protected.request.ts b/libs/common/src/auth/models/request/captcha-protected.request.ts deleted file mode 100644 index f15f4efebeb..00000000000 --- a/libs/common/src/auth/models/request/captcha-protected.request.ts +++ /dev/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; -} diff --git a/libs/common/src/auth/models/request/identity-token/password-token.request.ts b/libs/common/src/auth/models/request/identity-token/password-token.request.ts index 3fe466e143b..4f2313473da 100644 --- a/libs/common/src/auth/models/request/identity-token/password-token.request.ts +++ b/libs/common/src/auth/models/request/identity-token/password-token.request.ts @@ -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; } diff --git a/libs/common/src/auth/models/response/captcha-protected.response.ts b/libs/common/src/auth/models/response/captcha-protected.response.ts deleted file mode 100644 index 3f34b71479d..00000000000 --- a/libs/common/src/auth/models/response/captcha-protected.response.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ICaptchaProtectedResponse { - captchaBypassToken: string; -} diff --git a/libs/common/src/auth/models/response/identity-captcha.response.ts b/libs/common/src/auth/models/response/identity-captcha.response.ts deleted file mode 100644 index f5ec727d901..00000000000 --- a/libs/common/src/auth/models/response/identity-captcha.response.ts +++ /dev/null @@ -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"); - } -} diff --git a/libs/common/src/auth/models/response/identity-device-verification.response.ts b/libs/common/src/auth/models/response/identity-device-verification.response.ts index b45f47e99e1..ac2ab62c474 100644 --- a/libs/common/src/auth/models/response/identity-device-verification.response.ts +++ b/libs/common/src/auth/models/response/identity-device-verification.response.ts @@ -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"); } } diff --git a/libs/common/src/auth/models/response/identity-two-factor.response.ts b/libs/common/src/auth/models/response/identity-two-factor.response.ts index dce64e8ef3e..b52fbcb8771 100644 --- a/libs/common/src/auth/models/response/identity-two-factor.response.ts +++ b/libs/common/src/auth/models/response/identity-two-factor.response.ts @@ -8,14 +8,12 @@ export class IdentityTwoFactorResponse extends BaseResponse { twoFactorProviders: TwoFactorProviderType[]; // a map of two-factor providers to necessary data for completion twoFactorProviders2: Record>; - 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( diff --git a/libs/common/src/auth/models/response/register.response.ts b/libs/common/src/auth/models/response/register.response.ts deleted file mode 100644 index 65f6ba62729..00000000000 --- a/libs/common/src/auth/models/response/register.response.ts +++ /dev/null @@ -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"); - } -} diff --git a/libs/common/src/auth/services/account-api.service.ts b/libs/common/src/auth/services/account-api.service.ts index 0347694c465..923153a0a0b 100644 --- a/libs/common/src/auth/services/account-api.service.ts +++ b/libs/common/src/auth/services/account-api.service.ts @@ -84,7 +84,7 @@ export class AccountApiServiceImplementation implements AccountApiService { } } - async registerFinish(request: RegisterFinishRequest): Promise { + async registerFinish(request: RegisterFinishRequest): Promise { const env = await firstValueFrom(this.environmentService.environment$); try { diff --git a/libs/common/src/models/request/register.request.ts b/libs/common/src/models/request/register.request.ts deleted file mode 100644 index 4f11aadd49c..00000000000 --- a/libs/common/src/models/request/register.request.ts +++ /dev/null @@ -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; - } -} diff --git a/libs/common/src/models/response/error.response.ts b/libs/common/src/models/response/error.response.ts index de88db04922..11dc7ec86de 100644 --- a/libs/common/src/models/response/error.response.ts +++ b/libs/common/src/models/response/error.response.ts @@ -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; } diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index bd769407608..fb4d08db81c 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -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 { - 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 { const r = await this.send("POST", "/accounts/premium", data, true, true); return new PaymentResponse(r);