Skip to content
Early preview. authbase is in active development. The source code and packages are not public yet; these docs describe how it works ahead of the first public release.

Security model

What authbase protects, how, and what stays your responsibility.

Each release is reviewed requirement by requirement against OWASP ASVS 4.0.3, level 2.

Party Trusted with Never gets
Operator the host, the database, the master key end-users’ passwords (only hashes exist)
Account (developer) its apps: users, keys, settings, audit log other accounts’ apps (every app route checks ownership; another account’s app is a 404)
API key one app’s end-user API the management API, other apps
End-user their own tokens anything but what your backend exposes
Your backend an API key, end-users’ tokens in transit signing keys (tokens are verified with public keys)
Secret At rest
End-user and account passwords Argon2id (19 MiB, t=2), NFKC-normalized first; never logged or emailed
API keys (sk_live_, 256 bits) SHA-256; shown once
Refresh tokens (rt_, 256 bits) SHA-256
Dashboard sessions (256 bits) SHA-256; the cookie is HttpOnly, Secure, SameSite=Lax, __Host- on https
Reset and verification links SHA-256; single use, 30 minutes / 24 hours, voided by a newer link or a password or email change
Apps’ Ed25519 signing keys AES-256-GCM under the master key, bound to their app and key id
Queued email (it carries live links) AES-256-GCM under the master key; deleted once sent
The master key only in the environment; never in the image, the database or logs

A stolen database therefore yields no usable credential; a stolen master key alone yields nothing. Both together allow forging tokens, which is why the runbook for that case rotates every signing key (after a leak).

  • Access tokens are EdDSA-signed JWTs with typ: at+jwt (RFC 9068), an issuer per app and the app id as audience, so a token for one app is worthless to another, and other JWTs signed by the same key are not access tokens. Verifiers must accept only EdDSA and take keys only from the configured JWKS URL, never from the token (the SDKs do).
  • They cannot be revoked; their lifetime (15 minutes by default, at most 60) bounds how long a disabled or signed-out user keeps access.
  • Refresh tokens rotate on every use. Re-use of a spent one revokes the whole family, which ends a stolen token’s life at the next legitimate refresh (token.reuse_detected in the audit log).
  • A password change or reset revokes every refresh token of the user.
  • Per-IP rate limits on login, sign-up, reset and verification requests and dashboard sign-in; per-API-key limits. Behind a proxy they depend on AUTHBASE_TRUSTED_PROXIES being right.
  • Per-identifier lockout after 5 failures in 15 minutes (1 minute, doubling to 1 hour), shared by every instance through the database.
  • At most max(2, CPUs) password hashes run at once; beyond that requests wait up to 2 seconds, then get 429, so a login flood cannot exhaust memory.
  • Login answers the same for an unknown identifier and a wrong password; reset and verification requests answer 202 for any address.
  • The dashboard is served by authbase itself: one origin, no CORS, a Content-Security-Policy of 'self' with nothing inline, and CSRF protection (Sec-Fetch-Site / Origin) on every state-changing request.
  • The hosted reset and verification pages run no code beyond one same-origin script (a “Show passwords” checkbox), are never cached, and act only on a POST, so link-scanning mail filters cannot use them.
  • Every response carries X-Content-Type-Options, Referrer-Policy and, on https, HSTS.

Every change to apps, keys and users, and every login, failure, lockout and token reuse, is written to the app’s audit log in the same transaction as the change, with the actor (account, API key, user or the operator’s CLI), the client IP and the time. Master key rotations are recorded as instance events.

As an integrator:

  • Keep the API key server-side, in the environment. Never ship it to a browser or a mobile app.
  • Verify every access token (the SDK middleware, or the rules in integration guide).
  • Keep refresh tokens out of reach of scripts: an HttpOnly cookie or your server’s session, not localStorage.
  • Pass the end-user’s IP (client_ip) on login and refresh, so rate limits and the audit log mean something.
  • Keep access tokens short-lived if you rely on disabling users.

As an operator:

  • Serve it over HTTPS only, behind a proxy you list in AUTHBASE_TRUSTED_PROXIES.
  • Keep the master key in a secret store, apart from database backups.
  • Close sign-up (AUTHBASE_ALLOW_SIGNUP=false) once your developers have accounts.
  • Keep /metrics off the internet (it is on its own listener for that).
  • No multi-factor authentication yet, for end-users or the dashboard (planned).
  • No social login or hosted login page yet (planned).
  • Per-IP rate limits are counted per instance; lockout is shared.
  • Access tokens cannot be revoked before they expire (by design, above).