Files
justin d5cd602d85
Build & push patched image / build (push) Failing after 1m13s
initial commit: wrapper Dockerfile + CI
The wrapper sed-patches the two host-wide OAuth 2.1 discovery route
registrations in MetaMCP's compiled backend bundle so spec-compliant
MCP clients do not force an interactive login flow on public endpoints.

See README for full rationale (RFC 9728, MCP Authorization spec
2026-03-26 revision) and the diagnostic evidence.

Guarded with grep pre/post-conditions so a future upstream release
that renames or restructures these routes fails the build loudly
rather than silently shipping an unpatched image.
2026-07-22 12:04:58 -04:00

62 lines
3.4 KiB
Docker

# Patch: hide RFC 9728 / RFC 8414 OAuth discovery on the MetaMCP host.
#
# Why: MetaMCP unconditionally publishes /.well-known/oauth-protected-resource
# and /.well-known/oauth-authorization-server for the WHOLE host, even when
# every configured endpoint is public. Spec-compliant MCP clients (claude.ai,
# Claude Desktop, per MCP Authorization spec 2026-03-26 + RFC 9728) probe
# those paths on connect, see "resource requires OAuth", and pop the
# MetaMCP/Authentik login before calling any tool — even a public one.
#
# The route mounts live in a compiled bundle with no env-var gate
# (app.use(oauth_default) at line 10560 of dist/index.js pulls the metadata
# router in unconditionally). Fix here is a surgical string swap on the
# path literals so Express falls through to its default 404 for those
# requests. All 4 references get swapped (2 route registrations + 2
# WWW-Authenticate/resource_metadata string interpolations) so the set of
# references is self-consistent. The WWW-Authenticate change is harmless
# because it only fires on 401 responses from ENFORCED endpoints, and our
# only enforced endpoint (OB1) is consumed with static bearers set in
# client config, never via OAuth discovery.
#
# The RUN block is guarded — it verifies the target strings exist and that
# the exact expected number of substitutions happens. A future upstream
# rewrite that renames or restructures these paths will FAIL THE BUILD
# LOUDLY rather than silently ship a container that still leaks OAuth
# discovery. When that happens, re-inspect the bundle and update this file.
#
# Delete this whole file the day upstream ships an env-var toggle to
# disable OAuth discovery (or per-endpoint metadata scoping per RFC 9728).
FROM ghcr.io/metatool-ai/metamcp:latest
USER root
RUN set -eux; \
F=/app/apps/backend/dist/index.js; \
test -f "$F"; \
# Sanity: both discovery route registrations must exist before we patch.
grep -Fq "\"/.well-known/oauth-protected-resource\"" "$F"; \
grep -Fq "\"/.well-known/oauth-authorization-server\"" "$F"; \
# Refuse to double-patch (idempotency guard).
if grep -Fq "/__jpaul_disabled_oauth" "$F"; then \
echo "already patched — aborting"; exit 1; \
fi; \
# Count occurrences to catch upstream drift (expect 2 of each today).
B1=$(grep -Fc "\"/.well-known/oauth-protected-resource\"" "$F"); \
B2=$(grep -Fc "\"/.well-known/oauth-authorization-server\"" "$F"); \
[ "$B1" = "1" ] || { echo "unexpected count $B1 for oauth-protected-resource; upstream changed"; exit 2; }; \
[ "$B2" = "1" ] || { echo "unexpected count $B2 for oauth-authorization-server; upstream changed"; exit 2; }; \
sed -i \
-e "s#\"/\\.well-known/oauth-protected-resource\"#\"/__jpaul_disabled_oauth_pr\"#g" \
-e "s#\"/\\.well-known/oauth-authorization-server\"#\"/__jpaul_disabled_oauth_as\"#g" \
"$F"; \
# Post-condition: no more references to the original discovery paths.
if grep -Fq "\"/.well-known/oauth-protected-resource\"" "$F"; then \
echo "patch failed: oauth-protected-resource still present"; exit 3; \
fi; \
if grep -Fq "\"/.well-known/oauth-authorization-server\"" "$F"; then \
echo "patch failed: oauth-authorization-server still present"; exit 3; \
fi; \
# Patched markers present.
grep -Fq "/__jpaul_disabled_oauth_pr" "$F"; \
grep -Fq "/__jpaul_disabled_oauth_as" "$F"; \
echo "OAuth discovery routes disabled — patch verified"