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
104 changes: 90 additions & 14 deletions src/providers/codex/auth/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,42 @@ impl<S: AuthStorage<StoredAuth>> CodexAuthManager<S> {
}

pub fn get_auth(&self) -> Result<StoredAuth, anyhow::Error> {
let cached = {
let guard = self.cached.lock().map_err(|e| anyhow::anyhow!("{e}"))?;
guard.clone()
};
let stored = match cached {
Some(ref auth) => auth.clone(),
None => {
let loaded = self.store.load_auth()?;
match loaded {
Some(auth) => {
let mut guard = self.cached.lock().map_err(|e| anyhow::anyhow!("{e}"))?;
let stored = if self.store.reload_each_request() {
let loaded = self.store.load_auth()?;
match loaded {
Some(auth) => {
let mut guard = self.cached.lock().map_err(|e| anyhow::anyhow!("{e}"))?;
if guard.as_ref() != Some(&auth) {
*guard = Some(auth.clone());
auth
}
None => {
anyhow::bail!("Not authenticated. Run: claude-code-proxy codex auth login");
auth
}
None => {
self.reset_cache();
anyhow::bail!("Not authenticated. Run: claude-code-proxy codex auth login");
}
}
} else {
let cached = {
let guard = self.cached.lock().map_err(|e| anyhow::anyhow!("{e}"))?;
guard.clone()
};
match cached {
Some(ref auth) => auth.clone(),
None => {
let loaded = self.store.load_auth()?;
match loaded {
Some(auth) => {
let mut guard =
self.cached.lock().map_err(|e| anyhow::anyhow!("{e}"))?;
*guard = Some(auth.clone());
auth
}
None => {
anyhow::bail!(
"Not authenticated. Run: claude-code-proxy codex auth login"
);
}
}
}
}
Expand Down Expand Up @@ -197,4 +217,60 @@ mod tests {
.contains("Not authenticated")
);
}

#[test]
fn reloading_store_observes_profile_swap_on_next_request() {
let store = CodexTokenStore::new_reloading(InMemoryAuthStore::new());
store
.save_auth(StoredAuth {
access: "first_access".into(),
refresh: String::new(),
expires: 9999999999999,
account_id: Some("acct_1".into()),
})
.unwrap();
let manager = CodexAuthManager::new(store);

assert_eq!(manager.get_auth().unwrap().access, "first_access");
manager
.store
.save_auth(StoredAuth {
access: "second_access".into(),
refresh: String::new(),
expires: 9999999999999,
account_id: Some("acct_2".into()),
})
.unwrap();

let swapped = manager.get_auth().unwrap();
assert_eq!(swapped.access, "second_access");
assert_eq!(swapped.account_id.as_deref(), Some("acct_2"));
}

#[test]
fn normal_store_keeps_cached_auth() {
let store = test_store();
store
.save_auth(StoredAuth {
access: "first_access".into(),
refresh: "first_refresh".into(),
expires: 9999999999999,
account_id: Some("acct_1".into()),
})
.unwrap();
let manager = CodexAuthManager::new(store);

assert_eq!(manager.get_auth().unwrap().access, "first_access");
manager
.store
.save_auth(StoredAuth {
access: "second_access".into(),
refresh: "second_refresh".into(),
expires: 9999999999999,
account_id: Some("acct_2".into()),
})
.unwrap();

assert_eq!(manager.get_auth().unwrap().access, "first_access");
}
}
17 changes: 16 additions & 1 deletion src/providers/codex/auth/token_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,31 @@ pub struct StoredAuth {
pub struct CodexTokenStore<S: AuthStorage<StoredAuth>> {
store: S,
codex_cli_fallback: bool,
reload_each_request: bool,
}

impl<S: AuthStorage<StoredAuth>> CodexTokenStore<S> {
pub fn new(store: S) -> Self {
Self {
store,
codex_cli_fallback: false,
reload_each_request: false,
}
}

pub fn new_with_codex_cli_fallback(store: S) -> Self {
Self {
store,
codex_cli_fallback: true,
reload_each_request: false,
}
}

pub fn new_reloading(store: S) -> Self {
Self {
store,
codex_cli_fallback: false,
reload_each_request: true,
}
}

Expand Down Expand Up @@ -74,6 +85,10 @@ impl<S: AuthStorage<StoredAuth>> CodexTokenStore<S> {
pub fn auth_path(&self) -> String {
self.store.path()
}

pub fn reload_each_request(&self) -> bool {
self.reload_each_request
}
}

pub type DefaultCodexAuthStore = KeychainFileAuthStore<StoredAuth, SystemKeychain>;
Expand All @@ -92,7 +107,7 @@ pub fn file_store() -> CodexTokenStore<DefaultCodexAuthStore> {
if std::env::var_os("CCP_CONFIG_DIR").is_none() {
CodexTokenStore::new_with_codex_cli_fallback(store)
} else {
CodexTokenStore::new(store)
CodexTokenStore::new_reloading(store)
}
}

Expand Down
Loading