Sign-in

Passwords, codes and two-factor

Password, passwordless one-time codes by email or SMS, authenticator-app two-factor and recovery codes.

View .md llms.txt

Everything here is set in the console under Login & Sign-Up and Security, and applies to every application of the tenant. Your apps don't change: they redirect to the hosted page, which shows what's turned on.

#Identifiers

What people type to sign in: email (on by default), username and phone number. With several turned on, one field accepts any of them. For each identifier you choose whether it signs in with a password, a one-time code, or both. Phone numbers can be limited to some country codes.

#Passwords

Passwords are hashed with argon2id. Password rules (length, required character kinds) apply when people choose a password. Imported hashes (bcrypt, PBKDF2, Firebase scrypt…) keep working and are upgraded to argon2id at the next sign-in.

Password recovery sends a one-time code by email (or SMS for phone accounts) and lets the user pick a new password. You can send people to your own page instead (Login & Sign-Up → Sign-In → Use your own recovery page instead).

#One-time codes (passwordless)

With one-time codes on, users get a 6-digit code (6 to 8, configurable) by email or text message instead of typing a password. Codes expire after 10 minutes (configurable, 1 to 60) and allow 5 attempts. If an account has both an email and a phone number, the user picks where to receive it.

  • Email codes are sent by 9in, with your logo and wording (Emails and texts).
  • SMS codes go through 9in's Twilio account on paid plans, or through your own Twilio account on any plan (Security → SMS delivery), with a sender number or a Twilio Verify service (Emails and texts). Limit the countries texts may go to; it's the main defence against SMS pumping fraud.
  • With your own user database, 9in sends you an otp.requested event and you deliver the code yourself.

In the sandbox, codes are always 000000 and nothing is sent.

#Sign-up

Login & Sign-Up → Sign-Up turns self-registration on or off, chooses the fields the form asks for (name, username, phone, birthdate…, each optional or required, plus your own custom fields) and whether people are signed in straight after. You can also send people to your own sign-up page.

Open the sign-up form directly with prompt=create on the authorization request (auth.signup() in the SDK).

#Two-factor authentication

Two-factor uses an authenticator app (Google Authenticator, 1Password, Authy… any TOTP app, 6 digits every 30 seconds) after the first factor, with 10 single-use recovery codes for when the phone is lost. Security → Two-factor authentication:

PolicyWhat happens
OffNever asked
Optional (default)Users turn it on from their account page; once on, it's asked at every sign-in
RequiredEveryone sets it up at their next sign-in

It applies to every sign-in method, including social, enterprise and login links. The ID token's amr claim lists how the user signed in: pwd (password), otp (one-time code), social, fed (enterprise), link (login link), plus mfa after a second factor. Access tokens don't carry amr; your app can check the ID token before calling sensitive APIs, or the tenant can require two-factor for everyone.

Users manage two-factor on their account page (/account). For someone who lost their phone and their recovery codes, an admin opens the user in the console and chooses Reset 2FA; the reset is recorded in the activity log, and if two-factor is required they set it up again at their next sign-in.

#Brute-force protection

After 10 failed attempts (configurable) an account and the IP address are locked for 15 minutes (configurable). Bot protection with Cloudflare Turnstile can be added to sign-up, password recovery and sign-in (always, or only after failures). See Sessions and security.

#Legacy grants

For applications that can't use redirects yet, the password grant (resource owner password credentials) can be turned on per first-party application, and the implicit flow per application. Both are off by default and not recommended: they bypass bot protection and single sign-on. The password grant also refuses users who have two-factor turned on (and everyone when the tenant requires it), with invalid_grant, because it can't ask for a second factor. Prefer the authorization code flow.