Files
autoreg-skills-max/skills/browser-signup-automation/references/turnstile-api-first-registration.md

4.3 KiB

API-First Registration + Turnstile Bypass — Lovable.dev Example

Context

Session: 2026-08-04. Goal: register accounts on lovable.dev. The signup is blocked by Cloudflare Turnstile (CDP detection). The site uses SolidStart (SolidJS) with Firebase Auth backend.

Signup Flow

  1. GET https://lovable.dev/signup — initial page with email-only field
  2. Enter email → hits POST https://api.lovable.dev/auth/check-auth-provider {email}
    • Returns provider list, userExists flag, domainLock, recoveryState
  3. Password form appears → Turnstile widget rendered with sitekey 0x4AAAAAAChnKAZBY0iFpFHC
  4. Form submit:
    • Calls POST https://api.lovable.dev/auth/turnstile-signup {email, token} → 204
    • Then Firebase createUserWithEmailAndPassword(email, password)
  5. After account creation → redirect to /verify-email or login

SolidStart Server Function Pattern

The form uses SolidStart RPC: /_serverFn/<sha256hash>. These hash-based endpoints proxy to api.lovable.dev.

  • /_serverFn/[REDACTED-KEY] — email check (GET)
  • Refresh between page loads — hashes change per deploy

2captcha Configuration

import requests, time

def solve_turnstile(sitekey, pageurl, action="signup_email_password", api_key="[REDACTED-KEY]", timeout=60):
    r = requests.get("https://2captcha.com/in.php", params={
        "key": api_key, "method": "turnstile",
        "sitekey": sitekey, "pageurl": pageurl,
        "action": action, "json": 1
    }, timeout=10)
    if r.json().get("status") != 1:
        return None
    cid = r.json()["request"]
    for _ in range(timeout // 3):
        time.sleep(3)
        r2 = requests.get("https://2captcha.com/res.php", params={
            "key": api_key, "action": "get", "id": cid, "json": 1
        }, timeout=10)
        if r2.json().get("status") == 1:
            return r2.json()["request"]
    return None

Key params: method=turnstile, action=signup_email_password (must match the widget's action). Token is ~837 chars.

Turnstile CDP Detection

  • Chrome DevTools Protocol is detected by Turnstile's implicit challenge mode
  • Clicking the checkbox via CDP tools (mcp__chrome_devtools__click) produces: URL changes from /new/normal to /failure_retry/normal, iframe shows "Сбой проверки" (Verification failure)
  • Multiple retries with 2captcha tokens still fail because the form framework (turnstile.getResponse()) returns empty even when the hidden input is set
  • The Turnstile iframe also resets itself between clicks, each time showing /new/normal

Key API Endpoint

POST https://api.lovable.dev/auth/turnstile-signup

Headers required:

  • Content-Type: application/json
  • Origin: https://lovable.dev
  • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36

Body: {"email": "user@example.com", "token": "<2captcha-solved-token>"}

Returns: 204 No Content on success.

Rate limit: 2000/hour (x-ratelimit-remaining starts at 2000).

Temp-Mail Domain Blocklist

Lovable maintains a large blocked-domain Set (po) with ~300 entries in the JS bundle. Common blocked patterns:

  • yopmail.com, guerrillamail.com, mail.tm, tempmail domains
  • Any .sbs, .rest TLDs
  • Domains with + in the local part (plus-addressing) unless on whitelist (lovable.dev only)
  • Full list visible in the LoginSignupForm* JS bundle under po = new Set([...])

JS Bundle Discovery Pattern

  1. Load page → extract modulepreloads from <link rel="modulepreload">
  2. Read importmap <script type="importmap"> — maps old hash names to current ones
  3. Target files: signup-*.js, LoginSignupForm-*.js, _auth-*.js
  4. Search for turnstile-sitekey, serverFn calls, API endpoints
  5. The importmap format: {"/assets/old-name.js": "/assets/new-name.js"}

Firebase Auth Integration

After turnstile validation, the client-side bundle calls Firebase Auth REST API via identitytoolkit.googleapis.com/v1/accounts:signUp?key=<API_KEY>. The API key is embedded in the webpack/rolldown bundle (not on window.firebase).

What Didn't Work

  • Setting cf-turnstile-response input value directly — form still shows disabled button
  • Clicking Turnstile checkbox via CDP — detected and rejected
  • Puppeteer-extra-stealth via browser_navigate tool — Chrome crashes with sandbox error
  • Standard HTTP client (requests) — blocked by Cloudflare