Use runtime authentication for OpenAI MCP and app requests (#59749)

## What changed

- Select fresh authentication for each HTTP operation on eligible OpenAI MCP connections while preserving the connection's credential source and account network policy. Stop redirects when requests carry runtime assertions.
- Pass the owning thread's authentication and runtime context through app discovery, connector metadata, and trusted-access requests. Include the runtime authentication mode in MCP connection and tool-cache identities.
- Give resumed actors analytics clients bound to their own authentication context, and serialize stored-credential reloads with token refresh while preserving host-provided credentials.

## Testing

Add coverage for fresh request assertions, revoked account policies, redirect handling, resumed-actor isolation, and MCP uploads in dual-header and assertion-only modes.

GitOrigin-RevId: f06900a6db61cd6cf53c3aa8075afeec4d5706d9
This commit is contained in:
cooper-oai
2026-11-05 22:03:29 +00:00
committed by copyberry
parent e934edd68c
commit 219ab4043e
51 changed files with 1901 additions and 250 deletions
@@ -1113,6 +1113,7 @@ impl PluginRequestProcessor {
None => None,
};
let app_summaries = load_plugin_app_summaries(
&self.thread_manager,
&config,
auth.as_ref(),
&outcome.plugin.apps,
@@ -1219,6 +1220,7 @@ impl PluginRequestProcessor {
.map(plugin_app_category_by_id_from_value)
.unwrap_or_default();
let app_summaries = load_plugin_app_summaries(
&self.thread_manager,
&config,
auth.as_ref(),
&plugin_apps,
@@ -1738,37 +1740,42 @@ impl PluginRequestProcessor {
}
let is_chatgpt_auth = auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth);
let apps_needing_auth = if let Some(app_ids_needing_auth) =
installation.app_ids_needing_auth
{
if app_ids_needing_auth.is_empty()
|| !config.features.apps_enabled_for_auth(is_chatgpt_auth)
{
Vec::new()
} else {
let plugin_apps = app_ids_needing_auth
.into_iter()
.map(codex_plugin::AppConnectorId)
.collect::<Vec<_>>();
let app_category_by_id = remote_detail
.app_manifest
.as_ref()
.map(plugin_app_category_by_id_from_value)
.unwrap_or_default();
load_plugin_app_summaries(&config, auth.as_ref(), &plugin_apps, &app_category_by_id)
let apps_needing_auth =
if let Some(app_ids_needing_auth) = installation.app_ids_needing_auth {
if app_ids_needing_auth.is_empty()
|| !config.features.apps_enabled_for_auth(is_chatgpt_auth)
{
Vec::new()
} else {
let plugin_apps = app_ids_needing_auth
.into_iter()
.map(codex_plugin::AppConnectorId)
.collect::<Vec<_>>();
let app_category_by_id = remote_detail
.app_manifest
.as_ref()
.map(plugin_app_category_by_id_from_value)
.unwrap_or_default();
load_plugin_app_summaries(
&self.thread_manager,
&config,
auth.as_ref(),
&plugin_apps,
&app_category_by_id,
)
.await
}
} else {
let plugin_app_declarations =
load_plugin_apps(result.installed_path.as_path(), &result.plugin_id).await;
self.plugin_apps_needing_auth_for_install(
&config,
auth.as_ref(),
&result.plugin_id.as_key(),
&plugin_app_declarations,
)
.await
};
}
} else {
let plugin_app_declarations =
load_plugin_apps(result.installed_path.as_path(), &result.plugin_id).await;
self.plugin_apps_needing_auth_for_install(
&config,
auth.as_ref(),
&result.plugin_id.as_key(),
&plugin_app_declarations,
)
.await
};
Ok(PluginInstallResponse {
auth_policy: remote_detail.summary.auth_policy,
@@ -1839,7 +1846,13 @@ impl PluginRequestProcessor {
.collect();
let environment_manager = self.thread_manager.environment_manager();
let (app_summaries, accessible_connectors_result) = tokio::join!(
load_plugin_app_summaries(config, auth, &plugin_apps, &app_category_by_id),
load_plugin_app_summaries(
&self.thread_manager,
config,
auth,
&plugin_apps,
&app_category_by_id
),
connectors::list_accessible_connectors_from_mcp_tools_with_mcp_manager(
config,
/*force_refetch*/ true,
@@ -1856,8 +1869,7 @@ impl PluginRequestProcessor {
"failed to load accessible apps after plugin install: {err:#}"
);
(
connectors::list_cached_accessible_connectors_from_mcp_tools(config)
.await
connectors::list_cached_accessible_connectors_from_mcp_tools(config, auth)
.unwrap_or_default(),
false,
)
@@ -2159,6 +2171,7 @@ impl PluginRequestProcessor {
}
async fn load_plugin_app_summaries(
thread_manager: &ThreadManager,
config: &Config,
auth: Option<&CodexAuth>,
plugin_apps: &[codex_plugin::AppConnectorId],
@@ -2185,9 +2198,17 @@ async fn load_plugin_app_summaries(
)
.fresh_records(&app_ids, /*include_tools*/ false),
);
let runtime = thread_manager.runtime_auth_for_config(config);
for app_ids in app_ids.chunks(APP_READ_MAX_IDS) {
let runtime = match &runtime {
Ok(runtime) => runtime.as_ref(),
Err(error) => {
warn!("failed to initialize app metadata authentication: {error}");
break;
}
};
match connectors::read_connector_metadata(
config, auth, app_ids, /*include_tools*/ false,
config, auth, runtime, app_ids, /*include_tools*/ false,
)
.await
{