test remote MCP OAuth callback flow

This commit is contained in:
starr-openai
2026-05-22 11:26:56 +02:00
parent fe06f30547
commit 164790d2bb
2 changed files with 63 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ use axum::Router;
use codex_app_server_protocol::ListMcpServerStatusParams;
use codex_app_server_protocol::ListMcpServerStatusResponse;
use codex_app_server_protocol::McpAuthStatus;
use codex_app_server_protocol::McpServerOauthLoginCompletedNotification;
use codex_app_server_protocol::McpServerOauthLoginResponse;
use codex_app_server_protocol::McpServerStatusDetail;
use codex_app_server_protocol::RequestId;
@@ -448,6 +449,42 @@ client_id = "codex-app-server-test"
.authorization_url
.starts_with(&format!("{}/oauth/authorize?", remote_mcp.server_url()))
);
let browser_response = reqwest::Client::new()
.get(&response.authorization_url)
.send()
.await?;
assert_eq!(browser_response.status(), reqwest::StatusCode::OK);
let notification = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_notification_message("mcpServer/oauthLogin/completed"),
)
.await??;
let notification: McpServerOauthLoginCompletedNotification =
serde_json::from_value(notification.params.context("oauth completion params")?)?;
assert_eq!(
notification,
McpServerOauthLoginCompletedNotification {
name: "remote-oauth".to_string(),
success: true,
error: None,
}
);
let request_id = mcp
.send_list_mcp_server_status_request(ListMcpServerStatusParams {
cursor: None,
limit: None,
detail: Some(McpServerStatusDetail::ToolsAndAuthOnly),
})
.await?;
let response = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
)
.await??;
let response: ListMcpServerStatusResponse = to_response(response)?;
assert_eq!(response.data[0].auth_status, McpAuthStatus::OAuth);
Ok(())
}

View File

@@ -9,6 +9,7 @@ use std::time::Duration;
use axum::Router;
use axum::body::Body;
use axum::extract::Json;
use axum::extract::Query;
use axum::extract::State;
use axum::http::HeaderMap;
use axum::http::Method;
@@ -19,6 +20,7 @@ use axum::http::header::CONTENT_TYPE;
use axum::http::header::HOST;
use axum::middleware;
use axum::middleware::Next;
use axum::response::Redirect;
use axum::response::Response;
use axum::routing::get;
use axum::routing::post;
@@ -146,6 +148,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}),
)
.route("/oauth/authorize", get(fake_oauth_authorize))
.route("/oauth/token", post(fake_oauth_token))
.nest_service(
"/mcp",
StreamableHttpService::new(
@@ -172,6 +176,28 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
async fn fake_oauth_authorize(
Query(params): Query<HashMap<String, String>>,
) -> Result<Redirect, StatusCode> {
let redirect_uri = params.get("redirect_uri").ok_or(StatusCode::BAD_REQUEST)?;
let state = params.get("state").ok_or(StatusCode::BAD_REQUEST)?;
let mut callback_url =
reqwest::Url::parse(redirect_uri).map_err(|_| StatusCode::BAD_REQUEST)?;
callback_url
.query_pairs_mut()
.append_pair("code", "test-authorization-code")
.append_pair("state", state);
Ok(Redirect::temporary(callback_url.as_str()))
}
async fn fake_oauth_token() -> Json<serde_json::Value> {
Json(json!({
"access_token": "test-access-token",
"refresh_token": "test-refresh-token",
"token_type": "bearer",
}))
}
impl ServerHandler for TestToolServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {