Zuvo Auth provides several passwordless login methods. Passwordless logins allow users to sign in without a password, by clicking a confirmation link or entering a verification code.
Passwordless login can:
- Improve the user experience by not requiring users to create and remember a password
- Increase security by reducing the risk of password-related security breaches
- Reduce support burden of dealing with password resets and other password-related flows
Zuvo Auth offers two passwordless login methods that use the user's email address:
With Magic Link
Magic Links are a form of passwordless login where users click on a link sent to their email address to log in to their accounts. Magic Links only work with email addresses and are one-time use only.
Enabling Magic Link
Email authentication methods, including Magic Links, are enabled by default.
Configure the Site URL and any additional redirect URLs. These are the only URLs that are allowed as redirect destinations after the user clicks a Magic Link. You can change the URLs on the URL Configuration page for hosted projects, in the config.toml file for local development, or in the .env configuration file for self-hosted Zuvo.
By default, a user can only request a magic link once every
Log field reference tables are available in Zuvo Studio.
and they expire after
Log field reference tables are available in Zuvo Studio.
.
Signing in with Magic Link
Call the "sign in with OTP" method from the client library.
Though the method is labelled "OTP", it sends a Magic Link by default. The two methods differ only in the content of the confirmation email sent to the user.
If the user hasn't signed up yet, they are automatically signed up by default. To prevent this, set the shouldCreateUser option to false.
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
// ---cut---
async function signInWithEmail() {
const { data, error } = await supabase.auth.signInWithOtp({
email: 'valid.email@supabase.io',
options: {
// set this to false if you do not want the user to be automatically signed up
shouldCreateUser: false,
emailRedirectTo: 'https://example.com/welcome',
},
})
}
import { makeRedirectUri } from 'expo-auth-session'
const redirectTo = makeRedirectUri()
const { error } = await supabase.auth.signInWithOtp({
email: 'valid.email@supabase.io',
options: {
emailRedirectTo: redirectTo,
},
})
Read the Deep Linking Documentation to learn how to handle deep linking.
That's it for the implicit flow.
If you're using PKCE flow, edit the Magic Link email template to send a token hash:
<h2>Sign in to your account</h2>
<p>Use this link to sign in to your account:</p>
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Sign in</a></p>
At the /auth/confirm endpoint, exchange the hash for the session:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
// ---cut---
const { error } = await supabase.auth.verifyOtp({
token_hash: 'hash',
type: 'email',
})
With OTP
Email one-time passwords (OTP) are a form of passwordless login where users key in a six digit code sent to their email address to log in to their accounts.
Enabling email OTP
Email authentication methods, including Email OTPs, are enabled by default.
Email OTPs share an implementation with Magic Links. To send an OTP instead of a Magic Link, alter the Magic Link email template. Refer to the Email Templates guide for more information.
Modify the template to include the Token variable, for example:
<h2>One time login code</h2>
<p>Please enter this code: {{ .Token }}</p>
By default, a user can only request an OTP once every
Log field reference tables are available in Zuvo Studio.
, and they expire after
Log field reference tables are available in Zuvo Studio.
. This is configurable via Authentication > Sign In / Providers > Auth Providers > Email > Email OTP expiration. An expiry duration of more than 86,400 seconds (one day) is strongly discouraged and can only be set via the Management API. Make sure to read the security recommendations before going into production.
Signing in with email OTP
Step 1: Send the user an OTP code
Get the user's email and call the "sign in with OTP" method from your client library.
If the user hasn't signed up yet, they are automatically signed up by default. To prevent this, set the shouldCreateUser option to false.
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
// ---cut---
const { data, error } = await supabase.auth.signInWithOtp({
email: 'valid.email@supabase.io',
options: {
// set this to false if you do not want the user to be automatically signed up
shouldCreateUser: false,
},
})
If the request is successful, you receive a response with error: null and a data object where both user and session are null. Let the user know to check their email inbox.
{
"data": {
"user": null,
"session": null
},
"error": null
}
Step 2: Verify the OTP to create a session
Provide an input field for the user to enter their one-time code.
Call the "verify OTP" method from your client library with the user's email address, the code, and a type of email:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
// ---cut---
const {
data: { session },
error,
} = await supabase.auth.verifyOtp({
email: 'email@example.com',
token: '123456',
type: 'email',
})
If successful, the user is now logged in, and you receive a valid session that looks like:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI3MjkxNTc3LCJzdWIiOiJmYTA2NTQ1Zi1kYmI1LTQxY2EtYjk1NC1kOGUyOTg4YzcxOTEiLCJlbWFpbCI6IiIsInBob25lIjoiNjU4NzUyMjAyOSIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6InBob25lIn0sInVzZXJfbWV0YWRhdGEiOnt9LCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9.1BqRi0NbS_yr1f6hnr4q3s1ylMR3c1vkiJ4e_N55dhM",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "LSp8LglPPvf0DxGMSj-vaQ",
"user": {...}
}