feat(dpop): add documentation

This commit is contained in:
Edouard Vanbelle
2026-08-09 00:07:05 +02:00
parent 56726618dc
commit a8e801d1f9
2 changed files with 48 additions and 0 deletions
+8
View File
@@ -62,6 +62,14 @@ OPAQUE (RFC 9807) is a zero-knowledge password-authenticated key exchange: the p
| `OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS` | `1` | Client-side Argon2id iteration count (OWASP interactive-auth recommendation). |
| `OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM` | `1` | Client-side Argon2id parallelism lanes (OWASP recommendation). Higher only helps on multi-core hardware and can hurt single-core / older mobile devices. |
### DPoP — session cookie binding (RFC 9449)
DPoP cryptographically binds a session cookie to a browser-held ECDSA keypair (P-256, non-extractable via `SubtleCrypto`). Every request carries a signed proof the middleware verifies against the session's binding. Closes the info-stealer replay threat: a cookie copied to another machine is useless without the private key. Non-browser clients (Nextcloud sync via app passwords, CLI via device-authorization) never bind and are exempted at the middleware regardless of mode. See `docs/plan/dpop.md` for the full rollout plan.
| Variable | Default | Description |
|---|---|---|
| `OXICLOUD_DPOP_MODE` | `off` | Enforcement mode. `off` = middleware pass-through (default, ship-safe). `opportunistic` = verify when a proof is present, log `dpop.header_missing_but_session_bound` audit when absent on a bound session, but allow the request through (rollout mode — catches client bugs). `required` = bound sessions MUST present a valid proof or 401. Unbound sessions always exempt. Recommended rollout: `off` → `opportunistic` for 2-4 weeks → `required`. For DPoP to be meaningful, cookies must be `Secure` (`OXICLOUD_COOKIE_SECURE=true` in production over HTTPS) and reverse-proxy `X-Forwarded-Proto` / `X-Forwarded-Host` must reach the app so the `htu` claim canonicalises correctly. |
### Rate Limiting & Account Lockout
| Variable | Default | Description |
+40
View File
@@ -250,6 +250,46 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/oxicloud
# helps on multi-core devices and hurts single-core / older mobile)
#OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM=1
# -----------------------------------------------------------------------------
# DPOP — session cookie binding to a browser-held keypair (RFC 9449)
# -----------------------------------------------------------------------------
# Each SPA session generates a P-256 ECDSA keypair with `extractable: false`
# in the browser at login time; the JWK thumbprint is sent as `dpop_jkt`
# and stored on the session row. Every subsequent request carries a signed
# DPoP proof header the middleware verifies against the session's binding.
#
# Threat closed: an info-stealer that copies the cookie to another machine
# cannot replay the session — the private key never leaves the browser
# (SubtleCrypto stores it in the crypto subsystem; JS can only call
# `sign()` on the handle, never `exportKey()`).
#
# Non-browser clients (Nextcloud sync, mobile apps via Basic Auth on app
# passwords, CLI via device-authorization) never bind a keypair — their
# session rows have `dpop_jkt IS NULL` and the middleware exempts them
# regardless of mode. So enabling this does NOT break your NC clients.
#
# Values: off | opportunistic | required
# off — middleware pass-through, no verification. Default. Ship-
# safe while the client rollout catches up.
# opportunistic — verify when a proof is present, reject invalid ones;
# allow when absent (log a warning if the session was
# bound). Rollout mode — catches client bugs before
# flipping enforcement.
# required — bound sessions MUST present a valid proof or 401.
# Unbound sessions still work (see NC-client note above).
#
# Recommended rollout: off → opportunistic (2-4 weeks, watch audit for
# `dpop.header_missing_but_session_bound` counts trending to zero) →
# required. See `docs/plan/dpop.md` for the full rollout plan.
#
# NB: for DPoP to be meaningful, cookies must be `Secure` (HTTPS) —
# there's no point cryptographically binding a session that ships over
# plain HTTP. Set `OXICLOUD_COOKIE_SECURE=true` in production. Also
# ensure `X-Forwarded-Proto` + `X-Forwarded-Host` reach the app if
# you're behind a reverse proxy — the middleware reads those to build
# the canonical `htu` claim the proof binds to.
#OXICLOUD_DPOP_MODE=off
# -----------------------------------------------------------------------------
# RATE LIMITING & ACCOUNT LOCKOUT
# -----------------------------------------------------------------------------