fix: OAuth token introspection "context canceled" on concurrent MCP connections

Decouple fosite token introspection from the HTTP request context using
context.WithoutCancel + 10s timeout. When claude.ai opens multiple
concurrent MCP connections and one disconnects, the token validation
for subsequent connections no longer fails with "context canceled".

Fixes Bug #6 from #bugs-synapbus.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Algis Dumbris
2026-03-16 17:59:23 +02:00
co-authored by Claude Opus 4.6
parent 18d8179061
commit 1dd332adf7
2 changed files with 13 additions and 5 deletions
+7 -3
View File
@@ -229,9 +229,13 @@ func RequiredAuthMiddlewareWithOAuth(service *AgentService, keyService *apikeys.
// resolveOAuthToken introspects an OAuth bearer token and extracts agent identity.
func resolveOAuthToken(ctx context.Context, provider fosite.OAuth2Provider, token string, service *AgentService) (agentName string, ownerID string, ok bool) {
// Use a fositeSession-compatible struct for introspection.
// We import the type indirectly through the fosite interface.
_, ar, err := provider.IntrospectToken(ctx, token, fosite.AccessToken, &oauthIntrospectSession{})
// Decouple from the HTTP request context so token introspection completes
// even if the client disconnects (fixes "context canceled" errors during
// concurrent MCP connections from claude.ai).
dbCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second)
defer cancel()
_, ar, err := provider.IntrospectToken(dbCtx, token, fosite.AccessToken, &oauthIntrospectSession{})
if err != nil {
return "", "", false
}
+6 -2
View File
@@ -5,6 +5,7 @@ import (
"log/slog"
"net/http"
"strings"
"time"
"github.com/ory/fosite"
)
@@ -107,8 +108,11 @@ func RequireBearer(provider fosite.OAuth2Provider, userStore UserStore) func(htt
token := parts[1]
_ = token
// Use fosite introspection
_, ar, err := provider.IntrospectToken(r.Context(), parts[1], fosite.AccessToken, new(fositeSession))
// Use fosite introspection — decouple from HTTP request context so
// token validation completes even if the client disconnects.
dbCtx, cancel := context.WithTimeout(context.WithoutCancel(r.Context()), 10*time.Second)
_, ar, err := provider.IntrospectToken(dbCtx, parts[1], fosite.AccessToken, new(fositeSession))
cancel()
if err != nil {
slog.Debug("bearer token validation failed", "error", err)
w.Header().Set("WWW-Authenticate", `Bearer error="invalid_token"`)