# Security notes ## Authentication & authorisation - **Okta OIDC** (Authlib) validates state, nonce, and the id_token signature (JWKS from discovery). The app trusts the `email` claim and checks it against its own allowed-users list. Roles are re-resolved per request, so removal/demotion is immediate. - **Static mode** is development-only: loud startup warning, `hmac.compare_digest` credential check, and it refuses to bind a non-loopback interface without `PPSQ_ALLOW_INSECURE_AUTH=1`. ## Sessions & CSRF - Cookies: `HttpOnly`, `SameSite=Lax` (required for the OIDC callback), `Secure` when `cookie_secure=true`. **Browser-session only** — no persistent cookie, no idle-timeout cookie; closing the browser ends the session. - CSRF: a per-session token from `/api/config` must be echoed as `X-CSRF-Token` on every state-changing `/api/*` request. Enforced in `before_request`. - The post-login `next` target is sanitised to a local path (`safe_next`) — no open redirect. ## Secrets - `pps.password`, `okta.client_secret`, `auth.static.password`, `app.secret_key` are **write-only**: never returned by any endpoint (`redacted()` emits a `*_set` boolean). A blank value on save means "unchanged". - `config.toml` is written `0600`. It is plaintext on disk — see "Known limitations". ## Input validation - Config writes are validated before the atomic write (URLs, ranges, folder membership, pipeline step order, emails/roles). Folder names may not contain commas (they'd corrupt the `localguid` join in the PPS POST payload). - `POST /api/actions` validates `targetfolder` against the configured folder list — a user cannot move mail to an arbitrary folder. - The log viewer's `source` is an enum key resolved to a path server-side; there is no client-supplied path (no arbitrary file read). ## Transport - Put a TLS-terminating reverse proxy in front; bind the app to loopback. - `verify_tls` controls whether the app verifies the PPS certificate. `false` (self-signed) is common for PPS but should be a CA-bundle path where possible; the client logs a loud warning when verification is disabled. ## Known limitations (accepted for internal use) - `config.toml` holds secrets in plaintext (0600, never readable via the app). Real secret management (Vault, env injection) is a future step. - Roles come from the app's own list, not Okta groups. - Single-process by design; no HA/failover.