mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-09 13:21:42 +00:00
Replace the loopback/PKCE-callback server and manual-paste fallback with the RFC 8628 device-code flow as the only xAI Grok OAuth login path. The flow works in headless/SSH/container sessions with no 127.0.0.1 listener, shrinking the local attack surface. - Poll the token endpoint with server-provided interval, honoring slow_down and expires_in; store tokens with auth_mode oauth_device_code. - Adaptive proactive refresh skew for short-lived device-code JWTs; rotated tokens sync back to auth.json, the global root store, and the credential pool (no refresh-token replay). - Clear source suppression on successful re-login (CLI + dashboard) and drop the duplicate dashboard pool entry so exactly one seeded device_code entry exists. - Use the shared device_code source name for consistency with the nous/codex device-code providers. - Desktop: remove the loopback OAuth flow states and dead type variants; pkce providers' sign-in URL selection is unchanged. - Docs (EN + zh-Hans) rewritten for device-code login; drop the deleted --manual-paste flag from documented commands.
57 lines
1.5 KiB
Python
57 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
import json
|
|
import time
|
|
|
|
from hermes_cli import auth
|
|
|
|
|
|
def _jwt_with_exp(exp: int) -> str:
|
|
header = (
|
|
base64.urlsafe_b64encode(json.dumps({"alg": "none"}).encode())
|
|
.decode()
|
|
.rstrip("=")
|
|
)
|
|
payload = (
|
|
base64.urlsafe_b64encode(json.dumps({"exp": exp}).encode())
|
|
.decode()
|
|
.rstrip("=")
|
|
)
|
|
return f"{header}.{payload}.sig"
|
|
|
|
|
|
def test_xai_oauth_refresh_skew_is_one_hour() -> None:
|
|
assert auth.XAI_ACCESS_TOKEN_REFRESH_SKEW_SECONDS == 3600
|
|
|
|
|
|
def test_xai_oauth_token_expiring_uses_one_hour_skew() -> None:
|
|
token = _jwt_with_exp(int(time.time()) + 30 * 60)
|
|
|
|
assert auth._xai_access_token_is_expiring(
|
|
token,
|
|
auth.XAI_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
|
)
|
|
|
|
|
|
def test_xai_oauth_token_not_expiring_beyond_one_hour_skew() -> None:
|
|
token = _jwt_with_exp(int(time.time()) + 90 * 60)
|
|
|
|
assert not auth._xai_access_token_is_expiring(
|
|
token,
|
|
auth.XAI_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
|
|
)
|
|
|
|
|
|
def test_xai_proactive_refresh_skew_short_lived_token() -> None:
|
|
token = _jwt_with_exp(int(time.time()) + 15 * 60)
|
|
skew = auth._xai_proactive_refresh_skew_seconds(token)
|
|
|
|
assert skew == 120
|
|
assert not auth._xai_access_token_is_expiring(token, skew)
|
|
|
|
|
|
def test_xai_proactive_refresh_skew_long_lived_token() -> None:
|
|
token = _jwt_with_exp(int(time.time()) + 5 * 60 * 60)
|
|
|
|
assert auth._xai_proactive_refresh_skew_seconds(token) == auth.XAI_ACCESS_TOKEN_REFRESH_SKEW_SECONDS
|