Files
autoreg-skills-max/skills/account-validation/references/github-oauth-programmatic-login.md

3.9 KiB

Programmatic GitHub OAuth Login (requests-based)

Full OAuth flow without a browser — for new-api/one-api/gorouter family services. Works up to the 2FA gate.

Architecture Discovery

new-api/one-api forks expose OAuth config via GET /api/status:

{
  "github_oauth": true,
  "github_client_id": "Ov23lipc1Ups6bRqeQYE"
}

The callback URL is always {server_address}/oauth/github (visible in admin panel JS).

Full Flow

1. Construct GitHub OAuth URL

import secrets
state = secrets.token_hex(16)
url = f"https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&scope=user:email&state={state}"

2. GET authorize page → parse login form

session = requests.Session()
session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
})
r = session.get(auth_url, allow_redirects=True, timeout=30)

If not already logged into GitHub, redirected to https://github.com/login?....

Parse hidden fields:

from bs4 import BeautifulSoup
soup = BeautifulSoup(r.text, 'html.parser')
token = [REDACTED]'input', {'name': 'authenticity_token'}).get('value')

# Also needed (GitHub anti-bot):
timestamp = re.search(r'name="timestamp"[^>]*value="([^"]+)"', r.text).group(1)
timestamp_secret = re.search(r'name="timestamp_secret"[^>]*value="([^"]+)"', r.text).group(1)

3. POST login credentials

login_data = {
    'login': 'USERNAME',         # GitHub username, NOT email
    'password': 'PASSWORD',
    'authenticity_token': token,
    'timestamp': timestamp,
    'timestamp_secret': timestamp_secret,
}
r2 = session.post('https://github.com/session', data=login_data, allow_redirects=True)

4. Route by redirect target

Final URL contains Meaning Action
two-factor 2FA required STOP — cannot proceed without user
authorize Login OK, OAuth consent needed Submit authorization form (step 5)
session or login Login FAILED Check for error messages in page
gorouter.app (or target) Already authorized Extract session cookies — done

5. Submit OAuth authorization

soup2 = BeautifulSoup(r2.text, 'html.parser')
auth_token = [REDACTED]'input', {'name': 'authenticity_token'}).get('value')
r3 = session.post(r2.url, data={'authenticity_token': auth_token}, allow_redirects=True)

if 'gorouter.app' in r3.url:  # or target service domain
    # Session cookies are in session.cookies — use them
    print(f"Session: {dict(session.cookies)}")

6. Use the session

# Access protected endpoints with the authenticated session
r4 = session.get('https://gorouter.app/api/v1/token?p=1&size=20',
                 headers={'Accept': 'application/json'})

Pitfalls

2FA is a hard stop

GitHub 2FA redirects to https://github.com/sessions/two-factor/app. There is NO programmatic bypass. The page may offer "Use recovery code" or "Use security key" — none are automatable. Stop and report to user.

Username, not email

The login field in the POST to /session takes the GitHub username (e.g. Chunkuotriumph), NOT the email. Using email may still work but username is canonical.

After successful OAuth, the session cookie is set for the target service domain (e.g. gorouter.app), not github.com. Use the same requests.Session() throughout the flow.

Timestamp fields are dynamic

timestamp and timestamp_secret change on every page load. Always parse them fresh — never reuse from a previous attempt.

When to Use

  • Validating GitHub credentials against new-api/one-api services
  • Programmatic login when browser is unavailable
  • Checking if a GitHub account has 2FA before attempting full browser flow
  • Batch checking which accounts in a pool can access a service