mirror of
https://github.com/openai/codex.git
synced 2026-11-08 08:37:30 +00:00
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:
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));
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in new issue
Block a user