11 KiB
Laravel OAuth-bypass + Next.js native-Turnstile registration patterns
Two reusable flows proven on small free-credit API providers (cleanapis.com, terrapolis.xyz, abhibots/opus). Scripts of record: C:/Users/User/tmp/freeapi_3pack/ca_mass_gh.py (Laravel/GitHub-OAuth) and terr_full.py (Next.js/native-Turnstile + voidash verify). Reuse C:/Users/User/tmp/regfarm/github_oauth/gh_http.py (GHSession.login, GHSession.oauth_code(client_id, state, scope=...)).
Pattern A — Laravel + CF Turnstile: bypass via GitHub OAuth (preferred)
When a Laravel site's email registration is Turnstile-gated AND server-side siteverify rejects solver tokens, do NOT fight the captcha — check /register page for /auth/github/redirect / /auth/google/redirect links. OAuth paths are almost never captcha-gated.
Flow (cleanapis.com, generalized):
GET /auth/github/redirect(no follow) → parsestatefrom the Location header's GitHub authorize URL. Also noteclient_idfrom that URL (cleanapis:Ov23li48tsmVvDWqWfhs, scopeuser:email,read:user).GHSession(bind=False).login(login, password, totp)on a pooled GH account →oauth_code(client_id, state, scope=...)→ authorization code.GET /auth/github/callback?code=...&state=...in the SAME requests.Session that fetched the redirect (session cookie carries the state).- Activate free plan if the site gates keys behind a plan: cleanapis =
GET /dashboard/get-started?plan=starter→ extract_token→POST /dashboard/billing/checkout/4with{_token, billing_cycle: monthly, payment_method: paddle, coupon_code: ''}. Pitfall: omittingbilling_cycleorpayment_methodreturns to the same page with<li>validation errors — read them from the response body, they are the API contract. - Create key:
POST /dashboard/api-keyswith{_token, name, scopes[]: [inference, models:read, embeddings]}. - Reveal: key IDs in the response HTML are inside JS-escaped strings (
api-keys\/63476\/reveal) — a strictapi-keys/(\d+)/revealregex misses them. Usere.findall(r'api-keys.{0,4}?(\d{4,7}).{0,4}?reveal', html)and takemax(ids, key=int)for the newest. ThenGET /dashboard/api-keys/{id}/revealwithAccept: application/json→{"ok":true,"key":"cc_..."}. - Verify:
GET /v1/models+ one chat completion withAuthorization: Bearer <key>. Pitfall: a just-revealed key can answerInvalid or revoked API keyfor a short propagation window — re-test after a minute before marking the account dead. Also fetch the model list from/v1/modelsinstead of guessing model IDs (claude-haiku-4-5404'd; the catalog wasclaude-opus-5,claude-sonnet-5,claude-fable-5.1, etc.).
Pacing/limits:
- Key creation is rate-limited: 429 after rapid consecutive creates. Sleep 20–35 s between accounts; on 429 sleep ~90 s and retry the same account (plan activation already succeeded — only the key step needs redo).
- Keep a
used_gh.txtof consumed GH logins; GH poolC:/Users/User/tmp/gh_accounts.json(~672). OAuth code exchange is per GitHub app, sostate fail: 429waves (known from the new-api relay farm) can also hit here — pace and re-enter later. - Email+Turnstile path on cleanapis is a dead end: both 2captcha-remote tokens and sidecar
real_pagetokens POSTed from a separaterequestssession getSecurity verification failed. Only full in-browser submission could work — OAuth is cheaper.
Pattern B — Next.js server-action form + NATIVE Turnstile (terrapolis.xyz)
Signs: registration HTML has <input type="hidden" name="$ACTION_REF_1">, $ACTION_1:0 (JSON with action id), $ACTION_KEY, and fields name/email/password/captcha.
- Raw HTTP POST of the server-action form (multipart or form-encoded, with or without
Next-Actionheader) returns 500 with an RSCE{"digest":...}error — the action expects the real RSC/Next runtime. Don't reverse it; drive a browser. - Use cloakbrowser:
b = cloakbrowser.launch(headless=True, proxy=px)(sync API iscloakbrowser.launch, NOTcloakbrowser.sync_api.sync_playwright— that module doesn't exist; the sidecar's own code importscloakbrowserdirectly).pg = b.new_page(). - The page renders Turnstile ITSELF (
window.turnstilepresent,input[name=captcha]auto-fills, submit button un-disables). Wait fordocument.querySelector('input[name=captcha]').value.length > 100(~6 s on a clean IP). Injecting an externally solved token into the hidden input fails server-side ("Проверка Cloudflare не прошла или истекла") — native resolution is required; the sidecar'sreal_pageroute works only because it solves inside its own page context. - Submit via real click, then detect success by URL change (
/verify-email), not by absence of error text — the page keeps showing marketing copy. - IP limit: 3 accounts per IP per 30 days (error text says so). Rotate proxies, ≤2 accounts per proxy.
- Proxy pre-filter is mandatory: HTTP-reachability ≠ Turnstile-capable. Of ~25 "live" proxies only ~5 completed the native Turnstile (others: goto timeouts, ERR_CERT_AUTHORITY_INVALID, widget never tokens). Filter by actually launching cloakbrowser through each proxy and waiting for the captcha token (see
terr_px_filter.py). - Email verify via voidash (no IMAP needed):
POST https://api.voidash.com/api/v1/inboxes {"domain":"voidash.bond"}→{address, session_key}. Read mail atGET https://api.voidash.com/api/v1/messageswithAuthorization: Bearer <session_key>— the per-inbox path/inboxes/{addr}/messagesreturns 404 "inbox not found". Poll forhttps://terrapolis.xyz/verify?token=...in the JSON body (unescape\/), open it in the browser, expect "Почта подтверждена". - Browser stability: wrap
goto+wait_for_selectorin a 3-attempt retry that closes and reopens the page — headless sessions intermittently fail to render the form; a fresh page fixes it.
Pattern C — Google-OAuth-only sites (abhibots/opus)
No email registration endpoint exists. GET /api/auth/google/signin 307-redirects to accounts.google.com/o/oauth2/auth?client_id=...&prompt=select_account. FastAPI-style backend ({"detail":"Method Not Allowed"} on wrong verbs). Requires a logged-in Google account pool to automate — skip unless one exists.
Pattern B2 — raw Next-Action HTTP POST + native CDP Turnstile harvest (vani.ai)
Contrast with Pattern B (raw POST → 500 digest): some Next.js App Router sites DO accept server actions over plain HTTP. Probe before launching any browser: POST to the page URL with headers Next-Action: <actionId> (ids live in the page bundle as createServerReference("<id>",...)), Accept: text/x-component, Origin/Referer of the site, multipart body with 1_-prefixed fields (1_email, 1_turnstile_token, 1_next) plus a trailer field 0 = ["$K1",true]. A structurally accepted call returns an RSC payload containing a line 1:{"status":...} — parse the state from there (^1:(\{.*?\})$ MULTILINE). 500 with E{"digest"...} = action needs the real RSC runtime → fall back to Pattern B browser drive.
Native Turnstile token harvest without cloakbrowser or paid solvers ($0): launch real Chrome --remote-debugging-port=N --remote-allow-origins=* --user-data-dir=<fresh> → CDP Target.createBrowserContext → createTarget(loginURL) → attachToTarget{flatten:true} → poll typeof window.turnstile == 'object' → Runtime.evaluate a Promise that calls window.turnstile.render(detachedDiv, {sitekey, action:'signup', theme:'auto', size:'flexible', callback}) → token (~750 chars, ~6 s). Order pitfall: render the widget BEFORE filling form inputs — a pre-filled email state can reset/block the widget on some pages. Proxy runs: add --proxy-server=http://host:port; if window.turnstile never appears through a proxy, that proxy drops the challenges script — pre-filter proxies by widget presence, not by HTTP reachability.
Backend-outage diagnosis (saves solver credits and hours): if structurally valid NATIVE tokens are rejected with the same generic error ("temporarily unavailable", "security check didn't pass") from a clean IP AND several country proxies AND different mailbox domains AND all widget action variants (signup/login/signin/verify), the failure is server-side (their siteverify secret or email provider is dead) — not your token, IP, or mail. Check their status page and recent incident reports, STOP brute-forcing, and deploy a silent watcher cron that retries the full cycle every few hours until it passes.
Watcher-cron pattern for "target down, auto-finish later": cron job with no_agent=true and empty-stdout-means-silent delivery. The cron script must be a filename relative to ~/AppData/Local/hermes/scripts/ — put a small Python wrapper there that subprocess.runs the real bash script with PYTHONPATH removed from env. The bash wrapper: kill stale target-matched chromes via Get-CimInstance Win32_Process (CommandLine match), run the reg attempt, then an idempotent notify_once.py (marker json + .notified flag → bot sendMessage). Reg script: mtime-checked lockfile against overlapping runs, marker file (*_account.json with email/cookies) written only on success, print one stdout line on success so cron delivers it; failures log to a local file only.
Vani snapshot (2026-10-12): platform.vani.ai signup = magic-link/email-code server actions (magic 603c7204c5c2433875cd7af57e3cc552e17ca3101b, code 604615c8bb79e439416b1fc99fd6da1a4847ea835e, OTP 6 digits), password signup disabled server-side; Turnstile sitekey 0x4AAAAAAFM_3cPV7Jg5kRlB. Blocked by THEIR email backend outage (all variants above rejected identically). api.vani.ai/v1 is live OpenAI-compat (401 unauthenticated); keys sk_live_* are created at /dashboard/keys after login. Files: tmp/vani/{vani_watcher.py, run_watcher.sh, notify_once.py, vani_reg*.py}; cron vani-autoreg-watcher (ffc0ce7d5cb0, every 3h, silent-on-fail).
Recon order for any new small provider
curlhome +/register+/login; identify stack (Laravel_token/ Next.js$ACTION_*/ new-api / FastAPIdetail). 1.5. For Next.js: extractcreateServerReferenceaction ids from the page bundle and probe a rawNext-Actionmultipart POST (Pattern B2) BEFORE building any browser automation — the RSC response shape immediately tells you HTTP-callable vs browser-only.- Grep register HTML for
/auth/(github|google)/redirect→ if present, Pattern A (no captcha needed). - Else check whether the page natively renders Turnstile (
window.turnstile, auto-filled captcha input) → Pattern B with cloakbrowser + proxy pre-filter. - Grep for per-IP signup limits in error copy AFTER the first failure — sites state them in the response body ("не больше N аккаунтов за M дней").
- Always end-to-end verify: registration success ≠ usable credit. Fetch the model list, run one chat completion, save
api_key+ account creds to a JSONL bank.