Skip to content

fix(auth): honor X-Forwarded-Proto when validating NIP-98 URLs - #2

Merged
delirehberi merged 1 commit into
masterfrom
claude/beautiful-goodall-xyf8b7
Oct 2, 2026
Merged

delirehberi merged 1 commit into
masterfrom
claude/beautiful-goodall-xyf8b7

Conversation

@delirehberi

Copy link
Copy Markdown
Member

Description

Behind a reverse proxy that terminates TLS (the bundled Caddy, Nginx Proxy Manager, Traefik, …) the app receives plain HTTP. @hono/node-server derives the request scheme only from socket.encrypted, so c.req.url is http://… while the dashboard signs the public https://… URL in the NIP-98 u tag. Every authenticated API call therefore fails with 401 NIP-98 URL mismatch, which shows up as: keypair stuck on "Generating...", no QR code, new connections never appearing.

Changes:

  • middleware/nip98.ts: new getExternalRequestUrl() takes the scheme from X-Forwarded-Proto (first value of a chain, http/https only). The host still comes from the Host header; X-Forwarded-Host is deliberately not trusted, so a token signed for another site can't be replayed here by spoofing a header.
  • Optional PUBLIC_URL env var pins the public origin for proxies that don't forward these headers.
  • 5 new tests in nip98_auth.test.ts (forwarded proto, proto chain, no headers → still 401, X-Forwarded-Host replay → 401, PUBLIC_URL).
  • Docs: README (custom reverse proxy note + env table), DEPLOY.md troubleshooting row for this exact symptom, .env.example / .env.dist.

Related Issues

Fixes #1

Domain Agent Checklist

  • @agent-arch: API router & SQLite storage compatibility verified.
  • @agent-nostr: NIP compliance & cryptographic signatures verified (exact-match u tag check unchanged; only the server-side expected URL is corrected).
  • @agent-ui: TailAdmin UI design & NIP-07 state verified. (No UI changes.)
  • @agent-devops: typecheck, lint (0 errors) and tests (43/43) pass locally.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WYVXxXnVonPB4UYazaLLRK


Generated by Claude Code

Behind a TLS-terminating reverse proxy (bundled Caddy, Nginx Proxy
Manager, ...) the app receives plain HTTP, and @hono/node-server derives
the request scheme only from the socket. The browser signs the public
https:// URL, so every authenticated dashboard call failed with
"NIP-98 URL mismatch": the keypair stayed on "Generating...", no QR code
rendered, and new connections never appeared.

The middleware now takes the scheme from X-Forwarded-Proto (host still
comes from the Host header, so a token signed for another site can't be
replayed by spoofing X-Forwarded-Host). An optional PUBLIC_URL env var
pins the origin for proxies that don't forward these headers.

Fixes #1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYVXxXnVonPB4UYazaLLRK
@delirehberi
delirehberi merged commit a4306e6 into master Oct 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No keypair/qr code generated

2 participants