Email and password sign-in
Sign users up and in with an email and password on SupaHost, confirm their address, set the minimum password length, and build a working password reset flow.
Email and password is on by default for every SupaHost project. A user signs up, confirms their address from an email Auth sends, and then signs in to get a session. Passwords are hashed with bcrypt by Auth and never stored in plain text.
Settings that apply
Dashboard → your project → Auth tab → Sign-in & security:
| Setting | Effect |
|---|---|
| Email sign-in | Turns email and password, magic links and email OTP on or off together. On by default |
| Disable new sign-ups | Existing users can still sign in, and you can still invite people. signUp returns an error |
| Skip email confirmation | Users get a session straight from signUp without confirming. Handy in development, risky in production |
| Minimum password length | 6 to 72 characters. Default 6 |
Click Save sign-in settings. The change reaches the project within a minute. The minimum length is the only password rule you can set on SupaHost; if you want more (a mix of character types, a blocklist), check it in your app before calling signUp.
Also set the Site URL on the Auth card, because confirmation and reset links fall back to it. See Site URL and redirect URLs.
Sign up
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'plum-orchard-42-lantern',
options: {
emailRedirectTo: 'https://invoices.example.com/auth/callback',
data: { company: 'Alex Design Ltd' },
},
})
With confirmation on (the default), data.user is set but data.session is null. Auth sends the Confirm sign-up email. It contains a button with a link and, below it, a one-time code. Either works:
- The link goes to
/auth/v1/verifyon your project, which confirms the address and redirects toemailRedirectTo(or the Site URL) with a session. - The code lets the user confirm without leaving your app, which helps on mobile:
const { data, error } = await supabase.auth.verifyOtp({
email: '[email protected]',
token: codeFromInput,
type: 'email',
})
If emailRedirectTo isn't in your Redirect URLs list, Auth ignores it and uses the Site URL instead.
When the email is already registered
With confirmation on, signing up with an address that already has an account doesn't return an error, so attackers can't use the form to find out who has an account. For an already confirmed account, the response has a user object with an empty identities array and no session. If you want to show "you already have an account", check data.user?.identities?.length === 0.
Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: passwordFromInput,
})
if (error?.code === 'invalid_credentials') {
// wrong email or password; don't say which
}
if (error?.code === 'email_not_confirmed') {
// offer to resend the confirmation email
}
Resend a confirmation email:
await supabase.auth.resend({ type: 'signup', email: '[email protected]' })
Every email counts toward the project's email limit (30 per rate-limit window, fixed on SupaHost), so don't resend automatically in a loop. See Rate limits.
Password reset
A reset has two steps: send the email, then let the user choose a new password once they're signed in through the link.
1. Send the reset email from a "Forgot password" form:
await supabase.auth.resetPasswordForEmail('[email protected]', {
redirectTo: 'https://invoices.example.com/account/new-password',
})
Auth sends the Reset password template. Add https://invoices.example.com/account/new-password (or a wildcard that covers it) to Redirect URLs first.
2. Set the new password. When the user follows the link, they arrive on that page signed in. In a browser-only app, the client library fires a PASSWORD_RECOVERY event you can listen for. In both browser and server apps, the page can simply call updateUser:
const { error } = await supabase.auth.updateUser({ password: newPassword })
If you use server-side auth with the PKCE flow, the link arrives with a ?code= parameter. Exchange it in your callback route first (see Server-side auth), then redirect to the new-password page. The user can also type the code from the email instead:
await supabase.auth.verifyOtp({ email, token: codeFromEmail, type: 'recovery' })
await supabase.auth.updateUser({ password: newPassword })
Changing a password while signed in
The same updateUser call works for a signed-in user on a settings page. Ask for the current password in your UI and check it by signing in again before you call it, so someone who walks up to an unlocked laptop can't change it.
Signing out
await supabase.auth.signOut() // all of this user's sessions (default)
await supabase.auth.signOut({ scope: 'local' }) // only this device
Common problems
- Emails don't arrive: check spam, then the rate limit, then the Site URL. See Not receiving Auth emails.
- The link says it's expired: links are single use, and some email scanners open them before the user does. See OTP or magic link expired.
- The link lands on the wrong site: see Redirected to the wrong URL.
For every option on these methods, see the supabase-js reference for signUp.