From 03d11509fa889ba17c7a2f97cc98d4aa3890a7b1 Mon Sep 17 00:00:00 2001 From: Artin <101245159+Artin0123@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:38:42 +0800 Subject: [PATCH] Reduce OAuth scope to avoid Windows credential size limit (#58541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I encountered an issue where I couldn't sign in to ChatGPT Subscription on Windows: ``` ChatGPT subscription sign-in failed to persist credentials: Failed to write credentials to Windows Credential Manager: 占位程序接收到错误数据。 (0x800706F7) ``` Interestingly, only one of my three ChatGPT accounts had this problem — the other two signed in successfully. ## What I Found After investigating, I noticed that the OAuth scope in `openai_subscribed.rs` requests 6 permissions: ``` openid profile email offline_access api.connectors.read api.connectors.invoke ``` I wrote a test script to measure token sizes and found that: - With 6 scopes: my problematic account's token was **2578 bytes** - With 4 scopes (removing `api.connectors.read` and `api.connectors.invoke`): the same token was **2516 bytes** Since Windows Credential Manager has a 2560-byte limit (`CRED_MAX_CREDENTIAL_BLOB_SIZE`), this could explain why some accounts fail while others succeed — it depends on the base token size. ## My Hypothesis The issue seems to be that: - Error code `0x800706F7` = `RPC_X_BAD_STUB_DATA` (size limit, not format) - Accounts with larger base tokens exceed the limit when the extra 48 chars are added - Accounts with smaller base tokens still fit, which is why this isn't universally reproducible I'm not 100% certain this is the root cause, but the evidence seems to point in this direction. ## Proposed Fix I removed `api.connectors.read` and `api.connectors.invoke` from the OAuth scope, since: - These scopes don't appear to be used by Zed's current ChatGPT integration - OpenCode (another tool using the same OAuth provider) successfully uses only 4 scopes - This fix allowed my problematic account to sign in However, I'd like to ask the maintainers: - Are there plans to use OpenAI Connectors API in the future? - Are there security or compliance reasons for requesting these extra scopes? If the answer is yes to either, we'd need a different approach (e.g., splitting credentials, using encrypted file storage, or checking token size before storage). ## Testing - [x] Verified my problematic account now signs in successfully - [x] Verified my other accounts still work - [x] Ran `cargo check -p language_models` (compilation successful) - [ ] Would appreciate help testing on macOS/Linux ## Questions 1. Does this analysis make sense? 2. Are there any other considerations I'm missing? 3. Would it be helpful to add a size check with a clearer error message for future cases? I'm happy to adjust this approach based on your feedback. Release Notes: - Fixed ChatGPT Subscription sign-in failing on Windows for some accounts by removing unused OAuth scopes (`api.connectors.read`, `api.connectors.invoke`) that pushed JWT tokens over Windows Credential Manager's limit. --------- Co-authored-by: Smit Barmase --- crates/gpui_windows/src/platform.rs | 9 +++++++++ .../language_models/src/provider/openai_subscribed.rs | 10 ++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/gpui_windows/src/platform.rs b/crates/gpui_windows/src/platform.rs index 871da413e3..9ba94563a4 100644 --- a/crates/gpui_windows/src/platform.rs +++ b/crates/gpui_windows/src/platform.rs @@ -741,6 +741,15 @@ impl Platform for WindowsPlatform { } fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { + // CredWriteW rejects larger blobs with the opaque RPC error + // 0x800706F7 "The stub received bad data", so fail with a clear + // message instead. + if password.len() > CRED_MAX_CREDENTIAL_BLOB_SIZE as usize { + return Task::ready(Err(anyhow!( + "credential for {url} is {} bytes, which exceeds the Windows Credential Manager limit of {CRED_MAX_CREDENTIAL_BLOB_SIZE} bytes", + password.len() + ))); + } let password = password.to_vec(); let mut username = username.encode_utf16().chain(Some(0)).collect_vec(); let mut target_name = windows_credentials_target_name(url) diff --git a/crates/language_models/src/provider/openai_subscribed.rs b/crates/language_models/src/provider/openai_subscribed.rs index 20038e6123..e3ec95859a 100644 --- a/crates/language_models/src/provider/openai_subscribed.rs +++ b/crates/language_models/src/provider/openai_subscribed.rs @@ -760,10 +760,12 @@ async fn do_oauth_flow( .query_pairs_mut() .append_pair("client_id", CLIENT_ID) .append_pair("redirect_uri", &redirect_uri) - .append_pair( - "scope", - "openid profile email offline_access api.connectors.read api.connectors.invoke", - ) + // Deliberately excludes `api.connectors.read api.connectors.invoke` + // (which Codex CLI requests): extra scopes inflate the + // access-token JWT, and the serialized credentials must fit within + // Windows Credential Manager's 2560-byte blob limit + // (CRED_MAX_CREDENTIAL_BLOB_SIZE). See #58541. + .append_pair("scope", "openid profile email offline_access") .append_pair("response_type", "code") .append_pair("code_challenge", &challenge) .append_pair("code_challenge_method", "S256")