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}
-