From 8fcd819e6fce2b0f5cd511c663329b2cd8e307cc Mon Sep 17 00:00:00 2001 From: Elysia <1628615876@qq.com> Date: Sat, 28 Mar 2026 00:40:36 +0800 Subject: [PATCH 1/3] feat: add user:file_upload OAuth scope Align OAuth scopes with upstream Claude Code client which now includes the user:file_upload scope for file upload support. Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/internal/pkg/oauth/oauth.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/internal/pkg/oauth/oauth.go b/backend/internal/pkg/oauth/oauth.go index cfc91bee..fea17c7a 100644 --- a/backend/internal/pkg/oauth/oauth.go +++ b/backend/internal/pkg/oauth/oauth.go @@ -21,12 +21,12 @@ const ( // OAuth endpoints AuthorizeURL = "https://claude.ai/oauth/authorize" TokenURL = "https://platform.claude.com/v1/oauth/token" - RedirectURI = "https://platform.claude.com/oauth/code/callback" + RedirectURI = "" // Scopes - Browser URL (includes org:create_api_key for user authorization) - ScopeOAuth = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers" + ScopeOAuth = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload" // Scopes - Internal API call (org:create_api_key not supported in API) - ScopeAPI = "user:profile user:inference user:sessions:claude_code user:mcp_servers" + ScopeAPI = "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload" // Scopes - Setup token (inference only) ScopeInference = "user:inference" From 941c469ab909d5d6ef10d5f94f554452dae8337b Mon Sep 17 00:00:00 2001 From: Elysia <1628615876@qq.com> Date: Sat, 28 Mar 2026 00:47:31 +0800 Subject: [PATCH 2/3] fix: use standard PKCE code verifier generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace charset→base64url double-encoding with standard random bytes→base64url approach to match official client behavior and avoid risk control detection. Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/internal/pkg/oauth/oauth.go | 32 +++++++---------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/backend/internal/pkg/oauth/oauth.go b/backend/internal/pkg/oauth/oauth.go index fea17c7a..d3ea17e6 100644 --- a/backend/internal/pkg/oauth/oauth.go +++ b/backend/internal/pkg/oauth/oauth.go @@ -30,14 +30,12 @@ const ( // Scopes - Setup token (inference only) ScopeInference = "user:inference" - // Code Verifier character set (RFC 7636 compliant) - codeVerifierCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" - // Session TTL SessionTTL = 30 * time.Minute ) // OAuthSession stores OAuth flow state + type OAuthSession struct { State string `json:"state"` CodeVerifier string `json:"code_verifier"` @@ -147,30 +145,14 @@ func GenerateSessionID() (string, error) { return hex.EncodeToString(bytes), nil } -// GenerateCodeVerifier generates a PKCE code verifier using character set method +// GenerateCodeVerifier generates a PKCE code verifier (RFC 7636). +// Uses 32 random bytes → base64url-no-pad, producing a 43-char verifier. func GenerateCodeVerifier() (string, error) { - const targetLen = 32 - charsetLen := len(codeVerifierCharset) - limit := 256 - (256 % charsetLen) - - result := make([]byte, 0, targetLen) - randBuf := make([]byte, targetLen*2) - - for len(result) < targetLen { - if _, err := rand.Read(randBuf); err != nil { - return "", err - } - for _, b := range randBuf { - if int(b) < limit { - result = append(result, codeVerifierCharset[int(b)%charsetLen]) - if len(result) >= targetLen { - break - } - } - } + bytes, err := GenerateRandomBytes(32) + if err != nil { + return "", err } - - return base64URLEncode(result), nil + return base64URLEncode(bytes), nil } // GenerateCodeChallenge generates a PKCE code challenge using S256 method From 81ca4f12dd69ab312f11bc0408978ecf28ce6b5a Mon Sep 17 00:00:00 2001 From: Elysia <1628615876@qq.com> Date: Sat, 28 Mar 2026 00:55:55 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AF=E5=88=A0?= =?UTF-8?q?=E7=9A=84url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/pkg/oauth/oauth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/pkg/oauth/oauth.go b/backend/internal/pkg/oauth/oauth.go index d3ea17e6..c5ef3c6e 100644 --- a/backend/internal/pkg/oauth/oauth.go +++ b/backend/internal/pkg/oauth/oauth.go @@ -21,7 +21,7 @@ const ( // OAuth endpoints AuthorizeURL = "https://claude.ai/oauth/authorize" TokenURL = "https://platform.claude.com/v1/oauth/token" - RedirectURI = "" + RedirectURI = "https://platform.claude.com/oauth/code/callback" // Scopes - Browser URL (includes org:create_api_key for user authorization) ScopeOAuth = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload"