Skip to main content
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)
}
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!')
}
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' })

Complete Sign-In Form

React Sign-In Component
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 CodeDescriptionSolution
invalid_credentialsWrong email or passwordVerify credentials
email_not_confirmedEmail not verifiedResend confirmation email
user_not_foundUser doesn’t existDirect to sign up
invalid_grantSession expiredRequest 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

Build docs developers (and LLMs) love