Skip to main content

Context

This is the Better Auth configuration for the organizational realm. The member realm is configured separately in core (ADR-010), and keeping the two configurations in different modules is what makes realm separation visible rather than remembered. The design position worth stating: we support passwords because B2B customers expect them, not because they are good. Everything around them is therefore hardened — breach-list checking, no composition rules that push users toward Password1!, and MFA (FS-IDN-0005) on top for anyone holding a sensitive permission. Magic link and passkey exist so a tenant that wants to avoid passwords entirely can.

Scope (normative)

  • Password authentication with modern hashing and breach-list checking.
  • Magic link over email, single-use and short-lived.
  • Passkeys (WebAuthn) as a first-class method.
  • Email verification and password reset flows.
  • identity.auth_accounts linking a user to each method they have enrolled.
  • Rate limiting and lockout on every credential path.

Non-scope (normative)

  • Social login and enterprise SSO — FS-IDN-0008.
  • MFA — FS-IDN-0005. A second factor is a separate concern from a first one.
  • Session issuance and lifetime — FS-IDN-0004.
  • Member authentication, which lives in core.

Behaviour (normative)

  1. A user may hold several methods at once: a password, a magic link and two passkeys. Removing the last method is rejected — it would lock the user out of their own account.
  2. Passwords are hashed with a memory-hard algorithm at parameters recorded in the spec and reviewed annually. Plain or reversibly encrypted storage is FORBIDDEN, and so is logging a password anywhere at any level.
  3. No composition rules. A minimum length and a breach-list check, nothing else. Rules that demand a symbol and a digit produce predictable passwords and a reuse habit; a breach check rejects the passwords that are actually compromised.
  4. Magic links and reset tokens are single-use, expire in 15 minutes, are stored hashed, and are invalidated when any other one is issued for the same user.
  5. Sign-in failures return an identical response and timing whether the email exists or not. Distinguishing them turns the endpoint into an account-enumeration oracle.
  6. Rate limiting is per identifier and per IP, with progressive lockout. A lockout notifies the user by email, because a lockout they did not cause is the signal that someone is trying.
  7. A password change or reset revokes every other session of that user and notifies them.
  8. Email verification is required before a user carries any role beyond member.
  9. Passkeys follow WebAuthn; a user may enrol several and name each, because a passkey bound to a lost laptop must be removable from a phone.
  10. FORBIDDEN: any credential in a log, an event payload, an error message or a URL.

Data (normative)

API (normative)

Events (normative)

None on the outbox. Authentication is recorded in core.audit_log with a typed actor; a domain event per sign-in would flood every consumer for no benefit.

Acceptance criteria (normative)

  1. Sign-in with an unknown email and with a wrong password are indistinguishable in body, status and timing — verified with a timing test, not by inspection.
  2. A password on a breach list is rejected at signup and at change.
  3. A magic link cannot be used twice, and expires at 15 minutes.
  4. Issuing a second magic link invalidates the first.
  5. A password reset revokes every other session and sends a notification.
  6. Removing the last authentication method is rejected.
  7. Progressive lockout triggers on repeated failures and notifies the user.
  8. A passkey enrolled on one device can be removed from another.
  9. Negative: no credential, token or reset link appears in any log, event, error message or URL — verified by a scan over a seeded request set.

Execution

Single slice, synchronous command. Better Auth configured for the organizational realm in backend/api; magic links and notifications are delivered through messaging by event, never by calling a provider from here.

Open questions

Changelog

Delivery record

Not implemented yet.