Files
codex/codex-rs/core/tests/suite/mod.rs
stevenlee-oai 555aa79d5a [connectors] Refresh codex_apps /ps/mcp auth (#31486)
[Codex Thread
019f2408-dc59-79f2-b245-4c11debd1a61](https://codex-thread-link.openai.chatgpt-team.site/thread/019f2408-dc59-79f2-b245-4c11debd1a61)

## Why

Long-lived Codex sessions can outlive the ChatGPT bearer token that was
present when the MCP runtime started.

The Responses path already recovers from token expiration by refreshing
or reloading the shared `AuthManager`. The reserved `codex_apps`
hosted-plugin client did not observe that update: `McpConnectionManager`
built its `/ps/mcp` HTTP auth once from a `CodexAuth` snapshot, and
`auth_provider_from_auth` copied that snapshot bearer into a static
`BearerAuthProvider`.

After the copied bearer expired, `/ps/mcp` kept sending it even though
Responses had a newer token in the same `AuthManager`. The failure
occurred before downstream connector execution, so unrelated apps such
as Gmail, Slack, and Google Calendar could all fail with the same
transport-level `401 token_expired`.

This replaces
[openai/codex#29474](https://github.com/openai/codex/pull/29474), which
was closed for inactivity without being merged. A new long-lived-session
report reproduced the same simultaneous `/ps/mcp` expiry pattern across
unrelated apps.

## What changed

- Add an `AuthManager`-backed request-header provider in
`codex-model-provider`. It keeps an `Arc<AuthManager>` and reads
`auth_cached()` for each outbound request, so the next `/ps/mcp` call
sees a token refreshed by the existing Responses/auth-recovery flow.
- Scope that provider to the startup account, ChatGPT user, and
workspace identity. Same-identity token reloads are followed; an account
switch emits no ambient auth until account-scoped MCP state is rebuilt.
- Have `McpConnectionManager` construct the dynamic provider only for
the reserved `codex_apps` registration used by the hosted-plugin
`/ps/mcp` path.

| MCP path | Auth behavior after this change |
| --- | --- |
| Reserved `codex_apps` hosted-plugin `/ps/mcp` | Read current
same-identity auth from the shared `AuthManager` per request |
| `codex_apps` with `CODEX_CONNECTORS_TOKEN` | Keep the environment
bearer-token override |
| User-configured/direct MCP registrations | Keep their existing
configured auth path |

## Non-goals

- No plugin-service changes.
- No downstream Slack, Gmail, Calendar, or other connector
OAuth/link-refresh changes.
- No auth UI changes.
- No behavior change for user-configured/direct MCP registrations.
- No new `/ps/mcp`-initiated token refresh; this makes `/ps/mcp` observe
refreshes already performed through the shared `AuthManager`.

## Tests

- `just test -p codex-model-provider`
- Covers same-identity token reloads and refuses a changed startup
identity.
- `just test -p codex-mcp`
- `just test -p codex-core mcp_auth_refresh`
- Creates the reserved hosted-plugin `codex_apps` `/ps/mcp` client
before the shared `AuthManager` changes, updates that same manager
through its public external-auth path, performs a real `tools/call`, and
asserts the request uses the current bearer.
2026-07-08 21:46:11 -04:00

145 lines
3.6 KiB
Rust

// Aggregates all former standalone integration tests as modules.
use codex_apply_patch::CODEX_CORE_APPLY_PATCH_ARG1;
use codex_exec_server::CODEX_FS_HELPER_ARG1;
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
use codex_test_binary_support::TestBinaryDispatchGuard;
use codex_test_binary_support::TestBinaryDispatchMode;
use codex_test_binary_support::configure_test_binary_dispatch;
use ctor::ctor;
// This code runs before any other tests are run.
// It allows the test binary to behave like codex and dispatch to apply_patch and codex-linux-sandbox
// based on the arg0.
// NOTE: this doesn't work on ARM
#[ctor]
pub static CODEX_ALIASES_TEMP_DIR: Option<TestBinaryDispatchGuard> = {
configure_test_binary_dispatch("codex-core-tests", |exe_name, argv1| {
if argv1 == Some(CODEX_CORE_APPLY_PATCH_ARG1) {
return TestBinaryDispatchMode::DispatchArg0Only;
}
if argv1 == Some(CODEX_FS_HELPER_ARG1) {
return TestBinaryDispatchMode::DispatchArg0Only;
}
if exe_name == CODEX_LINUX_SANDBOX_ARG0 {
return TestBinaryDispatchMode::DispatchArg0Only;
}
TestBinaryDispatchMode::InstallAliases
})
};
#[cfg(not(target_os = "windows"))]
mod abort_tasks;
mod additional_context;
mod agent_execution;
mod agent_jobs;
mod agent_websocket;
mod agents_md;
mod apply_patch_cli;
#[cfg(not(target_os = "windows"))]
mod approvals;
mod auto_review;
mod cli_stream;
mod client;
mod client_websockets;
mod code_mode;
mod code_mode_elicitation;
mod codex_delegate;
mod collaboration_instructions;
mod compact;
mod compact_remote;
mod compact_remote_parity;
mod compact_resume_fork;
mod current_time_reminder;
mod deprecation_notice;
mod exec;
mod exec_policy;
#[cfg(not(target_os = "windows"))]
mod extension_sandbox;
mod external_auth;
mod fork_thread;
#[cfg(not(target_os = "windows"))]
mod guardian_review;
#[cfg(not(target_os = "windows"))]
mod hooks;
#[cfg(not(target_os = "windows"))]
mod hooks_mcp;
mod image_rollout;
mod items;
mod json_result;
mod live_cli;
mod mcp_auth_elicitation;
mod mcp_auth_refresh;
#[cfg(unix)]
mod mcp_refresh_cleanup;
mod mcp_tool_exposure;
mod mcp_turn_metadata;
mod model_overrides;
mod model_runtime_selectors;
mod model_switching;
mod model_visible_layout;
mod models_cache_ttl;
mod models_etag_responses;
mod multi_agent_mode;
mod network_approval;
mod openai_file_mcp;
mod otel;
mod override_updates;
mod pending_input;
mod permissions_messages;
mod personality;
mod personality_migration;
mod plugins;
mod prompt_caching;
mod prompt_debug_tests;
mod quota_exceeded;
mod realtime_conversation;
mod remote_env;
mod remote_models;
mod request_compression;
#[cfg(not(target_os = "windows"))]
mod request_permissions;
#[cfg(not(target_os = "windows"))]
mod request_permissions_tool;
mod request_plugin_install;
mod request_user_input;
mod responses_api_proxy_headers;
mod responses_lite;
mod resume;
mod resume_warning;
mod review;
mod rmcp_client;
mod rollout_budget;
mod rollout_list_find;
mod safety_buffering;
mod safety_check_downgrade;
mod search_tool;
mod shell_command;
mod shell_serialization;
mod shell_snapshot;
mod skill_approval;
mod skills;
mod spawn_agent_description;
mod sqlite_state;
mod stream_error_allows_next_turn;
mod stream_no_completed;
mod subagent_notifications;
mod token_budget;
mod tool_harness;
mod tool_parallelism;
mod tools;
mod truncation;
mod turn_state;
mod unified_exec;
mod unified_exec_process_events;
#[cfg(unix)]
mod unified_exec_zsh_fork_approvals;
mod unstable_features_warning;
mod user_notification;
mod user_shell_cmd;
mod view_image;
mod web_search;
mod websocket_fallback;
mod window_headers;
#[cfg(target_os = "windows")]
mod windows_sandbox;