# OTP Entry & Post-Registration Provisioning ## OTP Entry: Multi-Strategy with Fallback GitHub's verification page has changed over time. The current flow (2026-08-10) uses a **single numeric input**, NOT the old `launch_code[]` digit boxes. Use 3 strategies in order: ### Strategy 1: launch_code[] digit boxes (LEGACY — GitHub pre-2026-08) ```html × 8 ``` If found (8 boxes), fill each individually and submit the form. ### Strategy 2: Single numeric input (CURRENT — works 2026-08-10) ```javascript var e = document.querySelector('input[inputmode="numeric"]') || document.querySelector('input[aria-label*="code" i]') || document.querySelector('input[aria-label*="verif" i]') || document.querySelector('input[type="text"]:not([name="login"]):not([name="password"])'); e.value = code; e.dispatchEvent(new Event('input', {bubbles: true})); e.dispatchEvent(new Event('change', {bubbles: true})); // Submit: form.requestSubmit() or click button[type="submit"] ``` ### Strategy 3: Brute force (last resort) Type code into ANY visible input and click any button matching verify/continue/submit/confirm. ### Wait for input to appear The verification page may render inputs dynamically. Wait up to 10s: ```python for _ in range(20): any_input = document.querySelectorAll('input:not([type="hidden"]):not([type="submit"])').length if any_input > 0: break time.sleep(0.5) ``` ## Post-OTP Result Detection After OTP submission, GitHub can redirect to **multiple states**. Use **BOTH path and full URL** for matching: | URL Pattern | Path | Meaning | Action | |-------------|------|---------|--------| | `/dashboard`, `/welcome`, `/` | match | Account created + auto-logged-in | Extract session cookies | | `/login?return_to=...account_verifications` | `/login` | Still on verification page (OTP pending/failed) | Check for error messages after 10s | | `/login` with flash "account was created successfully" | `/login` | Account created, needs login | Do post-signup login | | `/login` without flash | `/login` | Registration failed or redirected | Abort | **Critical: `/login?return_to=.../account_verifications` is the verification page.** The path is `/login`, but the full URL contains `account_verif`. Check BOTH: ```python path = urlparse(url).path.rstrip("/") url_lower = url.lower() # still_on_verify: check BOTH path and full URL still_on_verify = ("account_verif" in path and "launch" not in path) or \ ("account_verif" in url_lower and "launch" not in url_lower) ``` **Time-limit the still_on_verify check:** in the post-OTP loop, only skip for first 10s of the 30s window. After that, evaluate other signals (has_session, dashboard, login, code error): ```python deadline = time.time() + 30 while time.time() < deadline: # ... get URL, path, cookies ... if still_on_verify and time.time() < deadline - 20: # first 10s only time.sleep(1) continue # ... check other signals ... ``` ## Post-Signup Login If GitHub shows "Your account was created successfully! Please sign in to continue": 1. Navigate to `https://github.com/login` 2. Fill `#login_field` (email) and `#password` (password) 3. Click submit button 4. Navigate to `https://github.com/settings/profile` to verify authenticated 5. Extract `user_session` cookie ## Session Verification ```python page.goto("https://github.com/settings/profile") # If redirected to /login → NOT authenticated # If stays on /settings/profile → AUTHENTICATED ``` ## 2FA + PAT via provision.py After registration, use the `provision.py` from `авторег проект/backend/platforms/github/provision.py` for post-processing. **WARNING: HTTP provisioning is rate-limited on the same IP.** If the browser registration used the local IP, the HTTP `requests`-based login to the fresh account triggers "login failed: rate". To avoid this: - Use the SAME proxy for provisioning as registration, OR - Extract cookies from the browser session and inject them into `requests.Session`, OR - Do 2FA + PAT through the browser (CDP) instead of HTTP ### enable_2fa(email, password) → {totp_secret, recovery_codes} Pure HTTP (requests + pyotp), no browser needed. Flow: 1. GET `/login` → extract CSRF 2. POST `/session` → authenticate 3. GET `/settings/two_factor_authentication/setup/intro` → extract scoped CSRF 4. POST `/settings/two_factor_authentication/setup/initiate` (type=app) → get secret + recovery codes 5. POST `/settings/two_factor_authentication/setup/verify` (type=app, otp=TOTP) 6. POST `/settings/two_factor_authentication/setup/enable` ### generate_token(email, password, totp_secret, scopes) → ghp_/github_pat_... Creates a classic PAT. Flow: 1. Login with 2FA 2. Handle sudo re-auth if needed 3. GET `/settings/tokens/new` → extract CSRF 4. POST `/settings/tokens` with scopes 5. Extract `ghp_` or `github_pat_` from response ## Rate Limiting GitHub applies a **secondary rate limit** after 2-3 fast signups. Symptoms: - Form fields not found (all `querySelector` return null — `{"email":false,"password":false,"login":false}`) - Page stays on `/signup` after submit - HTTP login returns "login failed: rate" **Mitigation:** - Wait 10-15 minutes between batches - Use proxy rotation (SOCKS5 from residential pool) - Rate limit on same IP persists **1+ hour** — 15-minute wait is insufficient - HTTP provisioning (requests) hits the SAME rate limit as browser — use same proxy or browser cookies ## Verified - **2026-08-10**: `launch_code[]` boxes NO LONGER present on GitHub verification page. Single `input[inputmode="numeric"]` strategy works (S2). - **2026-08-10**: Multi-strategy OTP entry (S1→S2→S3) with explicit logging confirmed working - **2026-08-10**: Post-OTP `still_on_verify` check must be time-limited (10s max) — otherwise catches `/login?return_to=...account_verifications` indefinitely - **2026-08-10**: Both path AND full URL needed for verification page detection (path alone misses `/login?return_to=...account_verifications`) - **2026-08-10**: HTTP provisioning rate-limited on local IP (`login failed: rate`). Must use same proxy as browser or inject browser cookies. - provision.py verified working (2026-08-06, from `авторег проект` backend)