mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
Support confidential OAuth clients for enterprise-managed MCP auth (#56001)
## Why Enterprise-managed MCP authentication previously required public OAuth clients at both the identity provider and the MCP authorization server, preventing use of registrations that require client secrets. ## What changed - Accept separate `client_secret` and `token_endpoint_auth_method` settings for the enterprise IdP and MCP server OAuth registration. Support `client_secret_basic` and `client_secret_post`, defaulting to Basic when a secret is configured. - Validate the registered method against server metadata and use it for enterprise login and both ID-JAG exchanges. - Require a configured `oauth.authorization_server_issuer` for confidential MCP registrations and reject empty secrets or authentication methods without secrets. - Require the account OAuth client to match the configured IdP client before reusing an account grant with a provisioned secret; otherwise use dedicated enterprise login. ## Testing Extend configuration, login, exchange, and app-server lifecycle tests to cover confidential registrations, separate IdP and resource credentials, authentication method validation, and exclusion of client secrets from authorization URLs, stored login grants, and logs. GitOrigin-RevId: b3be378c2b1bb67e6a3652c648e72fd2e21e956d
This commit is contained in:
@@ -2492,6 +2492,14 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"McpEmaTokenEndpointAuthMethod": {
|
||||
"description": "Token endpoint authentication selected by a confidential EMA client registration.",
|
||||
"enum": [
|
||||
"client_secret_basic",
|
||||
"client_secret_post"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"McpEnterpriseManagedAuthConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -2561,12 +2569,24 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"description": "Public OAuth client registered with that enterprise IdP.",
|
||||
"description": "OAuth client registered with that enterprise IdP.",
|
||||
"type": "string"
|
||||
},
|
||||
"client_secret": {
|
||||
"description": "Provisioned client secret for confidential IdP login and token exchange. Omitted for a public client.",
|
||||
"type": "string"
|
||||
},
|
||||
"issuer": {
|
||||
"description": "Issuer used for enterprise OAuth discovery and identity validation.",
|
||||
"type": "string"
|
||||
},
|
||||
"token_endpoint_auth_method": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpEmaTokenEndpointAuthMethod"
|
||||
}
|
||||
],
|
||||
"description": "Registered EMA client authentication method. Defaults to client_secret_basic when a secret is configured; requires a client secret."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -2600,6 +2620,14 @@
|
||||
"client_secret": {
|
||||
"description": "OAuth client secret used for token exchange with a pre-registered client.",
|
||||
"type": "string"
|
||||
},
|
||||
"token_endpoint_auth_method": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpEmaTokenEndpointAuthMethod"
|
||||
}
|
||||
],
|
||||
"description": "Registered EMA client authentication method. Defaults to client_secret_basic when a secret is configured; requires a client secret."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
||||
@@ -169,6 +169,9 @@ fn serialize_mcp_server_table(config: &McpServerConfig) -> anyhow::Result<TomlTa
|
||||
if let Some(client_secret) = &oauth.client_secret {
|
||||
oauth_table["client_secret"] = value(client_secret.as_str());
|
||||
}
|
||||
if let Some(method) = oauth.token_endpoint_auth_method {
|
||||
oauth_table["token_endpoint_auth_method"] = value(method.as_str());
|
||||
}
|
||||
if let Some(callback_url) = &oauth.callback_url {
|
||||
oauth_table["callback_url"] = value(callback_url.clone());
|
||||
}
|
||||
|
||||
@@ -1054,11 +1054,20 @@ fn blocking_replace_mcp_servers_round_trips() {
|
||||
let codex_home = tmp.path();
|
||||
|
||||
let mut servers = BTreeMap::new();
|
||||
servers.insert("ema".to_string(), serde_json::from_value(serde_json::json!({
|
||||
"url": "https://ema.example/mcp", "auth": "ema_auth", "scopes": ["tools"],
|
||||
"oauth_resource": "https://ema.example",
|
||||
"oauth": {"client_id": "resource-client", "authorization_server_issuer": "https://as.example"}
|
||||
})).expect("EMA server"));
|
||||
servers.insert(
|
||||
"ema".to_string(),
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"url": "https://ema.example/mcp", "auth": "ema_auth", "scopes": ["tools"],
|
||||
"oauth_resource": "https://ema.example",
|
||||
"oauth": {
|
||||
"client_id": "resource-client",
|
||||
"client_secret": "test-ema-secret",
|
||||
"token_endpoint_auth_method": "client_secret_post",
|
||||
"authorization_server_issuer": "https://as.example"
|
||||
}
|
||||
}))
|
||||
.expect("EMA server"),
|
||||
);
|
||||
servers.insert(
|
||||
"stdio".to_string(),
|
||||
McpServerConfig {
|
||||
@@ -1154,6 +1163,8 @@ oauth_resource = \"https://ema.example\"
|
||||
|
||||
[mcp_servers.ema.oauth]
|
||||
client_id = \"resource-client\"
|
||||
client_secret = \"test-ema-secret\"
|
||||
token_endpoint_auth_method = \"client_secret_post\"
|
||||
authorization_server_issuer = \"https://as.example\"
|
||||
|
||||
[mcp_servers.http]
|
||||
|
||||
Reference in New Issue
Block a user