Prefer SSO OAuth PKCE for CPA minting and sync latest accounts.

Switch CPA export to SSO→Authorization Code with referrer=grok-build, keep device-code as optional fallback, and capture new register/auth artifacts.
This commit is contained in:
chaos committed 2026-07-12 15:54:24 +08:00
1 parent 8b3664a2d5
commit ee151343e0
455 files changed
+9051 -59

No files matched your search

+45 -6
View File
@@ -4,6 +4,9 @@ internal/auth/xai/token.go 的 TokenStorage 结构。
生成的 xai-<email>.json 可被 CLIProxyAPI 直接加载;CLIProxyAPI 请求 grok 时会
自带 x-grok-client-version 头(xai_executor.go 硬编码 0.2.93),免费 Build 账号
不会 426。
2026-07:AccessToken 必须含 referrer=grok-build,否则 cli-chat-proxy 拒用。
铸造请走 oidc_mint.oauth_code(SSO→Authorization Code + PKCE)。
"""
from __future__ import annotations
@@ -24,6 +27,18 @@ CLI_BASE_URL = "https://cli-chat-proxy.grok.com/v1"
API_BASE_URL = "https://api.x.ai/v1"
DEFAULT_BASE_URL = CLI_BASE_URL
GROK_VERSION = "0.2.93"
GROK_TOKEN_UA = (
f"grok-pager/{GROK_VERSION} grok-shell/{GROK_VERSION} (linux; x86_64)"
)
DEFAULT_HEADERS = {
"x-grok-client-version": GROK_VERSION,
"x-xai-token-auth": "xai-grok-cli",
"x-authenticateresponse": "authenticate-response",
"x-grok-client-identifier": "grok-pager",
"User-Agent": GROK_TOKEN_UA,
}
def _sanitize_file_segment(value: str) -> str:
"""对齐 CPA CredentialFileName 的清洗规则。"""
@@ -65,14 +80,15 @@ def _jwt_payload(token: str) -> dict[str, Any]:
return json.loads(base64.urlsafe_b64decode(seg))
def expired_from_access_token(access_token: str) -> tuple[str, int, str]:
"""从 access_token 解析 (expired_rfc3339, expires_in, sub)。"""
def expired_from_access_token(access_token: str) -> tuple[str, int, str, str]:
"""从 access_token 解析 (expired_rfc3339, expires_in, sub, referrer)。"""
pl = _jwt_payload(access_token)
exp = int(pl["exp"])
iat = int(pl["iat"]) if pl.get("iat") is not None else exp - 21600
expired = datetime.fromtimestamp(exp, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
sub = str(pl.get("sub") or pl.get("principal_id") or "").strip()
return expired, max(exp - iat, 0), sub
referrer = str(pl.get("referrer") or "").strip()
return expired, max(exp - iat, 0), sub, referrer
def build_cpa_xai_auth(
@@ -88,6 +104,9 @@ def build_cpa_xai_auth(
base_url: str = DEFAULT_BASE_URL,
token_endpoint: str = TOKEN_ENDPOINT,
redirect_uri: str = REDIRECT_URI,
sso: str | None = None,
headers: dict[str, str] | None = None,
disabled: bool = False,
) -> dict[str, Any]:
"""组装一个 CLIProxyAPI 可导入的 xai auth 对象(TokenStorage 字段)。"""
access_token = (access_token or "").strip()
@@ -97,10 +116,11 @@ def build_cpa_xai_auth(
if not refresh_token:
raise ValueError("refresh_token is required (CPA 无法在缺 refresh_token 时续期)")
referrer = ""
try:
exp_s, exp_in, sub_jwt = expired_from_access_token(access_token)
exp_s, exp_in, sub_jwt, referrer = expired_from_access_token(access_token)
except Exception:
exp_s, exp_in, sub_jwt = "", 21600, ""
exp_s, exp_in, sub_jwt, referrer = "", 21600, "", ""
if not expired:
expired = exp_s
@@ -111,10 +131,23 @@ def build_cpa_xai_auth(
if not last_refresh:
last_refresh = datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
# 从 id_token 补 email
email = (email or "").strip()
if not email and id_token:
try:
idp = _jwt_payload(id_token)
email = str(idp.get("email") or "").strip()
except Exception:
pass
base_url = (base_url or DEFAULT_BASE_URL).rstrip("/")
if not re.search(r"/v1$", base_url) and base_url.endswith("cli-chat-proxy.grok.com"):
base_url = base_url + "/v1"
hdrs = dict(DEFAULT_HEADERS)
if headers:
hdrs.update({str(k): str(v) for k, v in headers.items() if k and v is not None})
payload: dict[str, Any] = {
"type": "xai",
"auth_kind": "oauth",
@@ -124,12 +157,18 @@ def build_cpa_xai_auth(
"expires_in": int(expires_in),
"expired": expired,
"last_refresh": last_refresh,
"email": (email or "").strip(),
"email": email,
"sub": (sub or "").strip(),
"base_url": base_url,
"token_endpoint": token_endpoint,
"redirect_uri": redirect_uri,
"disabled": bool(disabled),
"headers": hdrs,
}
if id_token:
payload["id_token"] = id_token.strip()
if sso:
payload["sso"] = str(sso).strip()
if referrer:
payload["referrer"] = referrer
return payload