M1/F1: Kimi Code OAuth device-code flow
Replace the xAI OAuth stack with the Kimi device authorization grant:
- kimi_oauth.rs wire layer (device_authorization + token poll + refresh
against kigi_env::oauth_host(); client_id per PRD; retryable statuses
429/5xx with backoff; expired_token restarts authorization)
- X-Msh-Device-{Name,Model,Id} headers; device_id minted uuid4-hex at
~/.kigi/device_id (0600)
- Storage: system keyring service `kigi`, entry `oauth/kimi-code`
(macOS/Windows native backends), atomic-file fallback under ~/.kigi;
official client's keyring/~/.kimi never touched
- Refresh manager: 60s tick, threshold max(300, expires_in*0.5),
401-tombstone keyed by rejected refresh token with 300s cooldown and
rotation auto-clear, cross-process lock with sibling-adoption
triple-check, sleep/wake forced refresh
- Deleted xAI machinery: enterprise OIDC (PKCE/JWKS/teams), devbox login,
external auth provider, JWT tier gating + subscription paywall stack,
X-XAI-Token-Auth marker headers, ZDR gates, /user enrichment
- kigi login / TUI /login both drive the device flow; login-host display
now derives from kigi_env::oauth_host()
- 264 auth unit/wiremock tests; live contract probe of
auth.kimi.com/api/oauth/device_authorization matches the wire shapes
Gates: check/clippy --all-targets clean, fmt, deny ok, kigi-shell lib
5131 tests green.
This commit is contained in:
@@ -326,7 +326,7 @@ pub async fn connect_via_leader(
|
||||
// agent's disk-rotated token under the file lock (`try_adopt_disk_token`).
|
||||
let auth_manager = std::sync::Arc::new(kigi_shell::auth::AuthManager::new(
|
||||
&kigi_shell::util::kigi_home::kigi_home(),
|
||||
agent_config.grok_com_config.clone(),
|
||||
agent_config.kimi_code_config.clone(),
|
||||
));
|
||||
|
||||
Ok(AcpConnection {
|
||||
@@ -897,11 +897,7 @@ mod tests {
|
||||
// Realistic enterprise user: no cached session token, default `grok.com`
|
||||
// login (no enterprise OIDC).
|
||||
has_cached_token: false,
|
||||
has_enterprise_oidc: false,
|
||||
enterprise_oidc_issuer: None,
|
||||
login_label: None,
|
||||
has_auth_provider_command: false,
|
||||
preferred_method: None,
|
||||
});
|
||||
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&built.methods);
|
||||
|
||||
@@ -40,9 +40,9 @@ pub async fn spawn_grok_shell(
|
||||
) -> Result<SpawnedAgent> {
|
||||
let auth_manager = std::sync::Arc::new(AuthManager::new(
|
||||
&kigi_home(),
|
||||
agent_config.grok_com_config.clone(),
|
||||
agent_config.kimi_code_config.clone(),
|
||||
));
|
||||
auth_manager.configure_refresher(agent_config.grok_com_config.auth_provider_command.clone());
|
||||
auth_manager.configure_refresher();
|
||||
// Pause token refreshes across system sleep so an OIDC refresh can't
|
||||
// straddle a suspend (which can revoke the refresh token and force
|
||||
// re-login). No-op where the OS listener is unavailable.
|
||||
|
||||
Reference in New Issue
Block a user