Initial deliverable of the architecture phase: 16 ADRs (stack, CRDT collaboration, plugin sandbox, import/export, backups, CI/CD), data model, permission model, real-time collaboration and plugin concepts, deployment/operations/security documentation, and the milestone roadmap that the implementation issues are derived from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2.7 KiB
2.7 KiB
ADR 0007: Cookie sessions, Argon2id, OIDC-ready identity model
- Status: accepted
- Date: 2026-07-04
Context
Kickoff decisions: self-contained user management (username, e-mail, password) with mandatory e-mail verification (double opt-in), password reset, rate limiting, and the option to disable self-registration per instance. SSO is not in the MVP but the design must allow adding OIDC login later without schema surgery.
Decision
Authentication
- Server-side sessions stored in PostgreSQL, referenced by an opaque
HttpOnly; Secure; SameSite=Laxcookie. No JWTs for browser sessions (revocability and simplicity win). Sliding expiration, default 30 days. - Passwords hashed with Argon2id (tuned parameters documented in code).
- E-mail flows (verification, password reset) use single-use, expiring, hashed tokens; mail is sent via SMTP (instance-configured, see setup wizard) through a mail outbox table with retry.
- Rate limiting on signup, login, password reset, and token endpoints: fixed-window counters in PostgreSQL keyed by IP and by account — no Redis (ADR 0002).
- Self-registration can be disabled instance-wide
(
instance_settings.registration_mode:open|closed). The kickoff also asked for the option of admin approval as a later hardening step; the setting is an enum soapproval_requiredcan be added without migration pain. - Collaboration tokens: the API issues short-lived (≤ 60 s validity for connect) signed JWTs solely for the WebSocket handshake with the collab server (ADR 0003). These are the only JWTs in the system.
OIDC readiness (not in MVP)
- The identity model separates account from login method:
users(profile, status) anduser_identities(provider=password| futureoidc:<issuer>,subject,credential). Password login is just one identity row. - E-mail is unique per user and verified; future OIDC linking matches on verified e-mail or explicit account linking.
Consequences
- Logout and account deactivation are immediate (session rows deleted).
- No shared secret sprawl: one signing key for collab tokens, rotated via environment configuration.
- Adding OIDC later means: new identity provider rows, an authorization-code flow module, and a login button — no changes to sessions, permissions, or user references.
Alternatives considered
- JWT access/refresh tokens in the browser: harder revocation, XSS exposure of tokens, no benefit for a same-origin SPA. Rejected.
- Auth libraries/services (Keycloak, Authentik as mandatory): heavy extra container contradicting easy self-hosting; external IdPs remain possible later through the OIDC path.