Skip to content

Sign-in and sign-up flows

The components and the hosted pages run these flows for you; @zautha/js exposes them step by step (zautha.signIn, zautha.signUp).

  • Sign-up: an email address, verified with an email code or an email link, and a password unless the instance makes it optional; a username and names when Auth settings require them. Or a social provider, whose verified email needs no code.
  • Sign-in, first factor: password, email code, email link, passkey, or a social provider (Google, GitHub, Microsoft, Discord, GitLab, Apple). A forgotten password is reset with an email code.
  • Second factor, for a user with two-step verification: an authenticator app code, a backup code or a passkey. A passkey used as the first factor counts as both. See Account security.
  • Tickets: an organization invitation link signs the invited address in or up (strategy: "ticket"); an impersonation link signs a support agent in as the user.

Email codes are valid for 10 minutes and can be tried 5 times; a new one can be sent every 30 seconds. Passwords are at least the instance’s minimum length (8 by default) and at most 256 bytes, are not one of the 10,000 most common passwords and are checked against known data breaches (form_password_pwned), and do not contain the application’s name, zautha, the user’s username or the part of their email address before the @ (form_password_context_word). They are stored as Argon2id hashes.

With enumeration protection on (the default), a sign-in or sign-up never reveals whether an email address has an account: an unknown identifier is offered the same factors as a known one and fails the same way at the password or code, and a sign-up with a taken address goes on while the address’s owner is told by email. An address always gets one email: when there is no account behind it, or a password reset is asked for an account without a password, the email says so instead of carrying a code. Turn it off in Auth settings to answer at once with form_identifier_not_found and form_identifier_exists.

Every wrong password or code counts against the identifier, whether it has an account or not. After 5 failures it is locked for 1 minute, after 10 for 5 minutes, after 15 for 30 minutes and after 20, and each one after, for an hour. Failures are forgotten after 24 hours. A locked identifier gets identifier_locked with Retry-After. Requests are also rate limited per IP address and per identifier (too_many_requests).

On a production instance every sign-up, and a sign-in once the identifier or the IP address has failed often enough, needs a Cloudflare Turnstile check. The SDK runs it in a frame from https://accounts.zautha.com; most visitors never see it. A page with a Content Security Policy must allow frame-src https://accounts.zautha.com. Development instances have no bot check. See @zautha/js for the details and its errors.