Skip to content

Commit e47c52d

Browse files
committed
fix(auth): load OAuth tokens from keyring for API clients
Fixes bounty issue #1546 After successful OAuth device flow login, the access token is saved to the system keyring by cortex-login, but the API clients (BackendClient, CortexClient) were only checking environment variables for auth tokens. This fix: - Adds get_auth_token() and has_valid_auth() functions to cortex-login that retrieve OAuth tokens from the system keyring - Updates BackendClient::new() to fall back to keyring if env vars are not set - Updates CortexClient::new() to load OAuth tokens from keyring - Adds cortex-login as a dependency to cortex-engine Now when a user completes OAuth device authorization, the token is automatically used for API requests without requiring additional configuration.
1 parent 5579873 commit e47c52d

File tree

7 files changed

+88
-3
lines changed

7 files changed

+88
-3
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cortex-engine/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ cortex-common = { workspace = true }
2020
cortex-mcp-types = { path = "../cortex-mcp-types" }
2121
cortex-utils-git = { path = "../cortex-utils/git" }
2222
cortex-keyring-store = { path = "../cortex-keyring-store", features = ["serde"] }
23+
cortex-login = { path = "../cortex-login" }
2324

2425
# New feature crates (Phase 1)
2526
cortex-lsp = { path = "../cortex-lsp" }

cortex-engine/src/client/backend.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,16 @@ impl BackendClient {
3737
.build()
3838
.unwrap_or_else(|_| Client::new());
3939

40-
// Get auth token from environment
40+
// Get auth token with priority:
41+
// 1. Environment variables (cortex_AUTH_TOKEN, cortex_API_KEY)
42+
// 2. System keyring (OAuth tokens from device flow login)
4143
let auth_token = std::env::var("cortex_AUTH_TOKEN")
4244
.or_else(|_| std::env::var("cortex_API_KEY"))
43-
.ok();
45+
.ok()
46+
.or_else(|| {
47+
// Fall back to keyring for OAuth tokens
48+
cortex_login::get_auth_token()
49+
});
4450

4551
Self {
4652
client,

cortex-engine/src/client/cortex.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ impl CortexClient {
6363
.build()
6464
.unwrap_or_else(|_| Client::new());
6565

66+
// Try to load OAuth token from keyring (device flow login)
67+
let auth_token = cortex_login::get_auth_token();
68+
6669
Self {
6770
client,
6871
base_url: base_url.unwrap_or_else(|| {
@@ -76,7 +79,7 @@ impl CortexClient {
7679
context_window: 200_000,
7780
max_output_tokens: Some(8192),
7881
},
79-
auth_token: None,
82+
auth_token,
8083
api_key: None,
8184
expected_price_version: None,
8285
}

cortex-engine/src/client/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pub fn create_client(
9494
model.to_string(),
9595
base_url.map(std::string::ToString::to_string),
9696
);
97+
// CortexClient::new() already loads auth from keyring,
98+
// but if an explicit api_key is provided, use that instead
9799
if !api_key.is_empty() {
98100
client = client.with_api_key(api_key.to_string());
99101
}

cortex-login/src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,77 @@ pub fn safe_format_key(key: &str) -> String {
718718
format!("{prefix}***{suffix}")
719719
}
720720

721+
/// Get the current authentication token from keyring storage.
722+
///
723+
/// This function attempts to load the auth token from the system keyring.
724+
/// It returns the access token for OAuth auth or the API key for API key auth.
725+
/// If the token is expired, it will return None.
726+
///
727+
/// # Returns
728+
/// * `Some(String)` - The authentication token if available and valid
729+
/// * `None` - If no token is stored or the token is expired
730+
///
731+
/// # Example
732+
/// ```ignore
733+
/// if let Some(token) = cortex_login::get_auth_token() {
734+
/// // Use the token for API requests
735+
/// client.with_auth_token(token);
736+
/// }
737+
/// ```
738+
pub fn get_auth_token() -> Option<String> {
739+
match load_from_keyring() {
740+
Ok(Some(auth_data)) => {
741+
// Check if token is expired
742+
if auth_data.is_expired() {
743+
tracing::debug!("Auth token is expired");
744+
return None;
745+
}
746+
// Return the appropriate token based on auth mode
747+
auth_data.get_token().map(|s| s.to_string())
748+
}
749+
Ok(None) => {
750+
tracing::debug!("No auth data found in keyring");
751+
None
752+
}
753+
Err(e) => {
754+
tracing::warn!("Failed to load auth from keyring: {}", e);
755+
None
756+
}
757+
}
758+
}
759+
760+
/// Check if valid authentication is available in keyring storage.
761+
///
762+
/// This function checks if there is a valid (non-expired) auth token
763+
/// stored in the system keyring without actually retrieving the token value.
764+
///
765+
/// # Returns
766+
/// * `true` - If a valid authentication token exists
767+
/// * `false` - If no token exists or the token is expired
768+
///
769+
/// # Example
770+
/// ```ignore
771+
/// if cortex_login::has_valid_auth() {
772+
/// println!("User is authenticated");
773+
/// } else {
774+
/// println!("Please run 'cortex login' to authenticate");
775+
/// }
776+
/// ```
777+
pub fn has_valid_auth() -> bool {
778+
match load_from_keyring() {
779+
Ok(Some(auth_data)) => {
780+
// Check if token exists and is not expired
781+
if auth_data.is_expired() {
782+
return false;
783+
}
784+
// Check if we actually have a token
785+
auth_data.get_token().is_some()
786+
}
787+
Ok(None) => false,
788+
Err(_) => false,
789+
}
790+
}
791+
721792
/// Migrate from legacy storage to secure storage.
722793
pub fn migrate_to_secure_storage(cortex_home: &Path) -> Result<bool> {
723794
// Try to load from legacy file

target

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/root/cortex-cli/target

0 commit comments

Comments
 (0)