fix(x402): default 402 challenge resource.url to https on public hosts#751
Open
bussyjd wants to merge 1 commit into
Open
fix(x402): default 402 challenge resource.url to https on public hosts#751bussyjd wants to merge 1 commit into
bussyjd wants to merge 1 commit into
Conversation
buildResourceURL keyed the scheme off X-Forwarded-Proto alone, so behind a TLS-terminating tunnel (edge https -> plaintext to Traefik -> verifier) any route without an explicit X-Forwarded-Proto:https RequestHeaderModifier produced http:// resource URLs in 402 challenges. The controller's host-bound so-<name>-host routes carry that filter but the shared-origin so-<name> routes do not — and being more path-specific for /services/<name>, they win the match even on dedicated-hostname origins, so challenges on those origins advertised http:// resources on an https endpoint (strict v2 payment clients and discovery crawlers see a scheme mismatch; observed live on 5 of 8 host-bound offers, #679). resolveSiteURL already solved exactly this for 402-page links: default https, downgrade only for hosts the stack serves locally over plain HTTP (obol.stack, loopback, *.localhost/*.local), explicit signals always win. Extract that resolution into resolveScheme and use it in both places, so challenge resource URLs and page links can never disagree again. Refs #679 Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
What changed:
buildResourceURL(the 402 challengeresource.urlbuilder in the verifier's ForwardAuth path) now uses the same scheme resolution asresolveSiteURL: default https, downgrade to http only for hosts the stack serves locally over plain HTTP (obol.stack, loopback,*.localhost/*.local); an explicit https signal (direct TLS orX-Forwarded-Proto: https) still wins for any host. The resolution is extracted into a sharedresolveSchemeso challenge URLs and 402-page links can never disagree again.Why it matters: behind a TLS-terminating tunnel the ForwardAuth hop sees plaintext, so the scheme keyed off
X-Forwarded-Protoalone rendershttp://resource URLs whenever a route lacks theX-Forwarded-Proto: httpsRequestHeaderModifier. The controller's host-boundso-<name>-hostroutes carry that filter, but the shared-originso-<name>routes do not — and being more path-specific for/services/<name>, they win the match even on dedicated-hostname origins. Observed live: 5 of 8 host-bound offers advertisedhttp://resources in their 402 challenges on https endpoints — a scheme mismatch visible to strict x402 v2 clients and discovery crawlers.This is the verifier-side half of the correctness gap discussed in #679. It deliberately does NOT force
X-Forwarded-Proto: httpson shared-origin routes: those also serve genuine local plaintext traffic onobol.stack, where claiming https would be wrong in the other direction.Risk level: low
Base branch: main
Scope
Validation
Unit tests:
Live evidence (v0.13.0 deployment behind a Cloudflare tunnel): all 8 host-bound offers'
/services/<name>challenges advertisedhttp://orhttps://depending solely on whether a hand-authored route with the header filter happened to match; with trusted forwarded headers (the operator-side workaround) all emit https and a real x402 payment settled end-to-end ($0.01 USDC on Base, exact ledger delta). This patch makes the correct scheme the default with no per-route filters or Traefik trust configuration required.Refs #679
https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk