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.