From 65406937718275b5355c2a8cbaae40479406e041 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 25 Feb 2026 19:04:14 +0000 Subject: [PATCH] fix: parse Authorization header case-insensitively and support token format (#137) ## Summary - Make auth header parsing RFC 7235 compliant by comparing the scheme case-insensitively (`bearer`, `BEARER`, etc. all work now) - Add support for Gitea-style `token ` format in addition to `Bearer ` Fixes https://gitea.com/gitea/gitea-mcp/issues/59 --- *This PR was authored by Claude.* --------- Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/137 Reviewed-by: Lunny Xiao Co-authored-by: silverwind Co-committed-by: silverwind --- operation/operation.go | 28 ++++++++++++---------- operation/operation_test.go | 48 +++++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/operation/operation.go b/operation/operation.go index e893d2b..8864101 100644 --- a/operation/operation.go +++ b/operation/operation.go @@ -67,20 +67,24 @@ func RegisterTool(s *server.MCPServer) { s.DeleteTools("") } -// parseBearerToken extracts the Bearer token from an Authorization header. +// parseAuthToken extracts the token from an Authorization header. +// Supports "Bearer " (case-insensitive per RFC 7235) and +// Gitea-style "token " formats. // Returns the token and true if valid, empty string and false otherwise. -func parseBearerToken(authHeader string) (string, bool) { - const bearerPrefix = "Bearer " - if len(authHeader) < len(bearerPrefix) || !strings.HasPrefix(authHeader, bearerPrefix) { - return "", false +func parseAuthToken(authHeader string) (string, bool) { + if len(authHeader) > 7 && strings.EqualFold(authHeader[:7], "Bearer ") { + token := strings.TrimSpace(authHeader[7:]) + if token != "" { + return token, true + } } - - token := strings.TrimSpace(authHeader[len(bearerPrefix):]) - if token == "" { - return "", false + if len(authHeader) > 6 && strings.EqualFold(authHeader[:6], "token ") { + token := strings.TrimSpace(authHeader[6:]) + if token != "" { + return token, true + } } - - return token, true + return "", false } func getContextWithToken(ctx context.Context, r *http.Request) context.Context { @@ -89,7 +93,7 @@ func getContextWithToken(ctx context.Context, r *http.Request) context.Context { return ctx } - token, ok := parseBearerToken(authHeader) + token, ok := parseAuthToken(authHeader) if !ok { return ctx } diff --git a/operation/operation_test.go b/operation/operation_test.go index 758ae04..55c642b 100644 --- a/operation/operation_test.go +++ b/operation/operation_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestParseBearerToken(t *testing.T) { +func TestParseAuthToken(t *testing.T) { tests := []struct { name string header string @@ -12,23 +12,29 @@ func TestParseBearerToken(t *testing.T) { wantOK bool }{ { - name: "valid token", + name: "valid Bearer token", header: "Bearer validtoken", wantToken: "validtoken", wantOK: true, }, + { + name: "lowercase bearer", + header: "bearer lowercase", + wantToken: "lowercase", + wantOK: true, + }, + { + name: "uppercase BEARER", + header: "BEARER uppercase", + wantToken: "uppercase", + wantOK: true, + }, { name: "token with spaces trimmed", header: "Bearer spacedToken ", wantToken: "spacedToken", wantOK: true, }, - { - name: "lowercase bearer should fail", - header: "bearer lowercase", - wantToken: "", - wantOK: false, - }, { name: "bearer with no token", header: "Bearer ", @@ -47,6 +53,24 @@ func TestParseBearerToken(t *testing.T) { wantToken: "", wantOK: false, }, + { + name: "Gitea token format", + header: "token giteaapitoken", + wantToken: "giteaapitoken", + wantOK: true, + }, + { + name: "Gitea Token format capitalized", + header: "Token giteaapitoken", + wantToken: "giteaapitoken", + wantOK: true, + }, + { + name: "token with no value", + header: "token ", + wantToken: "", + wantOK: false, + }, { name: "different auth type", header: "Basic dXNlcjpwYXNz", @@ -60,7 +84,7 @@ func TestParseBearerToken(t *testing.T) { wantOK: false, }, { - name: "token with internal spaces", + name: "bearer token with internal spaces", header: "Bearer token with spaces", wantToken: "token with spaces", wantOK: true, @@ -69,12 +93,12 @@ func TestParseBearerToken(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotToken, gotOK := parseBearerToken(tt.header) + gotToken, gotOK := parseAuthToken(tt.header) if gotToken != tt.wantToken { - t.Errorf("parseBearerToken() token = %q, want %q", gotToken, tt.wantToken) + t.Errorf("parseAuthToken() token = %q, want %q", gotToken, tt.wantToken) } if gotOK != tt.wantOK { - t.Errorf("parseBearerToken() ok = %v, want %v", gotOK, tt.wantOK) + t.Errorf("parseAuthToken() ok = %v, want %v", gotOK, tt.wantOK) } }) }