From cf0892e081d8b4d9904f178133d5df45b16e74a0 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 6 Apr 2026 14:16:03 -0400 Subject: [PATCH] Share styled OAuth callback page between providers Add http_client::oauth_callback_page() that generates a nicely styled HTML page with Zed branding for OAuth callback responses. Use it in both the ChatGPT subscription provider and the MCP context server OAuth flow, replacing the unstyled inline HTML in both places. The template is parameterized on title and message so both callers get consistent styling that updates in one place. --- crates/context_server/src/oauth.rs | 12 ++- crates/http_client/src/http_client.rs | 86 +++++++++++++++++++ .../src/provider/openai_subscribed.rs | 15 ++-- 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/crates/context_server/src/oauth.rs b/crates/context_server/src/oauth.rs index 1a314de2fca9b9987336decb15b208ffd7759dea..44bcbbd557a67f40cbcf7621c52ddbd8d818b9d4 100644 --- a/crates/context_server/src/oauth.rs +++ b/crates/context_server/src/oauth.rs @@ -1107,15 +1107,19 @@ pub async fn start_callback_server() -> Result<( let (status_code, body) = match &result { Ok(_) => ( 200, - "

Authorization successful

\ -

You can close this tab and return to Zed.

", + http_client::oauth_callback_page( + "Authorization Successful", + "You can close this tab and return to Zed.", + ), ), Err(err) => { log::error!("OAuth callback error: {}", err); ( 400, - "

Authorization failed

\ -

Something went wrong. Please try again from Zed.

", + http_client::oauth_callback_page( + "Authorization Failed", + "Something went wrong. Please try again from Zed.", + ), ) } }; diff --git a/crates/http_client/src/http_client.rs b/crates/http_client/src/http_client.rs index bbbe3b1a832332bd6bee693b4c0b916b4f4c182a..b1f097fdac62107f2ba7aa9d571a4688f775fee4 100644 --- a/crates/http_client/src/http_client.rs +++ b/crates/http_client/src/http_client.rs @@ -291,6 +291,92 @@ impl HttpClient for HttpClientWithUrl { } } +/// Generate a styled HTML page for OAuth callback responses. +/// +/// Returns a complete HTML document (no HTTP headers) with a centered card +/// layout styled to match Zed's dark theme. The `title` is rendered as a +/// heading and `message` as body text below it. +pub fn oauth_callback_page(title: &str, message: &str) -> String { + format!( + r#" + + + + +{title} — Zed + + + +
+
+ +
+

{title}

+

{message}

+
Zed
+
+ +"#, + title = title, + message = message, + ) +} + pub fn read_proxy_from_env() -> Option { const ENV_VARS: &[&str] = &[ "ALL_PROXY", diff --git a/crates/language_models/src/provider/openai_subscribed.rs b/crates/language_models/src/provider/openai_subscribed.rs index 8751d8ad32d2be8be8c4b50206e2458aa1d3133e..e8fb7f058710b98c384f8df31575d7f3fbd1bf08 100644 --- a/crates/language_models/src/provider/openai_subscribed.rs +++ b/crates/language_models/src/provider/openai_subscribed.rs @@ -684,11 +684,16 @@ async fn await_oauth_callback(expected_state: &str) -> Result { } } - let html = b"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n\ -

Signed in to Zed

\ -

Authentication successful. You can close this tab.

\ - "; - stream.write_all(html).await.log_err(); + let page = http_client::oauth_callback_page( + "Signed In", + "You've signed into Zed via your ChatGPT subscription. You can close this tab and return to Zed.", + ); + let response = format!( + "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\n\r\n{}", + page.len(), + page + ); + stream.write_all(response.as_bytes()).await.log_err(); let received_state = received_state.ok_or_else(|| anyhow!("Missing state in OAuth callback"))?;