145 lines
6.2 KiB
Markdown
145 lines
6.2 KiB
Markdown
# 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
|
||
<input name="launch_code[]" type="number" maxlength="1"> × 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) |