synops/docs/erfaringer/multi_subdomain.md
vegard dfcec6b3b0 Kontekst-velger: Hjem og Administrasjon som separate lenker
Dropdown viser begge arbeidsflater med absolutte URLer
(ws.synops.no og adm.synops.no). Navigasjon mellom subdomener
fungerer uten å miste sesjon.

Erfaringsnotat: multi-subdomain med SvelteKit — ORIGIN-fellen,
cookie-domene, CSRF, OIDC redirect URIs, sjekkliste for nye
subdomener.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 02:30:41 +00:00

125 lines
4.2 KiB
Markdown

# Erfaring: Multi-subdomain med SvelteKit (mars 2026)
## Kontekst
Innføring av `adm.synops.no` som admin-domene ved siden av
`ws.synops.no` (app) avslørte flere arkitekturelle blindsoner.
## Problemer vi møtte
### 1. ORIGIN låser hostname
**Symptom:** `event.url.hostname` returnerte alltid `ws.synops.no`
uansett hvilken Host-header som kom inn.
**Årsak:** `ORIGIN=https://ws.synops.no` i `.env`. SvelteKit
adapter-node bruker ORIGIN for å konstruere `event.url` — den
*overskriver* Host-headeren.
**Løsning:** Fjern ORIGIN. `AUTH_TRUST_HOST=true` lar SvelteKit
lese hostname fra selve HTTP Host-headeren.
**Læring:** ORIGIN er ment for single-origin deployments.
Multi-subdomain krever at SvelteKit leser Host dynamisk.
### 2. Session-cookie bundet til ett subdomain
**Symptom:** Login på `ws.synops.no` ga ikke tilgang til
`adm.synops.no`. Brukeren ble bedt om å logge inn igjen.
**Årsak:** Session-cookie var satt med `domain=ws.synops.no`
(default). Cookien var ikke tilgjengelig for `adm.synops.no`.
**Løsning:** Sett cookie-domene til `.synops.no` i auth.ts.
Alle subdomener deler sesjonen.
**Læring:** Wildcard cookie-domene (`.synops.no`) er nødvendig
når flere subdomener trenger samme autentisering. Det er trygt
så lenge alle subdomener er under vår kontroll.
### 3. CSRF cross-origin blokkering
**Symptom:** `Cross-site POST form submissions are forbidden`
ved login-callback fra Authentik.
**Årsak:** SvelteKit sin innebygde CSRF-sjekk sammenligner
request origin mot ORIGIN-variabelen. POST fra `adm.synops.no`
til OIDC-callback ble blokkert.
**Løsning:** `csrf: { checkOrigin: false }` i svelte.config.js.
Trygt fordi OIDC bruker PKCE + state som CSRF-beskyttelse.
**Læring:** SvelteKit sin CSRF-sjekk er for streng for
multi-origin. Deaktiver den når du har egen CSRF-mekanisme.
### 4. Authentik redirect URI
**Symptom:** OIDC-callback feilet fordi `adm.synops.no` ikke
var registrert som gyldig redirect URI.
**Årsak:** Bare `ws.synops.no` var registrert i Authentik.
**Løsning:** Legg til `adm.synops.no/auth/callback/authentik`
i Authentik sin provider-konfig.
**Læring:** Hvert subdomain trenger egen redirect URI i OIDC.
### 5. TLS-sertifikat
**Ikke et problem:** Caddy henter automatisk sertifikat for
nye domener via Let's Encrypt ACME. `adm.synops.no` fikk
sertifikat i løpet av sekunder ved første request.
**Læring:** Caddy sin auto-TLS er utmerket for nye subdomener.
Bare legg til i Caddyfile og restart.
## Arkitekturprinsipper vi trekker ut
### 1. Ikke hardkod hostnames i konfigfiler
ORIGIN, cookie-domene, redirect URIs — alt som binder til
et spesifikt hostname gjør multi-subdomain vanskelig.
Foretrekk dynamisk host-deteksjon (`AUTH_TRUST_HOST=true`).
### 2. Cookies på toppdomenet for relaterte subdomener
Når flere subdomener trenger samme sesjon, sett cookie på
`.synops.no`. CSRF-token kan forbli host-bound (`__Host-`
prefix) for ekstra sikkerhet.
### 3. Samme SvelteKit-instans for alle subdomener
Ikke kjør separate SvelteKit-instanser per subdomain. Én
instans som leser hostname og tilpasser seg. Enklere deploy,
delt kodebase, felles sesjon.
### 4. Caddy gjør routing, SvelteKit gjør logikk
Caddy ruter domene → SvelteKit. SvelteKit sjekker hostname
og tilpasser innhold. Caddy trenger ingen hostname-logikk
utover reverse proxy.
## Hva ble gjort riktig
- **Caddy-konfig:** Enkelt å legge til nytt subdomain
- **Delt SvelteKit:** Ingen duplisert kode
- **Cookie-wildcard:** Riktig for relaterte subdomener
- **AUTH_TRUST_HOST:** Fungerer godt uten ORIGIN
## Hva vi tenderer til å glemme
- **ORIGIN-variabelen** — den overstyrer alt. Sjekk den først.
- **Cookie-domene** — default er gjeldende hostname, ikke parent.
- **OIDC redirect URIs** — må oppdateres for hvert nytt domene.
- **Be bruker slette cookies** — gammel cookie fra ett domene
kræsjer med ny cookie fra wildcard-domenet.
## Sjekkliste for nytt subdomain
1. DNS A-record (Hetzner DNS Console)
2. Caddy-blokk i Caddyfile (auto-TLS)
3. Authentik redirect URI
4. Verifiser at ORIGIN *ikke* er satt (eller er kompatibel)
5. Verifiser cookie-domene (`.synops.no`)
6. Test: login → callback → riktig side
7. Be bruker slette cookies hvis problemer