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:
@@ -7,7 +7,7 @@ use tokio::sync::mpsc;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
use crate::auth::{GrokAuth, read_auth_json};
|
||||
use crate::auth::{KimiAuth, read_auth_json};
|
||||
|
||||
use super::watcher::ConfigChangeEvent;
|
||||
|
||||
@@ -15,7 +15,7 @@ use super::watcher::ConfigChangeEvent;
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigUpdate {
|
||||
/// New auth credentials from disk.
|
||||
Auth(Box<GrokAuth>),
|
||||
Auth(Box<KimiAuth>),
|
||||
/// Auth scope was removed (user logged out).
|
||||
AuthCleared,
|
||||
/// A **broadcast** MCP reload — applies to every active session
|
||||
@@ -535,14 +535,14 @@ fn extract_ui_fields(config: &toml::Value) -> (Option<String>, bool, Option<Stri
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::auth::GrokAuth;
|
||||
use crate::auth::KimiAuth;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn make_auth(key: &str) -> GrokAuth {
|
||||
GrokAuth {
|
||||
fn make_auth(key: &str) -> KimiAuth {
|
||||
KimiAuth {
|
||||
key: key.to_string(),
|
||||
email: Some("test@test.com".to_string()),
|
||||
..GrokAuth::test_default()
|
||||
..KimiAuth::test_default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +604,7 @@ mod tests {
|
||||
reloader.reload_auth().unwrap();
|
||||
let update = rx.try_recv().expect("should send Auth update");
|
||||
assert!(
|
||||
matches!(update, ConfigUpdate::Auth(a) if a.key == "new-key"), // a is Box<GrokAuth>, Deref coercion
|
||||
matches!(update, ConfigUpdate::Auth(a) if a.key == "new-key"), // a is Box<KimiAuth>, Deref coercion
|
||||
"should contain new key"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user