Files
autoreg-skills-max/skills/github-oauth-automation/references/browser-gateway.md

3.2 KiB

Browser Gateway Pattern

Built 2026-07-29 for gorouter.app. Use when API keys are broken but browser sessions work.

Problem

API service returns 401 "Invalid token" for all API keys (even freshly created ones), but the browser playground works fine with session cookies. Direct HTTP requests with cookies get blocked by Cloudflare (CF 1010). Need a working API endpoint.

Solution: Playwright-Based API Proxy

Run a long-lived Playwright browser with an authenticated session. Route incoming API requests through page.evaluate() → fetch() — the request originates from the browser context, bypassing Cloudflare.

Architecture

Client (curl) → localhost:8777/v1/chat/completions
                    ↓
              aiohttp handler picks an active Playwright page
                    ↓
              page.evaluate(fetch(gourouter.app/v1/chat/completions))
                    ↓
              response returned to client

Core Code

async def api_request(self, page, method, path, body=None):
    """Make API request through browser context"""
    js_code = f"""
    (async () => {{
        const headers = {{ 'Content-Type': 'application/json' }};
        const opts = {{ method: '{method}', headers }};
        if (body) opts.body = JSON.stringify(body);
        const resp = await fetch('https://gorouter.app{path}', opts);
        const text = await resp.text();
        return JSON.stringify({{
            status: resp.status,
            body: text,
            headers: Object.fromEntries(resp.headers.entries()),
        }});
    }})()
    """
    for attempt in range(3):
        try:
            result = await page.evaluate(js_code)
            return json.loads(result)
        except Exception as e:
            if attempt < 2:
                await asyncio.sleep(2)
    return {"status": 0, "body": "all attempts failed"}

Session Management

  • One BrowserContext per authenticated user
  • Login via GitHub OAuth + 2FA TOTP (same flow as auto-reg)
  • Check liveness with await page.evaluate("1") before each request
  • Remove dead contexts on failure

Key Design Decisions

  1. Playwright, not raw HTTP: Cloudflare blocks non-browser User-Agents. page.evaluate() runs in the real browser context.
  2. aiohttp for the proxy server: Lightweight async HTTP. One browser instance, multiple routes.
  3. Session pool, not one-session-per-request: Login is slow (~60s). Keep sessions alive and reuse.
  4. 3 retries on fetch failures: Browser internals can flake; retry before giving up.

Pitfalls

  • Browser sessions expire: Gorouter cookies last ~30 min. Gateway needs periodic re-login.
  • One browser = one IP: All proxied requests share the same browser fingerprint and IP. Rate limits apply.
  • Memory: Each Playwright page consumes ~50-100MB. Pool size is limited.
  • Not for production: This is a workaround for broken API keys. Replace with real API keys when the service fixes auth.

Full Reference Implementation

See gorouter_gateway.py for the complete implementation with:

  • /login endpoint to add sessions
  • /v1/chat/completions — OpenAI-compatible
  • /v1/models — model list
  • /health — session status