Add oauth_resource handling for MCP login flows (#12866)

Addresses bug https://github.com/openai/codex/issues/12589

Builds on community PR #12763.

This adds `oauth_resource` support for MCP `streamable_http` servers and
wires it through the relevant config and login paths. It fixes the bug
where the configured OAuth resource was not reliably included in the
authorization request, causing MCP login to omit the expected
`resource` parameter.
This commit is contained in:
Eric Traut authored and GitHub committed 2026-02-26 20:10:12 -08:00
1 parent 6fe3dc2e22
commit cee009d117
14 files changed
+201 -1

No files matched your search

+60
View File
@@ -2433,6 +2433,7 @@ mod tests {
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
}
}
@@ -2452,6 +2453,7 @@ mod tests {
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
}
}
@@ -3489,6 +3491,7 @@ profile = "project"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
);
@@ -3646,6 +3649,7 @@ bearer_token = "secret"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -3717,6 +3721,7 @@ ZIG_VAR = "3"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -3768,6 +3773,7 @@ ZIG_VAR = "3"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -3817,6 +3823,7 @@ ZIG_VAR = "3"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -3882,6 +3889,7 @@ startup_timeout_sec = 2.0
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
apply_blocking(
@@ -3959,6 +3967,7 @@ X-Auth = "DOCS_AUTH"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -3989,6 +3998,7 @@ X-Auth = "DOCS_AUTH"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
);
apply_blocking(
@@ -4057,6 +4067,7 @@ url = "https://example.com/mcp"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
),
(
@@ -4077,6 +4088,7 @@ url = "https://example.com/mcp"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
),
]);
@@ -4160,6 +4172,7 @@ url = "https://example.com/mcp"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -4205,6 +4218,7 @@ url = "https://example.com/mcp"
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: None,
},
)]);
@@ -4250,6 +4264,7 @@ url = "https://example.com/mcp"
enabled_tools: Some(vec!["allowed".to_string()]),
disabled_tools: Some(vec!["blocked".to_string()]),
scopes: None,
oauth_resource: None,
},
)]);
@@ -4278,6 +4293,51 @@ url = "https://example.com/mcp"
Ok(())
}
#[tokio::test]
async fn replace_mcp_servers_streamable_http_serializes_oauth_resource() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
let servers = BTreeMap::from([(
"docs".to_string(),
McpServerConfig {
transport: McpServerTransportConfig::StreamableHttp {
url: "https://example.com/mcp".to_string(),
bearer_token_env_var: None,
http_headers: None,
env_http_headers: None,
},
enabled: true,
required: false,
disabled_reason: None,
startup_timeout_sec: None,
tool_timeout_sec: None,
enabled_tools: None,
disabled_tools: None,
scopes: None,
oauth_resource: Some("https://resource.example.com".to_string()),
},
)]);
apply_blocking(
codex_home.path(),
None,
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
)?;
let config_path = codex_home.path().join(CONFIG_TOML_FILE);
let serialized = std::fs::read_to_string(&config_path)?;
assert!(serialized.contains(r#"oauth_resource = "https://resource.example.com""#));
let loaded = load_global_mcp_servers(codex_home.path()).await?;
let docs = loaded.get("docs").expect("docs entry");
assert_eq!(
docs.oauth_resource.as_deref(),
Some("https://resource.example.com")
);
Ok(())
}
#[tokio::test]
async fn set_model_updates_defaults() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;