Security

Auth security, down to the primitives

ManyRows is an authentication server — so security isn’t a feature bolted on the side, it’s the whole job. Every protection below is on by default and built on standard, auditable primitives — Argon2id, ES256, AES‑256‑GCM, WebAuthn — with no proprietary crypto. Here’s exactly what guards your users, whether you self-host today or run it as a managed service later.

Passwords & credentials

If you store passwords, you store them the way OWASP recommends today — and verify them in a way that doesn’t leak who has an account.

  • Argon2id hashing — memory-hard and GPU-resistant, tuned to m=64 MiB, t=3, p=1. The cost parameters are baked into every hash (PHC format), so you can raise them over time without invalidating existing passwords.
  • Strength by guessability, not character rules — passwords are scored with a zxcvbn-style estimator (seeded with the user’s own email and name) rather than arbitrary “one uppercase, one symbol” rules that just produce Password1!. Minimum length and strength are configurable per app.
  • Constant-time verification — comparisons are constant-time, and a dummy hash is evaluated for unknown accounts so response timing never reveals whether an email is registered.
  • Optional reuse prevention — block reuse of a user’s recent passwords, per app, when your compliance regime asks for it.
  • No forced rotation — ManyRows follows NIST SP 800‑63B and doesn’t expire good passwords on a timer, a practice that pushes users toward weaker, predictable choices.

Multi-factor & strong auth

Phishing-resistant factors are first-class, and every one-time secret is short-lived, single-use, and stored hashed — never in the clear.

  • Passkeys / WebAuthn (FIDO2) — hardware-backed, phishing-resistant credentials, including discoverable credentials for fully passwordless sign-in.
  • TOTP authenticator apps (RFC 6238) — standard 30-second codes, replay-protected by tracking the consumed time-step so a code can’t be used twice.
  • One-time recovery codes — generated once, stored only as keyed (HMAC) hashes bound to the account, and burned on use.
  • Magic links & email OTP — high-entropy, short-lived, hashed at rest, single-use, and attempt-capped, with a resend cooldown to prevent inbox flooding.

Sessions & tokens

Stateless tokens for fast, offline verification; stateful records for instant revocation. You get both, with theft-resistance built in.

  • ES256 access tokens — signed with ECDSA P‑256, short-lived (15 min by default), and verifiable offline by any service against the published /.well-known/jwks.json — no shared secret to distribute.
  • Rotating refresh tokens — stored only as SHA‑256 hashes and rotated on every use, with reuse detection: replaying a rotated token revokes the whole session.
  • DPoP device binding (RFC 9449) — optionally bind a refresh token to a non-extractable device key, so a stolen token is useless anywhere else.
  • Zero-downtime key rotation — signing keys roll with an overlap window (current and previous keys both published in the JWKS) so rotating keys never signs anyone out.
  • Hardened browser sessionsHttpOnly, Secure, and SameSite cookies that JavaScript can’t read; idle and absolute expiry; a configurable cap on concurrent sessions; and server-side revocation.

Encryption & key management

Secrets at rest are encrypted with authenticated encryption, bound to their location in the database, and rotatable without downtime.

  • AES‑256‑GCM at rest — signing keys, OAuth client secrets, SMTP credentials, TOTP seeds, and webhook secrets are all encrypted, not stored plaintext.
  • Tamper- and swap-resistant — every ciphertext is bound to its row with additional authenticated data (AAD), so an encrypted value can’t be lifted and replayed into a different record.
  • Keys stay in your database — the encryption and signing material lives in your own Postgres (system_secrets), captured by an ordinary database backup. There’s no separate keystore to provision or lose.
  • Explicit, online key rotation — a versioned envelope with a key id lets you re-key live; previous keys are honored during a grace window via _PREVIOUS variables. Keys are auto-generated on first boot if you don’t supply your own.

Attack resistance

The common attacks against an auth server — credential stuffing, enumeration, CSRF, clickjacking, SSRF, injection — are blunted by default.

  • Layered rate limiting — per-IP (30 / 10 min) and per-account (10 / 10 min) limits on authentication endpoints, plus a daily per-address cap on outbound emails.
  • Progressive lockout — repeated failures lock an account for an escalating 15 min → 1 hour → 24 hours within a rolling window.
  • Enumeration resistance — login, reset, and registration return generic responses, and rate-limit attempts are counted even for unknown emails, so attackers can’t map who has an account.
  • CSRF defense in depthSameSite cookies plus OAuth state, OIDC nonce, and PKCE on the relevant flows.
  • Secure headers on by default — HSTS (max-age=1y; includeSubDomains), X-Frame-Options: DENY + CSP frame-ancestors 'none' against clickjacking, X-Content-Type-Options: nosniff, and a strict Referrer-Policy.
  • SSRF guards — outbound webhook and identity- provider requests are HTTPS-only and refused to private, loopback, and link-local ranges — re-checked at connection time to defeat DNS rebinding.
  • Injection-safe by construction — every query is parameterized (no string-built SQL), and an optional Cloudflare Turnstile challenge can gate registration and reset.
  • Trustworthy client IPs — a trusted-proxy allowlist means X-Forwarded-For can’t be spoofed, keeping rate limits and audit logs honest. TLS is terminated at your proxy, and the Secure cookie flag follows X-Forwarded-Proto.

OAuth, OIDC & SSO

ManyRows is both a standards-compliant OAuth/OIDC client (for social and corporate logins) and an OIDC provider for your own apps — with the protocol-level protections enforced for you.

  • PKCE, state & nonce — the authorization- code flow runs with PKCE (S256), a state parameter for CSRF, and a nonce for replay protection.
  • Verified ID tokens — signature, issuer, and audience are checked on every ID token, and provider endpoints must be HTTPS.
  • Exact redirect-URI allowlisting — redirect targets are matched exactly, never by substring or prefix.
  • Six built-in logins, plus bring-your-own — Google, Apple, Microsoft, GitHub, Kakao, and Naver out of the box, and any OIDC/OAuth2 provider (Okta, Auth0, Keycloak, Entra…) added from the admin UI via issuer discovery — no code, no release.
  • Consent captured at signup — including social signup, recorded append-only with version, timestamp, and IP.

Isolation & access control

One install can run many apps. The boundaries between them — and between tenants inside them — are enforced in every query, not left to convention.

  • Scoped hierarchy — workspace → project → app → user pool, with data access scoped at every level so one tenant can never read another’s.
  • Per-app secrets & settings — each app has its own OAuth credentials, sign-in methods, CORS origins, and redirect allowlists. Nothing is shared implicitly.
  • User pools as identity boundaries — share a pool for SSO across related apps, or isolate them so credentials and resets never cross over.
  • RBAC & scopes — roles and resource:action permissions assigned per app; OIDC scopes gate token claims and the /userinfo response.
  • Scoped API keys — server-to-server keys are read or read-write, workspace- or app-scoped, rate-limited, and shown only once.
  • Locked-down first boot — the super-admin slot can be pre-pinned to a known email (MANYROWS_SUPER_ADMIN_EMAIL) so a scanner can’t claim a fresh install before you do.

Audit & observability

Everything security-relevant is recorded in a structured log you can query, retain, and reason about.

  • Structured auth log — sign-in success and failure (with reason), logout, password and MFA changes, session revocation, admin actions, and organization membership changes.
  • Forensic detail — each entry captures the actor, method, outcome, IP, user-agent, and request id.
  • Configurable retention — a default 90-day window with an automatic cleanup janitor; tune it to your policy.
  • Operational endpoints — a /health check and an optional token-protected /metrics endpoint.

Privacy & data protection

ManyRows ships tooling to help you meet privacy obligations like GDPR and CCPA. It’s tooling, not a compliance certification — but it does the mechanical work these regimes require.

  • Self-service data export — a user can download their profile, linked identities, sessions, passkeys, organization memberships, and recent auth history as JSON (subject access).
  • Right to erasure — account deletion (confirmed by password or emailed code) cascades the user’s data and scrubs residual PII from logs and webhook payloads in a single transaction.
  • Consent records — stored append-only with version, timestamp, and IP, so you can prove what was agreed and when.
  • Data minimization — no display names collected by default; IP addresses are anonymized in stdout logs by default; emails are masked in operational logs.
  • Protected email changes — changing an email requires confirmation from both the old and new address, so a hijacked session can’t silently take over the account.

Hardened by default

There’s no security checklist to wire up after install. The defaults are the secure path.

  • Standards, not secrets — Argon2id, ES256 / JWKS, AES‑256‑GCM, WebAuthn, OAuth / OIDC, and DPoP. Recognizable, auditable building blocks — no home-grown cryptography.
  • Open source — the full implementation is public (AGPL‑3.0), so you can read exactly what runs in front of your users instead of trusting a black box.
  • Hardening guidance — the self-host guide covers TLS termination, cookie scope, trusted proxies, and key rotation for production.
Found a security issue? Responsible disclosures are welcome — get in touch and we’ll work with you on a fix and timeline.