mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Add connector auth failure detection without an install URL (#44938)
## Why `connector_auth_failure_from_tool_result` requires an install URL to return an auth failure, even when the tool result contains valid auth failure metadata. ## What changed Export `is_connector_auth_failure_from_tool_result` from `codex-mcp` to detect auth failures independently of an install URL. Share metadata validation with the existing parser, requiring an error result, an explicit auth failure flag, and a nonempty connector ID that does not conflict with the metadata. ## Testing Add unit tests for detection without an install URL, rejection of missing or mismatched connector identities and non-auth errors or successful results, and detection for all three supported auth reasons. GitOrigin-RevId: 6e4e177f26d9a34c56874b0360bae63f6c89107e
This commit is contained in:
committed by
copyberry
parent
12e82f44f8
commit
2e572378f4
@@ -66,35 +66,10 @@ pub fn connector_auth_failure_from_tool_result(
|
||||
connector_name: Option<&str>,
|
||||
install_url: Option<String>,
|
||||
) -> Option<CodexAppsConnectorAuthFailure> {
|
||||
if result.is_error != Some(true) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let auth_failure = result
|
||||
.meta
|
||||
.as_ref()?
|
||||
.as_object()?
|
||||
.get(MCP_TOOL_CODEX_APPS_META_KEY)?
|
||||
.as_object()?
|
||||
.get(CONNECTOR_AUTH_FAILURE_META_KEY)?
|
||||
.as_object()?;
|
||||
if auth_failure
|
||||
.get(CONNECTOR_AUTH_FAILURE_IS_AUTH_FAILURE_KEY)
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
!= Some(true)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let connector_id = connector_id
|
||||
.map(str::trim)
|
||||
.filter(|connector_id| !connector_id.is_empty())?;
|
||||
if let Some(auth_failure_connector_id) =
|
||||
string_auth_failure_field(auth_failure, CONNECTOR_AUTH_FAILURE_CONNECTOR_ID_KEY)
|
||||
&& auth_failure_connector_id != connector_id
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let auth_failure = connector_auth_failure_metadata(result, connector_id)?;
|
||||
let connector_name = connector_name
|
||||
.map(str::trim)
|
||||
.filter(|name| !name.is_empty())
|
||||
@@ -121,6 +96,49 @@ pub fn connector_auth_failure_from_tool_result(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_connector_auth_failure_from_tool_result(
|
||||
result: &CallToolResult,
|
||||
connector_id: Option<&str>,
|
||||
) -> bool {
|
||||
connector_id
|
||||
.map(str::trim)
|
||||
.filter(|connector_id| !connector_id.is_empty())
|
||||
.is_some_and(|connector_id| connector_auth_failure_metadata(result, connector_id).is_some())
|
||||
}
|
||||
|
||||
fn connector_auth_failure_metadata<'a>(
|
||||
result: &'a CallToolResult,
|
||||
connector_id: &str,
|
||||
) -> Option<&'a serde_json::Map<String, serde_json::Value>> {
|
||||
if result.is_error != Some(true) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let auth_failure = result
|
||||
.meta
|
||||
.as_ref()?
|
||||
.as_object()?
|
||||
.get(MCP_TOOL_CODEX_APPS_META_KEY)?
|
||||
.as_object()?
|
||||
.get(CONNECTOR_AUTH_FAILURE_META_KEY)?
|
||||
.as_object()?;
|
||||
if auth_failure
|
||||
.get(CONNECTOR_AUTH_FAILURE_IS_AUTH_FAILURE_KEY)
|
||||
.and_then(serde_json::Value::as_bool)
|
||||
!= Some(true)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if let Some(auth_failure_connector_id) =
|
||||
string_auth_failure_field(auth_failure, CONNECTOR_AUTH_FAILURE_CONNECTOR_ID_KEY)
|
||||
&& auth_failure_connector_id != connector_id
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(auth_failure)
|
||||
}
|
||||
|
||||
pub fn build_auth_elicitation_plan(
|
||||
call_id: &str,
|
||||
result: &CallToolResult,
|
||||
@@ -293,6 +311,76 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_auth_failure_without_an_install_url() {
|
||||
let result = auth_failure_result();
|
||||
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(&result, Some("connector_calendar")),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
connector_auth_failure_from_tool_result(
|
||||
&result,
|
||||
Some("connector_calendar"),
|
||||
Some("Google Calendar"),
|
||||
/*install_url*/ None,
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_failure_detection_requires_trusted_connector_identity_and_auth_flag() {
|
||||
let result = auth_failure_result();
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(&result, /*connector_id*/ None),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(&result, Some("connector_drive")),
|
||||
false
|
||||
);
|
||||
|
||||
let mut ordinary_error = result.clone();
|
||||
ordinary_error.meta.as_mut().expect("auth metadata")[MCP_TOOL_CODEX_APPS_META_KEY]
|
||||
[CONNECTOR_AUTH_FAILURE_META_KEY][CONNECTOR_AUTH_FAILURE_IS_AUTH_FAILURE_KEY] =
|
||||
serde_json::Value::Bool(false);
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(&ordinary_error, Some("connector_calendar"),),
|
||||
false
|
||||
);
|
||||
|
||||
let mut successful_result = result;
|
||||
successful_result.is_error = Some(false);
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(
|
||||
&successful_result,
|
||||
Some("connector_calendar"),
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_each_supported_connector_auth_reason() {
|
||||
for auth_reason in [
|
||||
"missing_link",
|
||||
"oauth_upgrade_required",
|
||||
"reauthentication_required",
|
||||
] {
|
||||
let mut result = auth_failure_result();
|
||||
result.meta.as_mut().expect("auth metadata")[MCP_TOOL_CODEX_APPS_META_KEY]
|
||||
[CONNECTOR_AUTH_FAILURE_META_KEY][CONNECTOR_AUTH_FAILURE_AUTH_REASON_KEY] =
|
||||
serde_json::Value::String(auth_reason.to_string());
|
||||
|
||||
assert_eq!(
|
||||
is_connector_auth_failure_from_tool_result(&result, Some("connector_calendar")),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_url_elicitation_payload() {
|
||||
let auth_failure = connector_auth_failure_from_tool_result(
|
||||
|
||||
@@ -60,6 +60,7 @@ pub use auth_elicitation::auth_elicitation_id;
|
||||
pub use auth_elicitation::build_auth_elicitation;
|
||||
pub use auth_elicitation::build_auth_elicitation_plan;
|
||||
pub use auth_elicitation::connector_auth_failure_from_tool_result;
|
||||
pub use auth_elicitation::is_connector_auth_failure_from_tool_result;
|
||||
/// Backward-compatible name for the Codex Apps runtime context key builder.
|
||||
pub use codex_connectors::connector_runtime_context_key as codex_apps_tools_cache_key;
|
||||
pub use mcp::codex_apps_mcp_server_config;
|
||||
|
||||
Reference in New Issue
Block a user