Skip to content

Security: OmElMon/Forge

Security

docs/security.md

Security baseline

  • Passwords are hashed with Argon2 through pwdlib; plaintext credentials are never stored.
  • Access tokens are short-lived. Refresh tokens rotate on use, and only their SHA-256 fingerprints are persisted.
  • The Next.js browser-facing layer keeps both tokens in HttpOnly, SameSite=Lax cookies. Tokens are never exposed to client JavaScript or browser storage.
  • Protected routes revalidate access against /auth/me; expired access tokens are transparently rotated through the server-side refresh flow.
  • A token's company_id selects a tenant, but authorization revalidates the user-company membership from PostgreSQL on each authenticated request.
  • Role checks are composed as FastAPI dependencies. Domain services must receive the resolved principal and derive tenant IDs from it.
  • Login lockout and per-IP rate limiting are enforced on the login endpoint. Failed attempts are counted per account email and per client IP; after account_lockout_max_attempts failures within account_lockout_window_seconds the account/IP is blocked for account_lockout_duration_seconds (429 + Retry-After), and the response is identical whether or not the email exists. The store follows rate_limiter_backend (in-process memory by default; Redis for shared multi-instance enforcement) and fails open if the store is unavailable. Frontal brute-force volume is additionally capped by the per-IP auth rate limit (30/min default).
  • Every response carries hardened security headers set by security_headers_middleware (outermost middleware, so they appear even on 429/4xx paths): X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, Cross-Origin-Opener-Policy: same-origin, and Strict-Transport-Security (max-age 180 days, includeSubDomains).
  • Email verification ships as single-use codes (POST /auth/email-verify + /auth/email-verify/confirm, delivered through the messaging port with an in-UI dev code when no real provider is set; outstanding codes expire on re-request; the reply never reveals whether an account exists). Verification state lives on users.email_verified and is surfaced via /auth/me. Enforcement is opt-in via EMAIL_VERIFICATION_REQUIRED (off by default so the pilot isn't disrupted — note flipping it on locks out existing unverified accounts until they verify).
  • TOTP MFA ships for owner/admin accounts (enrollment gated by CONFIG_ROLES): POST /auth/mfa/enroll returns the Base32 secret, provisioning URI, and 8 single-use recovery codes (codes are stored BCrypt-hashed, never plaintext); POST /auth/mfa/enroll/confirm activates enrollment only after the operator proves they scanned it with a valid code; POST /auth/mfa/disable requires a fresh TOTP or recovery code. Once confirmed, POST /auth/login stops returning a token pair and instead issues a 5-minute mfa_session challenge (mfa_required: true); POST /auth/mfa/verify exchanges a TOTP code or an unconsumed recovery code for the real tokens. Recovery use is single-use and audited (auth.mfa.verified with recovery_used), and all lifecycle actions emit auth.mfa.* audit entries. The TOTP secret itself must be Base32-plaintext in mfa_settings (codes must be derivable at verify time) and is scoped by row-level security.
  • Tenant-owned tables require indexed, non-null company identifiers with foreign-key integrity.
  • Audit records are append-oriented and retain actor, action, resource, tenant, and structured context.
  • Secrets belong in the deployment secret manager or local .env, never source control.

Before production: turn on EMAIL_VERIFICATION_REQUIRED; connect a real messaging provider so the in-app dev codes stop showing; wire the payments provider; CSRF-aware audit of cookie-based mutation routes (mutation proxies already enforce same-origin); strict Content-Security-Policy for the Next.js host; S3 upload scanning (if file uploads ship); database row-level-security policies (considering Supabase); and centralized security event monitoring. Rate limiting, account lockout, password reset, email verification (with a web verify-email page), TOTP MFA (with login challenge step + enrollment in Settings), and the hardened security-header set are done (covered by tests).

There aren't any published security advisories