Skip to content

Feature request: a static (pre-shared) Bearer token validator for local servers, without OAuth resource metadata #284

Description

@ianegordon

Summary

BearerTokenValidator (0.12.1) can validate an opaque token through its tokenValidator closure, but it requires resourceMetadataURL and resourceIdentifier, and every 401 it emits advertises resource_metadata="…". That makes it OAuth-oriented: a locally-run server that wants the spec's "Require an authorization token" mitigation (security best practices, §Local MCP Server Compromise) with a manually configured secret has to either fabricate metadata URLs or write its own validator.

A request for a small second validator, alongside BearerTokenValidator, that checks a pre-shared token and emits plain RFC 6750 challenges without OAuth discovery parameters.

Motivation

The expected deployment is one macOS user, one app hosting an MCP endpoint on loopback, and one or more client programs (Claude Code, Codex, …) on the same machine. Those clients already send Authorization: Bearer <token> from a static configuration (claude mcp add --transport http … --header "Authorization: Bearer …", codex mcp add … --bearer-token-env-var …), so a static token rides the path clients already implement, and an embedder can later swap in BearerTokenValidator without changing what clients send.

Proposed contract

  • init(secret: String, realm: String = "mcp") and a store-backed form (e.g. init(secrets: @Sendable () -> Set<String>, realm:)) so an app can issue one token per client and revoke individually; the closure is consulted once per request.
  • Parsing per RFC 9110 §11.1 / RFC 6750 §2.1: case-insensitive scheme, 1*SP, strict b64token grammar, byte-exact comparison.
  • Responses per RFC 6750 §3.1: no credentials or a foreign scheme → 401 with WWW-Authenticate: Bearer realm="…" (no error code); well-formed mismatch → 401 with error="invalid_token"; malformed credentials → 400 with error="invalid_request". Bodies never disclose the secret.
  • Constant-time comparison of the presented token against every configured secret (e.g. HMAC both under a per-instance key and compare the MACs), with no early exit across secrets.
  • Placement documented as after OriginValidator and before SessionValidator, so an unauthenticated initialize draws 401 rather than a session error.
  • Explicitly not in scope: token generation/persistence, expiry, rate limiting, OAuth discovery.

Reference implementation

An implementation with this contract, unit tests (36 cases) and live-pipeline integration tests is in a downstream package: https://github.com/ianegordon/Misconception (Sources/MisconceptionMCP/StaticBearerTokenValidator.swift, commit 1e285db). Happy to adapt it into a pull request here if the maintainers would take it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions