Supabase offers multiple sign-in methods to authenticate users. Choose the method that best fits your application’s needs.
Password Sign In
Authenticate users with email and password:
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'example-password',
})
if (error) {
console.error('Error signing in:', error.message)
} else {
console.log('User signed in:', data.user)
console.log('Session:', data.session)
}
Magic Link Sign In
Passwordless authentication via email:
const { data, error } = await supabase.auth.signInWithOtp({
email: '[email protected]',
options: {
emailRedirectTo: 'https://example.com/auth/callback',
},
})
if (error) {
console.error('Error:', error.message)
} else {
console.log('Check your email for the magic link!')
}
Complete Magic Link Example
React Magic Link Component
import { useState, useEffect } from 'react'
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)
export default function MagicLinkAuth() {
const [email, setEmail] = useState('')
const [loading, setLoading] = useState(false)
const [user, setUser] = useState(null)
useEffect(() => {
// Check for existing session
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null)
})
// Listen for auth changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => {
setUser(session?.user ?? null)
}
)
return () => subscription.unsubscribe()
}, [])
const handleLogin = async (e) => {
e.preventDefault()
setLoading(true)
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
emailRedirectTo: window.location.origin,
},
})
if (error) {
alert(error.message)
} else {
alert('Check your email for the login link!')
}
setLoading(false)
}
const handleLogout = async () => {
await supabase.auth.signOut()
setUser(null)
}
if (user) {
return (
<div>
<h1>Welcome!</h1>
<p>You are logged in as: {user.email}</p>
<button onClick={handleLogout}>Sign Out</button>
</div>
)
}
return (
<form onSubmit={handleLogin}>
<h1>Sign In with Magic Link</h1>
<input
type="email"
placeholder="Your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Loading...' : 'Send magic link'}
</button>
</form>
)
}
Phone/SMS Sign In
Authenticate with phone number and OTP:
// Send OTP
const { data, error } = await supabase.auth.signInWithOtp({
phone: '+13334445555',
})
// Verify OTP
const { data, error } = await supabase.auth.verifyOtp({
phone: '+13334445555',
token: '123456',
type: 'sms',
})
Phone authentication requires configuring an SMS provider in your Supabase dashboard under Authentication > Settings > Phone Auth.
Session Management
Get Current Session
const { data: { session } } = await supabase.auth.getSession()
if (session) {
console.log('User is signed in:', session.user)
console.log('Access token:', session.access_token)
} else {
console.log('No active session')
}
Get Current User
const { data: { user } } = await supabase.auth.getUser()
if (user) {
console.log('User email:', user.email)
console.log('User metadata:', user.user_metadata)
}
Refresh Session
const { data, error } = await supabase.auth.refreshSession()
const { session, user } = data
Auth State Changes
Listen for authentication events:
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log('Auth event:', event)
switch (event) {
case 'SIGNED_IN':
console.log('User signed in:', session.user)
break
case 'SIGNED_OUT':
console.log('User signed out')
break
case 'TOKEN_REFRESHED':
console.log('Token refreshed')
break
case 'USER_UPDATED':
console.log('User updated')
break
}
}
)
// Unsubscribe when component unmounts
subscription.unsubscribe()
Sign Out
End the user session:
const { error } = await supabase.auth.signOut()
if (error) {
console.error('Error signing out:', error.message)
} else {
console.log('Signed out successfully')
}
Sign Out from All Devices
// Sign out from all sessions
const { error } = await supabase.auth.signOut({ scope: 'global' })
import { useState } from 'react'
import { createClient } from '@/lib/supabase/client'
export default function SignInForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const supabase = createClient()
const handleSignIn = async (e) => {
e.preventDefault()
setLoading(true)
setError('')
const { error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
setError(error.message)
} else {
// Redirect or update UI
window.location.href = '/dashboard'
}
setLoading(false)
}
return (
<form onSubmit={handleSignIn}>
<h1>Sign In</h1>
{error && (
<div style={{ color: 'red', marginBottom: '1rem' }}>
{error}
</div>
)}
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Signing in...' : 'Sign In'}
</button>
<p>
Don't have an account? <a href="/signup">Sign up</a>
</p>
</form>
)
}
Password Recovery
Send password reset email:
const { data, error } = await supabase.auth.resetPasswordForEmail(
'[email protected]',
{
redirectTo: 'https://example.com/update-password',
}
)
Update password:
const { data, error } = await supabase.auth.updateUser({
password: 'new-password'
})
Error Handling
Common sign-in errors:
| Error Code | Description | Solution |
|---|
invalid_credentials | Wrong email or password | Verify credentials |
email_not_confirmed | Email not verified | Resend confirmation email |
user_not_found | User doesn’t exist | Direct to sign up |
invalid_grant | Session expired | Request new magic link |
Security Best Practices
Supabase automatically rate limits sign-in attempts. Configure limits in Authentication > Settings > Rate Limits.
Sessions expire after 1 hour by default. Supabase automatically refreshes tokens if the user is active.
Always use HTTPS in production. Supabase sets secure, httpOnly cookies automatically.
Next Steps
OAuth Sign In
Add social login providers
Multi-Factor Auth
Enhance security with MFA