[codex] Pass auth mode to plugin manager (#27517)

## Summary
- Add auth mode state to `PluginsManager`.
- Sync the plugin manager auth mode when `ThreadManager` is created and
when account auth changes.
- Route plugin load outcomes through an auth-aware projection hook so
follow-up plugin filtering can stay inside `core-plugins`.

## Motivation
This prepares plugin capability loading to be configured by auth mode,
such as hiding or exposing app/MCP-backed plugin surfaces based on
whether the user is using ChatGPT auth or API-key auth, without leaking
those details outside the plugin manager.

## Tests
- `just fmt`
- `just test -p codex-core-plugins`
- `env -u CODEX_SANDBOX_NETWORK_DISABLED -u CODEX_SANDBOX just test -p
codex-core thread_manager::tests`
- `env -u CODEX_SANDBOX_NETWORK_DISABLED -u CODEX_SANDBOX just test -p
codex-app-server`
This commit is contained in:
xl-openai
2026-06-10 20:57:35 -07:00
committed by GitHub
Unverified
parent 4435ff2810
commit 856855914f
4 changed files with 64 additions and 5 deletions
@@ -158,6 +158,9 @@ impl AccountRequestProcessor {
pub(crate) fn clear_external_auth(&self) {
self.auth_manager.clear_external_auth();
self.thread_manager
.plugins_manager()
.set_auth_mode(self.auth_manager.get_api_auth_mode());
}
fn current_account_updated_notification(&self) -> AccountUpdatedNotification {
@@ -173,6 +176,10 @@ impl AccountRequestProcessor {
thread_manager: &Arc<ThreadManager>,
auth: Option<CodexAuth>,
) {
thread_manager
.plugins_manager()
.set_auth_mode(auth.as_ref().map(CodexAuth::api_auth_mode));
match config_manager
.load_latest_config(/*fallback_cwd*/ None)
.await
+39 -5
View File
@@ -48,6 +48,7 @@ use crate::store::PluginInstallResult as StorePluginInstallResult;
use crate::store::PluginStore;
use crate::store::PluginStoreError;
use codex_analytics::AnalyticsEventsClient;
use codex_app_server_protocol::AuthMode;
use codex_config::ConfigLayerStack;
use codex_config::clear_user_plugin;
use codex_config::set_user_plugin_enabled;
@@ -203,6 +204,13 @@ fn featured_plugin_ids_cache_key(
}
}
fn project_plugin_load_outcome_for_auth(
outcome: PluginLoadOutcome,
_auth_mode: Option<AuthMode>,
) -> PluginLoadOutcome {
outcome
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PluginInstallRequest {
pub plugin_name: String,
@@ -319,6 +327,7 @@ pub struct PluginsManager {
remote_installed_plugins_cache_refresh_state: RwLock<RemoteInstalledPluginsCacheRefreshState>,
global_remote_catalog_cache_refresh_state: RwLock<GlobalRemoteCatalogCacheRefreshState>,
restriction_product: Option<Product>,
auth_mode: RwLock<Option<AuthMode>>,
analytics_events_client: RwLock<Option<AnalyticsEventsClient>>,
}
@@ -375,10 +384,30 @@ impl PluginsManager {
GlobalRemoteCatalogCacheRefreshState::default(),
),
restriction_product,
auth_mode: RwLock::new(None),
analytics_events_client: RwLock::new(None),
}
}
pub fn set_auth_mode(&self, auth_mode: Option<AuthMode>) -> bool {
let mut stored_auth_mode = match self.auth_mode.write() {
Ok(auth_mode_guard) => auth_mode_guard,
Err(err) => err.into_inner(),
};
if *stored_auth_mode == auth_mode {
return false;
}
*stored_auth_mode = auth_mode;
true
}
pub fn auth_mode(&self) -> Option<AuthMode> {
match self.auth_mode.read() {
Ok(auth_mode_guard) => *auth_mode_guard,
Err(err) => *err.into_inner(),
}
}
pub fn set_analytics_events_client(&self, analytics_events_client: AnalyticsEventsClient) {
let mut stored_client = match self.analytics_events_client.write() {
Ok(client_guard) => client_guard,
@@ -417,7 +446,7 @@ impl PluginsManager {
remote_plugin_enabled: config.remote_plugin_enabled,
};
if !force_reload && let Some(outcome) = self.cached_enabled_outcome(&cache_key) {
return outcome;
return self.project_plugins_for_auth(outcome);
}
let Ok(_load_permit) = self.enabled_outcome_load_semaphore.acquire().await else {
@@ -425,7 +454,7 @@ impl PluginsManager {
return PluginLoadOutcome::default();
};
if !force_reload && let Some(outcome) = self.cached_enabled_outcome(&cache_key) {
return outcome;
return self.project_plugins_for_auth(outcome);
}
let cache_generation = self.enabled_outcome_cache_generation();
let outcome = load_plugins_from_layer_stack(
@@ -438,7 +467,11 @@ impl PluginsManager {
.await;
log_plugin_load_errors(&outcome);
self.cache_enabled_outcome_if_current(cache_generation, cache_key, outcome.clone());
outcome
self.project_plugins_for_auth(outcome)
}
fn project_plugins_for_auth(&self, outcome: PluginLoadOutcome) -> PluginLoadOutcome {
project_plugin_load_outcome_for_auth(outcome, self.auth_mode())
}
pub fn clear_cache(&self) {
@@ -468,14 +501,15 @@ impl PluginsManager {
if !config.plugins_enabled {
return PluginLoadOutcome::default();
}
load_plugins_from_layer_stack(
let outcome = load_plugins_from_layer_stack(
config_layer_stack,
self.remote_installed_plugin_configs(),
&self.store,
self.restriction_product,
config.remote_plugin_enabled,
)
.await
.await;
self.project_plugins_for_auth(outcome)
}
/// Resolve plugin hooks for a config layer stack without loading other plugin capabilities.
@@ -17,6 +17,7 @@ use crate::test_support::load_plugins_config as load_plugins_config_input;
use crate::test_support::write_curated_plugin_sha_with as write_curated_plugin_sha;
use crate::test_support::write_file;
use crate::test_support::write_openai_curated_marketplace;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ConfigLayerSource;
use codex_config::AppToolApproval;
use codex_config::CONFIG_TOML_FILE;
@@ -47,6 +48,21 @@ use wiremock::matchers::query_param;
const MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN: usize = 1024;
#[test]
fn plugins_manager_tracks_auth_mode() {
let tmp = TempDir::new().unwrap();
let manager = PluginsManager::new(tmp.path().to_path_buf());
assert_eq!(manager.auth_mode(), None);
assert!(manager.set_auth_mode(Some(AuthMode::ApiKey)));
assert_eq!(manager.auth_mode(), Some(AuthMode::ApiKey));
assert!(!manager.set_auth_mode(Some(AuthMode::ApiKey)));
assert!(manager.set_auth_mode(Some(AuthMode::ChatgptAuthTokens)));
assert_eq!(manager.auth_mode(), Some(AuthMode::ChatgptAuthTokens));
assert!(manager.set_auth_mode(/*auth_mode*/ None));
assert_eq!(manager.auth_mode(), None);
}
fn write_plugin_with_version(
root: &Path,
dir_name: &str,
+2
View File
@@ -272,6 +272,7 @@ impl ThreadManager {
codex_home.to_path_buf(),
restriction_product,
));
plugins_manager.set_auth_mode(auth_manager.get_api_auth_mode());
let mcp_manager = Arc::new(McpManager::new_with_extensions(
Arc::clone(&plugins_manager),
Arc::clone(&extensions),
@@ -365,6 +366,7 @@ impl ThreadManager {
codex_home.clone(),
restriction_product,
));
plugins_manager.set_auth_mode(auth_manager.get_api_auth_mode());
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
let skills_manager = Arc::new(SkillsManager::new_with_restriction_product(
skills_codex_home,