Initial commit: grok-register with private config and auth artifacts.

Include local config, CPA auth files, account dumps, and temp-mail deploy helpers for the private Gitea repo.
This commit is contained in:
chaos committed 2026-07-12 08:10:44 +08:00
commit 33f966ccea
110 files changed
+6903

No files matched your search

+201
View File
@@ -0,0 +1,201 @@
"""xAI OAuth device-code grant (Grok CLI).
Endpoints from https://auth.x.ai/.well-known/openid-configuration
"""
from __future__ import annotations
import json
import time
import urllib.error
import urllib.parse
import urllib.request
from dataclasses import dataclass
from typing import Any, Callable
from .proxyutil import resolve_proxy
# xAI OAuth client id (matches CLIProxyAPI default)
CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828"
ISSUER = "https://auth.x.ai"
DEVICE_CODE_URL = "https://auth.x.ai/oauth2/device/code"
TOKEN_URL = "https://auth.x.ai/oauth2/token"
SCOPE = "openid profile email offline_access grok-cli:access api:access"
LogFn = Callable[[str], None]
def _noop_log(_: str) -> None:
return None
def _proxy_handler(proxy: str | None = None) -> urllib.request.ProxyHandler | None:
p = resolve_proxy(proxy)
if not p:
return None
return urllib.request.ProxyHandler({"http": p, "https": p})
def _opener(proxy: str | None = None) -> urllib.request.OpenerDirector:
handlers: list[Any] = []
ph = _proxy_handler(proxy)
if ph is not None:
handlers.append(ph)
return urllib.request.build_opener(*handlers) if handlers else urllib.request.build_opener()
def _post_form(
url: str,
form: dict[str, str],
timeout: float = 30.0,
*,
proxy: str | None = None,
) -> tuple[int, dict[str, Any] | str]:
data = urllib.parse.urlencode(form).encode("utf-8")
req = urllib.request.Request(
url,
data=data,
method="POST",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
"User-Agent": "grok-reg-oidc-minter/1.0",
},
)
opener = _opener(proxy)
try:
with opener.open(req, timeout=timeout) as resp:
body = resp.read().decode("utf-8", errors="replace")
status = getattr(resp, "status", 200) or 200
try:
return int(status), json.loads(body)
except json.JSONDecodeError:
return int(status), body
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", errors="replace")
try:
return int(e.code), json.loads(body)
except json.JSONDecodeError:
return int(e.code), body
@dataclass
class DeviceCodeSession:
device_code: str
user_code: str
verification_uri: str
verification_uri_complete: str
expires_in: int
interval: int
raw: dict[str, Any]
@dataclass
class TokenResult:
access_token: str
refresh_token: str
id_token: str | None
token_type: str
expires_in: int
raw: dict[str, Any]
class OAuthDeviceError(RuntimeError):
pass
def request_device_code(
*,
client_id: str = CLIENT_ID,
scope: str = SCOPE,
timeout: float = 30.0,
proxy: str | None = None,
) -> DeviceCodeSession:
status, body = _post_form(
DEVICE_CODE_URL,
{"client_id": client_id, "scope": scope},
timeout=timeout,
proxy=proxy,
)
if status != 200 or not isinstance(body, dict):
raise OAuthDeviceError(f"device code request failed HTTP {status}: {body!r}")
device_code = str(body.get("device_code") or "").strip()
user_code = str(body.get("user_code") or "").strip()
if not device_code or not user_code:
raise OAuthDeviceError(f"device code response missing fields: {body}")
vuri = str(body.get("verification_uri") or "https://accounts.x.ai/oauth2/device").strip()
vcomplete = str(
body.get("verification_uri_complete") or f"{vuri}?user_code={user_code}"
).strip()
expires_in = int(body.get("expires_in") or 1800)
interval = max(int(body.get("interval") or 5), 1)
return DeviceCodeSession(
device_code=device_code,
user_code=user_code,
verification_uri=vuri,
verification_uri_complete=vcomplete,
expires_in=expires_in,
interval=interval,
raw=body,
)
def poll_device_token(
device_code: str,
*,
client_id: str = CLIENT_ID,
interval: int = 5,
expires_in: int = 1800,
timeout: float = 30.0,
log: LogFn | None = None,
cancel: Callable[[], bool] | None = None,
proxy: str | None = None,
) -> TokenResult:
"""Poll token endpoint until authorized or expired."""
log = log or _noop_log
deadline = time.time() + max(expires_in - 5, 30)
sleep_for = max(interval, 1)
while time.time() < deadline:
if cancel and cancel():
raise OAuthDeviceError("cancelled")
status, body = _post_form(
TOKEN_URL,
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": device_code,
"client_id": client_id,
},
timeout=timeout,
proxy=proxy,
)
if status == 200 and isinstance(body, dict) and body.get("access_token"):
access = str(body["access_token"]).strip()
refresh = str(body.get("refresh_token") or "").strip()
if not refresh:
raise OAuthDeviceError("token response missing refresh_token")
return TokenResult(
access_token=access,
refresh_token=refresh,
id_token=(str(body["id_token"]).strip() if body.get("id_token") else None),
token_type=str(body.get("token_type") or "Bearer"),
expires_in=int(body.get("expires_in") or 21600),
raw=body,
)
err = ""
desc = ""
if isinstance(body, dict):
err = str(body.get("error") or "")
desc = str(body.get("error_description") or "")
if err in ("authorization_pending", "slow_down"):
if err == "slow_down":
sleep_for = min(sleep_for + 5, 30)
log(f"oauth poll: {err} (sleep {sleep_for}s)")
time.sleep(sleep_for)
continue
if err in ("expired_token", "access_denied"):
raise OAuthDeviceError(f"device auth failed: {err}: {desc}")
if status == 400 and err:
raise OAuthDeviceError(f"device auth token error: {err}: {desc or body}")
log(f"oauth poll unexpected HTTP {status}: {body!r}")
time.sleep(sleep_for)
raise OAuthDeviceError("device auth timed out waiting for user approval")