Files

104 lines
11 KiB
Markdown

---
name: browser-signup-automation
description: Browser-driven account and referral signup automation using SeleniumBase/Playwright with temp mail, OTP polling, anti-bot fallbacks, and deterministic verification.
triggers: browser signup|browser registration|selenium signup|otp signup|referral bot|vercel signup|v0 referral
---
# Browser Signup Automation
## Use when
- The task is browser-based account creation or referral completion.
- The flow requires a real browser, JavaScript, redirects, OTP/email verification, or anti-bot handling.
- API-only registration is unavailable or incomplete.
## Do not use when
- A fully documented HTTP/API signup path exists and is simpler.
- The task is ordinary account access/checkouts rather than new registration.
## Alternative: API-first registration with 2captcha Turnstile bypass
When the browser approach is blocked by Cloudflare Turnstile (CDP/DevTools detection), switch to:
1. **Solve Turnstile via 2captcha** (not browser click) — submit sitekey + pageurl to 2captcha `in.php?method=turnstile`, poll `res.php` for the token.
2. **Find the server-side turnstile validation endpoint** — look for patterns like `turnstile-signup`, `captcha/verify`, `token/validate` in the site's JavaScript bundles or network logs.
3. **Call the validation endpoint** with `{email, token}` — if it returns `204 No Content` or similar, the token is accepted server-side.
4. **Complete registration via the actual auth API** — Firebase REST (`identitytoolkit.googleapis.com/accounts:signUp`), Auth0, or custom API.
### Identifying the signup API from JS bundles
- Load modulepreloads listed in the page's HTML `link[rel="modulepreload"]`.
- Check the importmap (`script[type="importmap"]`) for hash resolution — old hashes in JS imports map to current hashes.
- Focus on files named `signup*`, `LoginSignupForm*`, `_auth*`, and `auth*` — these contain the registration component.
- Search for server-function calls (`/_serverFn/<hash>`) and external API calls (`https://api.example.com/auth/`) in the bundle text.
- Trace the Turnstile widget component — typically rendered with `siteKey`, `action`, `onTokenChange` props. The `action` string (e.g. `signup_email_password`) must match when solving via 2captcha.
- See `references/turnstile-api-first-registration.md` for session-specific examples (SolidStart server functions, Firebase Auth integration).
### Turnstile CDP Detection Facts
- Turnstile with implicit rendering (the checkbox "Verify you are human") **detects Chrome DevTools Protocol** reliably. Clicking the checkbox via CDP always results in `failure_retry` → "Verification failure".
- Setting the hidden `cf-turnstile-response` input value via JS is NOT enough — the form framework checks `window.turnstile.getResponse()` internally, which returns empty.
- To properly inject a 2captcha-solved token there are three options, in order of preference:
- **(a) Stub `window.turnstile` BEFORE api.js loads and let the app call you.** Serve your own JS from `page.route("**/challenges.cloudflare.com/turnstile/**", fulfill(stub))` plus `add_init_script(stub)`; the stub's `render(el, opts)` immediately invokes `opts.callback(TOKEN)` (and `opts["before-interactive-callback"]()` first). The framework's own state updates, the submit button unlocks, and **the page sends the request itself** with every native header it normally adds. This is the only option that works when the endpoint also requires client-side attestation you cannot reproduce (WorkOS Radar `signalsId`, request-correlation ids, fingerprint headers) — hand-fetching such an endpoint returns 403 (`request_unverified` / Cloudflare WAF challenge) even with a perfectly valid token.
- **The token must be literal text inside the stub** (`STUB.replace("__TOKEN_PLACEHOLDER__", json.dumps(tok))`), never a global assigned by a separate init script: the fulfilled api.js runs in a context the earlier assignment does not reach. Symptom of getting this wrong is silent — stub installed, `render` called, button stays disabled forever. Put `console.log` lines in the stub and watch them via `page.on("console")`; the DOM looks identical either way.
- Full worked example with probe logs: `ai-provider-autoreg` → `references/workos-turnstile-native-submit.md`.
- **(b) call the Turnstile success callback directly** on a real widget (needs the widget to have rendered and a reachable widgetId).
- **(c) find the server-side endpoint that accepts the raw token** (bypasses widget state entirely) — viable only when the backend does not demand browser attestation alongside the token.
- Pass the widget's exact `action` to the solver (from the app's actions enum, e.g. `password_signup`). A valid token solved with an empty/mismatched action is rejected as unverified.
- If one solver's tokens are consistently rejected, switch solver before rewriting the payload — a dead/`internal error` solver looks identical to a wrong payload from the outside.
- Temp-mail domains are often blocked server-side. Check for a blocked-domain Set in the registration JS bundle — it may contain hundreds of entries.
## Pitfalls
- Browser automation can appear to "work" because the email field fills and button clicks, while the form never transitions. Treat that as failure, not progress.
1. Hardcode or pass the target referral/signup URL explicitly; avoid blank defaults that require manual menu input.
2. Prefer a CLI entrypoint over an interactive menu for unattended runs.
3. Create temp mail first, then launch the browser instance.
4. Navigate explicitly after browser activation; do not assume CDP activation already opened the target URL.
5. Verify navigation left `about:blank` before waiting for page elements.
6. Wait for the email field using direct DOM/JS polling first; treat slow framework default timeouts as a bug source.
7. After filling email, submit the form in layers:
- visible button click
- fallback alternate text button
- form.requestSubmit()/form.submit()
- Enter key dispatch as last resort
8. Only wait for OTP after confirming the UI truly left the email-entry step.
9. Distinguish these states explicitly:
- still on email form
- OTP screen visible
- redirected to final destination
- blocked/challenged/error state
10. Poll mail only after the OTP UI is real; otherwise you waste time on emails that may never be sent.
11. Keep final verification strict: success means redirected/landed on the target destination, not just that email was submitted.
## Implementation rules
- For SeleniumBase + argparse scripts, do NOT inject `sys.argv.append("-n")`; it can break CLI parsing with `unrecognized arguments: -n`.
- Expose `--ref`, `--workers`, `--headless`, and optional `--menu` so the script can run unattended.
- Add a top-level `HEADLESS` flag and map it consistently into the browser constructor.
- In Chrome args for server-like runs, include `--no-sandbox` and `--disable-gpu`.
- Prefer bulk `type()` or JS value assignment over slow per-key typing for email/OTP fields.
- When clicking submit buttons is flaky, submit the closest form via JS before trying synthetic Enter.
## Pitfalls
- Browser automation can appear to "work" because the email field fills and button clicks, while the form never transitions. Treat that as failure, not progress.
- **Gmail +aliases detected by some sites**: Token Harbor and similar platforms strip the `+alias` part from Gmail addresses, detecting `user+th1@gmail.com` and `user+th2@gmail.com` as the same account. Use different email providers (t-online.de pool) or completely different base emails for multi-account registration.
- **API key created ≠ API key works**: Some platforms allow API key creation without email verification, but the key returns `email_not_verified` on API calls. Always test the key with a real API call before declaring success.
- **$5/credit bonuses require verification first**: click the bonus claim button AFTER email verification, not before.
- **Signup credit can be granted asynchronously after a fraud review — do not conclude "no bonus" from a fresh account.** On abliteration.ai, `/api/console/v1/rewards` returned `eligible:false, status:"review_required", signup_credit_usd_micros:0` right after verify on 4 of 5 accounts, then `eligible:true, status:"available", signup_credit_usd_micros:1000000` (= $1) on one account ~45 min later; only that account's key passed a real chat completion (others: `insufficient_credits`). Rule: read the rewards/credits API rather than the pricing page, and re-poll older accounts before judging yield. Note marketing pages reuse the same figure for unrelated things — the "$1" there was the minimum prepaid top-up.
- **Provisioning behind the signup can be async too.** A console/session endpoint may answer `409 workspace_setup_pending` for minutes after a successful verify; poll it (5s intervals, generous cap) before declaring failure, and keep the account in a re-try list so a later pass can still collect its key.
- Temp-mail acceptance is target-specific. If the page never transitions after submit, suspect temp-mail rejection or hidden anti-bot checks.
- **The temp-mail API may ignore the address you ask for.** Voidash (`POST /api/v1/inboxes {"domain":...}`) accepts `address`/`local_part` in the body and then hands back its own random address; registering your invented name sends the OTP to a mailbox you do not hold, so the flow dies silently at "waiting for code". Always take the address **from the create response** and register that. Also confirm which read endpoint actually works for the session key (`GET /api/v1/messages` did; `/api/v1/inboxes/messages` → 404 on a fresh inbox).
- **A verification-code form may auto-submit** once the code is complete. Clicking its button then races the framework and dies with `TimeoutError ... waiting for element to be enabled` while it is `aria-busy`. Set the value with the native setter, poll for the network response, and click only as a fallback.
- **OAuth buttons sit above the password form on many sign-in pages.** `locator("button[type=submit]").first` hits "Continue with Google" and the session ends up on `accounts.google.com`. Scope the click: `document.querySelector('input[name="password"]').closest('form')` → its own submit button, then assert `page.url` has no `accounts.google.com`.
- **A validation error body often names the missing field.** `422 {"param":"header.Idempotency-Key"}` told us exactly which header to add (one UUID → 201). Read `param`/`field`/`code` before probing endpoint variants — probes burn IP rate-limit budget.
- Driver bootstrap/download noise can mask the real application logs; wait past first-run driver install before diagnosing the flow.
- A running browser process is not success; require OTP screen or final redirect.
## Verification checklist
- Browser opened the exact target URL.
- Email field became visible and value matched expected address.
- Submit action changed page state.
- OTP screen appeared OR an explicit block/error was captured.
- Final URL matched the intended destination.
## Support files
- `references/vercel-v0-referral-signup.md` — Vercel/v0-specific quirks, observed failures, and launch defaults.