Add trusted enterprise MCP auth configuration (#44832)

## Why

Enterprise MCP authorization must remain controlled by host, user, or managed configuration. Project settings and plugin declarations must not redirect enterprise credentials or downgrade the selected authentication mode.

## What changed

- Add `auth = "ema_auth"`, shared `mcp_enterprise_managed_auth` IdP configuration, and per-plugin enterprise registration settings.
- Require enterprise registrations to come from one non-project configuration layer, preserve managed IdP precedence, and reject project overrides that change authorization or re-enable disabled enterprise servers.
- Add the disabled-by-default `use_xaa` feature flag and require non-project opt-in or a managed requirement.
- Reject plugin declarations that select `ema_auth`. Keep EMA connections unsupported and block ordinary MCP OAuth login and credential fallback for this mode.
- Extend the configuration schema and serialization for the new settings.

## Testing

Add coverage for IdP precedence, incomplete registrations, alternate credential validation, project-only opt-in, plugin self-declaration rejection, configuration round-tripping, and trusted-project attempts to downgrade enterprise auth to OAuth or ChatGPT auth.

GitOrigin-RevId: f193bdfda74208c98e496a23bf2916955b131a1c
This commit is contained in:
Nick Steele authored and copyberry committed 2026-09-11 14:25:56 +00:00
1 parent 7a6f469dcf
commit 654b0a77d0
28 files changed
+872 -31

No files matched your search

@@ -0,0 +1,67 @@
//! Exercises EMA registration ownership through real configuration layers.
use anyhow::Result;
use codex_config::LoaderOverrides;
use codex_config::McpServerAuth;
use codex_config::test_support::CloudConfigBundleFixture;
use codex_core::config::ConfigBuilder;
use codex_core::config::set_project_trust_level;
use codex_protocol::config_types::TrustLevel;
use pretty_assertions::assert_eq;
use tempfile::tempdir;
use test_case::test_case;
#[test_case("oauth"; "OAuth")]
#[test_case("chatgpt"; "ChatGPT")]
#[tokio::test]
async fn trusted_project_cannot_downgrade_enterprise_auth(project_auth: &str) -> Result<()> {
let home = tempdir()?;
let workspace = tempdir()?;
std::fs::create_dir_all(workspace.path().join(".git"))?;
std::fs::create_dir_all(workspace.path().join(".codex"))?;
set_project_trust_level(home.path(), workspace.path(), TrustLevel::Trusted)?;
let managed_config = r#"
[features]
use_xaa = true
[mcp_enterprise_managed_auth.idp]
issuer = "https://idp.example"
client_id = "idp-client"
[mcp_servers.enterprise]
url = "https://resource.example/mcp"
auth = "ema_auth"
scopes = ["files.read"]
oauth_resource = "https://resource.example/mcp"
[mcp_servers.enterprise.oauth]
client_id = "mcp-client"
"#;
let builder = ConfigBuilder::default()
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
.codex_home(home.path().to_path_buf())
.fallback_cwd(Some(workspace.path().to_path_buf()))
.cloud_config_bundle(
CloudConfigBundleFixture::enterprise_config(managed_config.to_string())
.add_enterprise_requirement(
"[mcp_servers.enterprise.identity]\nurl = \"https://resource.example/mcp\"\n",
)
.into_loader(),
);
assert_eq!(
builder.clone().build().await?.mcp_servers.get()["enterprise"].auth,
McpServerAuth::EmaAuth,
);
std::fs::write(
workspace.path().join(".codex/config.toml"),
format!("[mcp_servers.enterprise]\nauth = {project_auth:?}\n"),
)?;
let error = builder
.build()
.await
.expect_err("a trusted project must not downgrade EMA before MCP startup");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
let message = format!("{error:#}");
assert!(
message.ends_with("one non-project config layer"),
"{message}"
);
Ok(())
}
@@ -419,6 +419,7 @@ async fn skill_mcp_dependency_oauth_uses_configured_http_client() -> Result<()>
client_id: None,
callback_url: None,
callback_port: Some(skill_callback_port),
..Default::default()
})
);
assert!(!servers.contains_key(DENIED_SERVER_NAME));
+1
View File
@@ -100,6 +100,7 @@ mod json_result;
mod live_cli;
mod mcp_auth_elicitation;
mod mcp_auth_refresh;
mod mcp_ema_config;
mod mcp_extension_protocol;
mod mcp_optional_startup_grace;
#[cfg(unix)]