Ny flyt: magic-link (POST /auth/request-link → POST /auth/verify-link) + JWT-sesjon i HttpOnly/SameSite=Lax/dynamisk-Secure-cookie (30 dager), pluss /auth/logout og /auth/me. Ny migrasjon 004_auth.sql (unik e-post-indeks + magic_link_token-tabell). Sikkerhetsdesignet fra Plan-agent-gjennomgangen holdt gjennom testing: Token: secrets.token_urlsafe(32), kun SHA-256-hash lagres Atomisk forbruk (UPDATE...RETURNING, ikke les-sjekk-skriv) — hindrer replay Generisk respons uansett om e-posten finnes — hindrer enumerering app_user opprettes først ved vellykket verifisering, ikke ved forespørsel — hindrer massopprettelse Gamle uforbrukte lenker ugyldiggjøres når en ny utstedes PyJWT (byttet fra python-jose pga. bredere sårbarhetsflate) med eksplisitt algorithms=["HS256"] Ekte eksistens-sjekk mot app_user på hvert kall — en slettet bruker mister tilgang umiddelbart, ikke etter 30 dager Alle 12 planlagte tester bestått, inkludert cooldown, token-ugyldiggjøring, utløp, tuklet JWT, slettet bruker, og at debug-headeren nå er helt uten effekt. To ting funnet og fikset/dokumentert underveis: ON CONFLICT (email) matchet ikke den nye partielle unike indeksen uten eksplisitt WHERE-klausul — fikset. En reell, dypere RLS-bug (dokumentert i FEATURE_BACKLOG.md, ikke fikset her): organization-tabellens RLS-policy kaster en 500 i stedet for "se ingenting" når app.current_org leses tilbake som tomstreng (ikke NULL) på en gjenbrukt pool-tilkobling. Berører trolig alle 15 RLS-policyer i skjemaet — for stort og sensitivt (ADR-003-grunnmuren) til å hastefikse her, så jeg mitigerte det lokalt i /auth/me og satte det som punkt 1 i neste-steg-listen.
5 lines
85 B
Text
5 lines
85 B
Text
fastapi>=0.111
|
|
uvicorn[standard]>=0.30
|
|
asyncpg>=0.29
|
|
pydantic[email]>=2.7
|
|
pyjwt>=2.8
|