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 sessions —
HttpOnly,Secure, andSameSitecookies 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
_PREVIOUSvariables. 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 depth —
SameSitecookies plus OAuthstate, OIDCnonce, 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 strictReferrer-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-Forcan’t be spoofed, keeping rate limits and audit logs honest. TLS is terminated at your proxy, and theSecurecookie flag followsX-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), astateparameter for CSRF, and anoncefor 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:actionpermissions assigned per app; OIDC scopes gate token claims and the/userinforesponse. - 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
/healthcheck and an optional token-protected/metricsendpoint.
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.