fix(auth): QR-login-URL uit x-forwarded-host i.p.v. interne bind-host #88

Merged
janpeter merged 1 commit from fix/qr-login-proxy-host into main 2026-06-15 12:18:51 +02:00
Owner

Probleem

QR-login opent op de telefoon een lege pagina. De start-route bouwde de scan-URL uit new URL(request.url).origin, wat achter de reverse proxy (Caddy/Vercel) de interne bind-host https://0.0.0.0:3000 is — onbereikbaar voor de telefoon die de QR scant.

{ "qrUrl": "https://0.0.0.0:3000/m/pair#id=…&s=…" }

Fix

Lees de publieke host uit de proxy-headers x-forwarded-host/x-forwarded-proto (die Caddy én Vercel zetten), met de request-origin als fallback voor lokale dev. Consistent met de bestaande x-forwarded-for-afhandeling in dezelfde route. Geen env-wijziging nodig; de QR volgt de host waar de gebruiker op zit (multi-host: scrum4me.com / .nl / thuis.jp-visser.nl).

- const origin = new URL(request.url).origin
+ const forwardedHost = request.headers.get('x-forwarded-host')
+ const forwardedProto = request.headers.get('x-forwarded-proto') ?? 'https'
+ const origin = forwardedHost
+   ? `${forwardedProto}://${forwardedHost}`
+   : new URL(request.url).origin
  const qrUrl = `${origin}/m/pair#id=${pairing.id}&s=${mobileSecret}`

Relatie tot DigiPlein

Port van DigiPlein #16. DigiPlein loste het op met APP_BASE_URL (single-host); scrum4me is multi-host en heeft die env-var niet, vandaar de proxy-header-aanpak. DigiPlein #17 (pairingId in claim-body) was hier niet van toepassing: scrum4me's claim-client stuurt pairingId al mee.

Verificatie

  • TDD-regressietest (__tests__/api/pair-start.test.ts): verzoek op 0.0.0.0:3000 met x-forwarded-host eist dat de QR de publieke host volgt. RED reproduceerde exact de 0.0.0.0-URL → nu GREEN.
  • npm run verify: typecheck schoon, 1489/1489 tests groen.
  • Reverse-proxy-valkuil gedocumenteerd in docs/patterns/qr-login.md.

🤖 Generated with Claude Code

## Probleem QR-login opent op de telefoon een lege pagina. De `start`-route bouwde de scan-URL uit `new URL(request.url).origin`, wat achter de reverse proxy (Caddy/Vercel) de interne bind-host `https://0.0.0.0:3000` is — onbereikbaar voor de telefoon die de QR scant. ```json { "qrUrl": "https://0.0.0.0:3000/m/pair#id=…&s=…" } ``` ## Fix Lees de publieke host uit de proxy-headers `x-forwarded-host`/`x-forwarded-proto` (die Caddy én Vercel zetten), met de request-origin als fallback voor lokale dev. Consistent met de bestaande `x-forwarded-for`-afhandeling in dezelfde route. Geen env-wijziging nodig; de QR volgt de host waar de gebruiker op zit (multi-host: scrum4me.com / .nl / thuis.jp-visser.nl). ```diff - const origin = new URL(request.url).origin + const forwardedHost = request.headers.get('x-forwarded-host') + const forwardedProto = request.headers.get('x-forwarded-proto') ?? 'https' + const origin = forwardedHost + ? `${forwardedProto}://${forwardedHost}` + : new URL(request.url).origin const qrUrl = `${origin}/m/pair#id=${pairing.id}&s=${mobileSecret}` ``` ## Relatie tot DigiPlein Port van DigiPlein #16. DigiPlein loste het op met `APP_BASE_URL` (single-host); scrum4me is multi-host en heeft die env-var niet, vandaar de proxy-header-aanpak. DigiPlein #17 (pairingId in claim-body) was hier **niet** van toepassing: scrum4me's claim-client stuurt `pairingId` al mee. ## Verificatie - TDD-regressietest (`__tests__/api/pair-start.test.ts`): verzoek op `0.0.0.0:3000` met `x-forwarded-host` eist dat de QR de publieke host volgt. RED reproduceerde exact de `0.0.0.0`-URL → nu GREEN. - `npm run verify`: typecheck schoon, **1489/1489 tests groen**. - Reverse-proxy-valkuil gedocumenteerd in `docs/patterns/qr-login.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(auth): QR-login-URL uit x-forwarded-host i.p.v. interne bind-host
All checks were successful
CI / Lint, Typecheck, Test & Build (pull_request) Successful in 4m53s
CI / Deploy Manual (workflow_dispatch) (pull_request) Has been skipped
CI / Detect deploy-relevant changes (pull_request) Has been skipped
CI / Deploy Preview (PR) (pull_request) Has been skipped
CI / Deploy Production (main) (pull_request) Has been skipped
90f3db00e1
De QR-start-route bouwde de scan-URL uit `new URL(request.url).origin`, wat achter de reverse proxy (Caddy/Vercel) de interne bind-host 0.0.0.0:3000 is — onbereikbaar voor de telefoon die de QR scant (symptoom: lege pagina op https://0.0.0.0:3000/m/pair). Lees nu de publieke host uit x-forwarded-host/-proto, met de request-origin als fallback voor lokale dev. Consistent met de bestaande x-forwarded-for-afhandeling in dezelfde route en QR volgt de host waar de gebruiker op zit (multi-host).

Port van DigiPlein #16. DigiPlein #17 (pairingId in claim-body) was hier niet van toepassing: scrum4me's claim-client stuurt pairingId al mee.

Regressietest in __tests__/api/pair-start.test.ts: verzoek op 0.0.0.0:3000 met x-forwarded-host eist dat de QR de publieke host volgt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/Scrum4Me!88
No description provided.