104 lines
11 KiB
Markdown
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.
|