@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
+
+package mcp
+
+import (
+ "crypto/subtle"
+ "net/http"
+ "strings"
+)
+
+func requireBearerToken(token string, next http.Handler) http.Handler {
+ if token == "" {
+ return next
+ }
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ scheme, value, ok := strings.Cut(r.Header.Get("Authorization"), " ")
+ if !ok || !strings.EqualFold(scheme, "Bearer") ||
+ subtle.ConstantTimeCompare([]byte(value), []byte(token)) != 1 {
+ w.Header().Set("WWW-Authenticate", "Bearer")
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
@@ -0,0 +1,112 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
+
+package mcp
+
+import (
+ "context"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+// server.SECURITY.3 server.SECURITY.6
+func TestHTTPBearerAuthACIDServerSecurity3And6AllowsMatchingBearerToken(t *testing.T) {
+ calls := 0
+ handler := requireBearerToken("secret-token", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ calls++
+ w.WriteHeader(http.StatusNoContent)
+ }))
+
+ recorder := httptest.NewRecorder()
+ request := httptest.NewRequestWithContext(context.Background(), http.MethodPost, "/mcp", nil)
+ request.Header.Set("Authorization", "Bearer secret-token")
+ handler.ServeHTTP(recorder, request)
+
+ if recorder.Code != http.StatusNoContent {
+ t.Fatalf("status = %d, want %d", recorder.Code, http.StatusNoContent)
+ }
+ if calls != 1 {
+ t.Fatalf("next calls = %d, want 1", calls)
+ }
+}
+
+// server.SECURITY.7
+func TestHTTPBearerAuthACIDServerSecurity7RejectsInvalidTokens(t *testing.T) {
+ tests := []struct {
+ name string
+ header string
+ }{
+ {name: "missing", header: ""},
+ {name: "wrong", header: "Bearer wrong-token"},
+ {name: "basic", header: "Basic secret-token"},
+ {name: "malformed", header: "Bearer"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ calls := 0
+ handler := requireBearerToken("secret-token", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
+ calls++
+ }))
+
+ recorder := httptest.NewRecorder()
+ request := httptest.NewRequestWithContext(context.Background(), http.MethodPost, "/mcp", nil)
+ if tt.header != "" {
+ request.Header.Set("Authorization", tt.header)
+ }
+ handler.ServeHTTP(recorder, request)
+
+ if recorder.Code != http.StatusUnauthorized {
+ t.Fatalf("status = %d, want %d", recorder.Code, http.StatusUnauthorized)
+ }
+ if got := recorder.Header().Get("WWW-Authenticate"); got != "Bearer" {
+ t.Fatalf("WWW-Authenticate = %q, want Bearer", got)
+ }
+ if calls != 0 {
+ t.Fatalf("next calls = %d, want 0", calls)
+ }
+ })
+ }
+}
+
+func TestHTTPBearerAuthAllowsRequestsWhenNoTokenConfigured(t *testing.T) {
+ calls := 0
+ handler := requireBearerToken("", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ calls++
+ w.WriteHeader(http.StatusNoContent)
+ }))
+
+ recorder := httptest.NewRecorder()
+ request := httptest.NewRequestWithContext(context.Background(), http.MethodPost, "/mcp", nil)
+ handler.ServeHTTP(recorder, request)
+
+ if recorder.Code != http.StatusNoContent {
+ t.Fatalf("status = %d, want %d", recorder.Code, http.StatusNoContent)
+ }
+ if calls != 1 {
+ t.Fatalf("next calls = %d, want 1", calls)
+ }
+}
+
+// server.SECURITY.2
+func TestHTTPBearerAuthACIDServerSecurity2DoesNotExposeTokensInUnauthorizedResponse(t *testing.T) {
+ handler := requireBearerToken("secret-token", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
+ t.Fatal("next handler was called")
+ }))
+
+ recorder := httptest.NewRecorder()
+ request := httptest.NewRequestWithContext(context.Background(), http.MethodPost, "/mcp", nil)
+ request.Header.Set("Authorization", "Bearer submitted-token")
+ handler.ServeHTTP(recorder, request)
+
+ body := recorder.Body.String()
+ if strings.Contains(body, "secret-token") {
+ t.Fatalf("response body exposed configured token: %q", body)
+ }
+ if strings.Contains(body, "submitted-token") {
+ t.Fatalf("response body exposed submitted token: %q", body)
+ }
+}