diff --git a/autogpt_platform/backend/.env.default b/autogpt_platform/backend/.env.default index a00af85724..a0004633ca 100644 --- a/autogpt_platform/backend/.env.default +++ b/autogpt_platform/backend/.env.default @@ -134,13 +134,6 @@ POSTMARK_WEBHOOK_TOKEN= # Error Tracking SENTRY_DSN= -# Cloudflare Turnstile (CAPTCHA) Configuration -# Get these from the Cloudflare Turnstile dashboard: https://dash.cloudflare.com/?to=/:account/turnstile -# This is the backend secret key -TURNSTILE_SECRET_KEY= -# This is the verify URL -TURNSTILE_VERIFY_URL=https://challenges.cloudflare.com/turnstile/v0/siteverify - # Feature Flags LAUNCH_DARKLY_SDK_KEY= diff --git a/autogpt_platform/backend/backend/server/rest_api.py b/autogpt_platform/backend/backend/server/rest_api.py index b572d84060..15e7485d5d 100644 --- a/autogpt_platform/backend/backend/server/rest_api.py +++ b/autogpt_platform/backend/backend/server/rest_api.py @@ -35,7 +35,6 @@ import backend.server.v2.library.routes import backend.server.v2.otto.routes import backend.server.v2.store.model import backend.server.v2.store.routes -import backend.server.v2.turnstile.routes import backend.util.service import backend.util.settings from backend.blocks.llm import LlmModel @@ -281,11 +280,6 @@ app.include_router( app.include_router( backend.server.v2.otto.routes.router, tags=["v2", "otto"], prefix="/api/otto" ) -app.include_router( - backend.server.v2.turnstile.routes.router, - tags=["v2", "turnstile"], - prefix="/api/turnstile", -) app.include_router( backend.server.routers.postmark.postmark.router, diff --git a/autogpt_platform/backend/backend/server/v2/turnstile/__init__.py b/autogpt_platform/backend/backend/server/v2/turnstile/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/autogpt_platform/backend/backend/server/v2/turnstile/models.py b/autogpt_platform/backend/backend/server/v2/turnstile/models.py deleted file mode 100644 index 9410b89511..0000000000 --- a/autogpt_platform/backend/backend/server/v2/turnstile/models.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel, Field - - -class TurnstileVerifyRequest(BaseModel): - """Request model for verifying a Turnstile token.""" - - token: str = Field(description="The Turnstile token to verify") - action: Optional[str] = Field( - default=None, description="The action that the user is attempting to perform" - ) - - -class TurnstileVerifyResponse(BaseModel): - """Response model for the Turnstile verification endpoint.""" - - success: bool = Field(description="Whether the token verification was successful") - error: Optional[str] = Field( - default=None, description="Error message if verification failed" - ) - challenge_timestamp: Optional[str] = Field( - default=None, description="Timestamp of the challenge (ISO format)" - ) - hostname: Optional[str] = Field( - default=None, description="Hostname of the site where the challenge was solved" - ) - action: Optional[str] = Field( - default=None, description="The action associated with this verification" - ) diff --git a/autogpt_platform/backend/backend/server/v2/turnstile/routes.py b/autogpt_platform/backend/backend/server/v2/turnstile/routes.py deleted file mode 100644 index 7a4fe5bafa..0000000000 --- a/autogpt_platform/backend/backend/server/v2/turnstile/routes.py +++ /dev/null @@ -1,112 +0,0 @@ -import logging - -import aiohttp -from fastapi import APIRouter - -from backend.util.settings import Settings - -from .models import TurnstileVerifyRequest, TurnstileVerifyResponse - -logger = logging.getLogger(__name__) - -router = APIRouter() -settings = Settings() - - -@router.post( - "/verify", response_model=TurnstileVerifyResponse, summary="Verify Turnstile Token" -) -async def verify_turnstile_token( - request: TurnstileVerifyRequest, -) -> TurnstileVerifyResponse: - """ - Verify a Cloudflare Turnstile token. - This endpoint verifies a token returned by the Cloudflare Turnstile challenge - on the client side. It returns whether the verification was successful. - """ - logger.info(f"Verifying Turnstile token for action: {request.action}") - return await verify_token(request) - - -async def verify_token(request: TurnstileVerifyRequest) -> TurnstileVerifyResponse: - """ - Verify a Cloudflare Turnstile token by making a request to the Cloudflare API. - """ - # Get the secret key from settings - turnstile_secret_key = settings.secrets.turnstile_secret_key - turnstile_verify_url = settings.secrets.turnstile_verify_url - - if not turnstile_secret_key: - logger.error( - "Turnstile secret key missing. Set TURNSTILE_SECRET_KEY to enable verification." - ) - return TurnstileVerifyResponse( - success=False, - error="CONFIGURATION_ERROR", - challenge_timestamp=None, - hostname=None, - action=None, - ) - - try: - async with aiohttp.ClientSession() as session: - payload = { - "secret": turnstile_secret_key, - "response": request.token, - } - - if request.action: - payload["action"] = request.action - - logger.debug(f"Verifying Turnstile token with action: {request.action}") - - async with session.post( - turnstile_verify_url, - data=payload, - timeout=aiohttp.ClientTimeout(total=10), - ) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"Turnstile API error: {error_text}") - return TurnstileVerifyResponse( - success=False, - error=f"API_ERROR: {response.status}", - challenge_timestamp=None, - hostname=None, - action=None, - ) - - data = await response.json() - logger.debug(f"Turnstile API response: {data}") - - # Parse the response and return a structured object - return TurnstileVerifyResponse( - success=data.get("success", False), - error=( - data.get("error-codes", None)[0] - if data.get("error-codes") - else None - ), - challenge_timestamp=data.get("challenge_timestamp"), - hostname=data.get("hostname"), - action=data.get("action"), - ) - - except aiohttp.ClientError as e: - logger.error(f"Connection error to Turnstile API: {str(e)}") - return TurnstileVerifyResponse( - success=False, - error=f"CONNECTION_ERROR: {str(e)}", - challenge_timestamp=None, - hostname=None, - action=None, - ) - except Exception as e: - logger.error(f"Unexpected error in Turnstile verification: {str(e)}") - return TurnstileVerifyResponse( - success=False, - error=f"UNEXPECTED_ERROR: {str(e)}", - challenge_timestamp=None, - hostname=None, - action=None, - ) diff --git a/autogpt_platform/backend/backend/server/v2/turnstile/routes_test.py b/autogpt_platform/backend/backend/server/v2/turnstile/routes_test.py deleted file mode 100644 index 5a9260131f..0000000000 --- a/autogpt_platform/backend/backend/server/v2/turnstile/routes_test.py +++ /dev/null @@ -1,32 +0,0 @@ -import fastapi -import fastapi.testclient -import pytest_mock - -import backend.server.v2.turnstile.routes as turnstile_routes - -app = fastapi.FastAPI() -app.include_router(turnstile_routes.router) - -client = fastapi.testclient.TestClient(app) - - -def test_verify_turnstile_token_no_secret_key(mocker: pytest_mock.MockFixture) -> None: - """Test token verification without secret key configured""" - # Mock the settings with no secret key - mock_settings = mocker.patch("backend.server.v2.turnstile.routes.settings") - mock_settings.secrets.turnstile_secret_key = None - - request_data = {"token": "test_token", "action": "login"} - response = client.post("/verify", json=request_data) - - assert response.status_code == 200 - response_data = response.json() - assert response_data["success"] is False - assert response_data["error"] == "CONFIGURATION_ERROR" - - -def test_verify_turnstile_token_invalid_request() -> None: - """Test token verification with invalid request data""" - # Missing token - response = client.post("/verify", json={"action": "login"}) - assert response.status_code == 422 diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index 42014f9698..fcd009f820 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -537,16 +537,6 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings): description="The secret key to use for the unsubscribe user by token", ) - # Cloudflare Turnstile credentials - turnstile_secret_key: str = Field( - default="", - description="Cloudflare Turnstile backend secret key", - ) - turnstile_verify_url: str = Field( - default="https://challenges.cloudflare.com/turnstile/v0/siteverify", - description="Cloudflare Turnstile verify URL", - ) - # OAuth server credentials for integrations # --8<-- [start:OAuthServerCredentialsExample] github_client_id: str = Field(default="", description="GitHub OAuth client ID") diff --git a/autogpt_platform/frontend/.env.default b/autogpt_platform/frontend/.env.default index 1a13696c35..3d4720431d 100644 --- a/autogpt_platform/frontend/.env.default +++ b/autogpt_platform/frontend/.env.default @@ -11,7 +11,6 @@ NEXT_PUBLIC_LAUNCHDARKLY_ENABLED=false NEXT_PUBLIC_LAUNCHDARKLY_CLIENT_ID=687ab1372f497809b131e06e - NEXT_PUBLIC_TURNSTILE=disabled NEXT_PUBLIC_REACT_QUERY_DEVTOOL=true NEXT_PUBLIC_GA_MEASUREMENT_ID=G-FH2XK2W4GN diff --git a/autogpt_platform/frontend/package.json b/autogpt_platform/frontend/package.json index 8bbdcff9cd..52ba28064f 100644 --- a/autogpt_platform/frontend/package.json +++ b/autogpt_platform/frontend/package.json @@ -30,7 +30,6 @@ "dependencies": { "@faker-js/faker": "10.0.0", "@hookform/resolvers": "5.2.2", - "@marsidev/react-turnstile": "1.3.1", "@next/third-parties": "15.4.6", "@phosphor-icons/react": "2.1.10", "@radix-ui/react-alert-dialog": "1.1.15", diff --git a/autogpt_platform/frontend/pnpm-lock.yaml b/autogpt_platform/frontend/pnpm-lock.yaml index aa80e5b21f..848fd3a88d 100644 --- a/autogpt_platform/frontend/pnpm-lock.yaml +++ b/autogpt_platform/frontend/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: '@hookform/resolvers': specifier: 5.2.2 version: 5.2.2(react-hook-form@7.66.0(react@18.3.1)) - '@marsidev/react-turnstile': - specifier: 1.3.1 - version: 1.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@next/third-parties': specifier: 15.4.6 version: 15.4.6(next@15.4.7(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -1583,12 +1580,6 @@ packages: peerDependencies: jsep: ^0.4.0||^1.0.0 - '@marsidev/react-turnstile@1.3.1': - resolution: {integrity: sha512-h2THG/75k4Y049hgjSGPIcajxXnh+IZAiXVbryQyVmagkboN7pJtBgR16g8akjwUBSfRrg6jw6KvPDjscQflog==} - peerDependencies: - react: ^17.0.2 || ^18.0.0 || ^19.0 - react-dom: ^17.0.2 || ^18.0.0 || ^19.0 - '@mdx-js/react@3.1.1': resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} peerDependencies: @@ -8919,11 +8910,6 @@ snapshots: dependencies: jsep: 1.4.0 - '@marsidev/react-turnstile@1.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - '@mdx-js/react@3.1.1(@types/react@18.3.17)(react@18.3.1)': dependencies: '@types/mdx': 2.0.13 @@ -12447,7 +12433,7 @@ snapshots: '@typescript-eslint/parser': 8.43.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) @@ -12467,7 +12453,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -12482,14 +12468,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.43.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -12504,7 +12490,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 diff --git a/autogpt_platform/frontend/src/app/(platform)/login/actions.ts b/autogpt_platform/frontend/src/app/(platform)/login/actions.ts index 00092470ac..936c879d69 100644 --- a/autogpt_platform/frontend/src/app/(platform)/login/actions.ts +++ b/autogpt_platform/frontend/src/app/(platform)/login/actions.ts @@ -2,17 +2,11 @@ import BackendAPI from "@/lib/autogpt-server-api"; import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; -import { verifyTurnstileToken } from "@/lib/turnstile"; -import { environment } from "@/services/environment"; import { loginFormSchema } from "@/types/auth"; import * as Sentry from "@sentry/nextjs"; import { shouldShowOnboarding } from "../../api/helpers"; -export async function login( - email: string, - password: string, - turnstileToken?: string, -) { +export async function login(email: string, password: string) { try { const parsed = loginFormSchema.safeParse({ email, password }); @@ -23,14 +17,6 @@ export async function login( }; } - const captchaOk = await verifyTurnstileToken(turnstileToken ?? "", "login"); - if (!captchaOk && !environment.isVercelPreview()) { - return { - success: false, - error: "CAPTCHA verification failed. Please try again.", - }; - } - const supabase = await getServerSupabase(); if (!supabase) { return { diff --git a/autogpt_platform/frontend/src/app/(platform)/login/page.tsx b/autogpt_platform/frontend/src/app/(platform)/login/page.tsx index b4f6d39a45..3f06e7f429 100644 --- a/autogpt_platform/frontend/src/app/(platform)/login/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/login/page.tsx @@ -7,7 +7,6 @@ import { AuthCard } from "@/components/auth/AuthCard"; import AuthFeedback from "@/components/auth/AuthFeedback"; import { EmailNotAllowedModal } from "@/components/auth/EmailNotAllowedModal"; import { GoogleOAuthButton } from "@/components/auth/GoogleOAuthButton"; -import Turnstile from "@/components/auth/Turnstile"; import { environment } from "@/services/environment"; import { LoadingLogin } from "./components/LoadingLogin"; import { useLoginPage } from "./useLoginPage"; @@ -18,8 +17,6 @@ export default function LoginPage() { user, form, feedback, - turnstile, - captchaKey, isLoading, isGoogleLoading, isCloudEnv, @@ -85,20 +82,6 @@ export default function LoginPage() { )} /> - {/* Turnstile CAPTCHA Component */} - {turnstile.shouldRender ? ( - - ) : null} -