Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/cortex-cli/src/cli/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,15 +530,15 @@ fn install_completions(shell: Shell) -> Result<()> {

/// Show current logged-in user.
pub async fn run_whoami() -> Result<()> {
use cortex_login::{AuthMode, load_auth_with_fallback, safe_format_key};
use cortex_login::{AuthMode, has_non_blank_token, load_auth_with_fallback, safe_format_key};

let cortex_home = dirs::home_dir()
.map(|h| h.join(".cortex"))
.unwrap_or_else(|| std::path::PathBuf::from(".cortex"));

// Check environment variables first
if let Ok(token) = std::env::var("CORTEX_AUTH_TOKEN")
&& !token.is_empty()
&& has_non_blank_token(&token)
{
println!(
"Authenticated via CORTEX_AUTH_TOKEN: {}",
Expand All @@ -548,7 +548,7 @@ pub async fn run_whoami() -> Result<()> {
}

if let Ok(token) = std::env::var("CORTEX_API_KEY")
&& !token.is_empty()
&& has_non_blank_token(&token)
{
println!(
"Authenticated via CORTEX_API_KEY: {}",
Expand Down
8 changes: 4 additions & 4 deletions src/cortex-cli/src/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use crate::styled_output::{print_dim, print_error, print_info, print_success, print_warning};
use cortex_common::CliConfigOverrides;
use cortex_login::{
AuthMode, CredentialsStoreMode, SecureAuthData, load_auth_with_fallback, logout_with_fallback,
safe_format_key, save_auth_with_fallback,
AuthMode, CredentialsStoreMode, SecureAuthData, has_non_blank_token, load_auth_with_fallback,
logout_with_fallback, safe_format_key, save_auth_with_fallback,
};
use std::collections::HashSet;
use std::io::{IsTerminal, Read};
Expand Down Expand Up @@ -132,7 +132,7 @@ pub async fn run_login_status(config_overrides: CliConfigOverrides) -> ! {

// Check environment variables first (CORTEX_AUTH_TOKEN and CORTEX_API_KEY)
if let Ok(token) = std::env::var("CORTEX_AUTH_TOKEN")
&& !token.is_empty()
&& has_non_blank_token(&token)
{
print_success(&format!(
"Authenticated via CORTEX_AUTH_TOKEN environment variable: {}",
Expand All @@ -142,7 +142,7 @@ pub async fn run_login_status(config_overrides: CliConfigOverrides) -> ! {
}

if let Ok(token) = std::env::var("CORTEX_API_KEY")
&& !token.is_empty()
&& has_non_blank_token(&token)
{
print_success(&format!(
"Authenticated via CORTEX_API_KEY environment variable: {}",
Expand Down
16 changes: 10 additions & 6 deletions src/cortex-engine/src/auth_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ use crate::error::{CortexError, Result};
pub fn get_auth_token(instance_token: Option<&str>) -> Result<String> {
// Priority 1: Instance token (if provided and non-empty)
if let Some(token) = instance_token {
if !token.is_empty() {
if cortex_login::has_non_blank_token(token) {
tracing::debug!(source = "instance", "Using auth token from client instance");
return Ok(token.to_string());
}
}

// Priority 2: CORTEX_AUTH_TOKEN environment variable
if let Ok(token) = std::env::var("CORTEX_AUTH_TOKEN") {
if !token.is_empty() {
if cortex_login::has_non_blank_token(&token) {
tracing::debug!(
source = "env_var",
"Using auth token from CORTEX_AUTH_TOKEN"
Expand All @@ -63,7 +63,7 @@ pub fn get_auth_token(instance_token: Option<&str>) -> Result<String> {

// Priority 3: CORTEX_API_KEY environment variable (alias for GitHub Actions workflow)
if let Ok(token) = std::env::var("CORTEX_API_KEY") {
if !token.is_empty() {
if cortex_login::has_non_blank_token(&token) {
tracing::debug!(source = "env_var", "Using auth token from CORTEX_API_KEY");
return Ok(token);
}
Expand Down Expand Up @@ -95,17 +95,20 @@ pub fn get_auth_token_optional(instance_token: Option<&str>) -> Option<String> {
/// Useful for fast availability checks in UI.
pub fn is_authenticated(instance_token: Option<&str>) -> bool {
// Check instance token
if instance_token.map_or(false, |t| !t.is_empty()) {
if instance_token.is_some_and(cortex_login::has_non_blank_token) {
return true;
}

// Check CORTEX_AUTH_TOKEN env var
if std::env::var("CORTEX_AUTH_TOKEN").map_or(false, |t| !t.is_empty()) {
if std::env::var("CORTEX_AUTH_TOKEN")
.is_ok_and(|token| cortex_login::has_non_blank_token(&token))
{
return true;
}

// Check CORTEX_API_KEY env var (alias)
if std::env::var("CORTEX_API_KEY").map_or(false, |t| !t.is_empty()) {
if std::env::var("CORTEX_API_KEY").is_ok_and(|token| cortex_login::has_non_blank_token(&token))
{
return true;
}

Expand Down Expand Up @@ -142,6 +145,7 @@ mod tests {
fn test_is_authenticated_with_instance() {
assert!(is_authenticated(Some("token")));
assert!(!is_authenticated(Some("")));
assert!(!is_authenticated(Some(" ")));
assert!(!is_authenticated(None));
}

Expand Down
6 changes: 4 additions & 2 deletions src/cortex-engine/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ pub trait ModelClient: Send + Sync {
fn get_auth_token() -> Result<String> {
// First check environment variable
if let Ok(token) = std::env::var("CORTEX_AUTH_TOKEN") {
return Ok(token);
if cortex_login::has_non_blank_token(&token) {
return Ok(token);
}
}

// Try to load from cortex-login keyring storage
Expand Down Expand Up @@ -78,7 +80,7 @@ pub fn create_client(
_base_url: Option<&str>,
) -> Result<Box<dyn ModelClient>> {
// Use provided api_key as auth token, or try to get from environment/keyring
let auth_token = if !api_key.is_empty() {
let auth_token = if cortex_login::has_non_blank_token(api_key) {
api_key.to_string()
} else {
get_auth_token()?
Expand Down
2 changes: 1 addition & 1 deletion src/cortex-login/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub use keyring::{delete_from_keyring, load_from_keyring, save_to_keyring};
pub use token::get_auth_token;

// Re-exports from utils
pub use utils::safe_format_key;
pub use utils::{has_non_blank_token, safe_format_key};

// Re-exports from device_code_auth
pub use device_code_auth::run_device_code_login;
Expand Down
3 changes: 2 additions & 1 deletion src/cortex-login/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::constants::{API_BASE_URL, USER_AGENT};
use crate::keyring::save_to_keyring;
use crate::storage::load_auth_with_fallback;
use crate::types::{AuthMode, SecureAuthData};
use crate::utils::has_non_blank_token;

/// Refresh an expired access token using the refresh token.
///
Expand Down Expand Up @@ -71,7 +72,7 @@ fn refresh_token_sync(refresh_token: &str) -> Option<SecureAuthData> {
pub fn get_auth_token() -> Option<String> {
// Check environment variable first (highest priority for CI/CD)
if let Ok(token) = std::env::var("CORTEX_API_KEY") {
if !token.is_empty() {
if has_non_blank_token(&token) {
tracing::debug!("Using API key from CORTEX_API_KEY environment variable");
return Some(token);
}
Expand Down
13 changes: 13 additions & 0 deletions src/cortex-login/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ pub fn safe_format_key(key: &str) -> String {
format!("{prefix}***{suffix}")
}

/// Return true when a token contains at least one non-whitespace character.
pub fn has_non_blank_token(token: &str) -> bool {
!token.trim().is_empty()
}

/// Set restrictive file permissions (0600 on Unix).
pub fn set_file_permissions(path: &Path) -> Result<()> {
#[cfg(unix)]
Expand Down Expand Up @@ -46,4 +51,12 @@ mod tests {
let key = "sk-proj-12345";
assert_eq!(safe_format_key(key), "***");
}

#[test]
fn test_has_non_blank_token() {
assert!(has_non_blank_token("ctx-token"));
assert!(has_non_blank_token(" ctx-token "));
assert!(!has_non_blank_token(""));
assert!(!has_non_blank_token(" \t\n"));
}
}