Back to Blog
Security

Authentication Strategies with Supabase: Beyond Email and Password

EagerUI Team

EagerUI Team

2025-08-157 min read
Authentication Strategies with Supabase: Beyond Email and Password

Authentication Strategies with Supabase: Beyond Email and Password

Authentication is a critical component of most modern applications, yet implementing it securely can be challenging. Supabase simplifies this process by providing a comprehensive authentication system that supports multiple strategies out of the box. In this article, we'll explore the various authentication methods available in Supabase and how to implement them in your applications.

Understanding Supabase Auth

Supabase Auth is built on top of GoTrue, an open-source API for managing users and issuing JWT tokens. It provides a secure, ready-to-use authentication system that integrates seamlessly with Supabase's Row Level Security (RLS) policies, ensuring that users can only access their own data.

Email and Password Authentication

The most basic form of authentication is email and password. Here's how to implement it with Supabase:

Sign Up

const signUp = async (email, password) => {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
  });
  
  if (error) {
    console.error('Error signing up:', error.message);
    return null;
  }
  
  return data;
};

By default, Supabase will send a confirmation email to the user. You can customize this behavior:

// Sign up without email confirmation
const signUpWithoutConfirmation = async (email, password) => {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      emailRedirectTo: 'https://yourapp.com/welcome',
      data: {
        first_name: 'John',
        last_name: 'Doe',
        // Add any custom user metadata here
      }
    }
  });
  
  // Handle response
};

Sign In

const signIn = async (email, password) => {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  
  if (error) {
    console.error('Error signing in:', error.message);
    return null;
  }
  
  return data;
};

Password Reset

Supabase makes it easy to implement password reset functionality:

// Request password reset
const requestPasswordReset = async (email) => {
  const { data, error } = await supabase.auth.resetPasswordForEmail(email, {
    redirectTo: 'https://yourapp.com/reset-password',
  });
  
  if (error) {
    console.error('Error requesting password reset:', error.message);
    return false;
  }
  
  return true;
};

// Update password after reset
const updatePassword = async (newPassword) => {
  const { data, error } = await supabase.auth.updateUser({
    password: newPassword,
  });
  
  if (error) {
    console.error('Error updating password:', error.message);
    return false;
  }
  
  return true;
};

Magic Link Authentication

Magic links provide a passwordless authentication experience by sending a login link to the user's email:

const signInWithMagicLink = async (email) => {
  const { data, error } = await supabase.auth.signInWithOtp({
    email,
    options: {
      emailRedirectTo: 'https://yourapp.com/dashboard',
    },
  });
  
  if (error) {
    console.error('Error sending magic link:', error.message);
    return false;
  }
  
  return true;
};

This sends an email with a link that, when clicked, automatically logs the user in. It's a great option for users who don't want to remember another password.

Social Authentication

Supabase supports authentication with popular social providers like Google, Facebook, GitHub, and more. Here's how to implement it:

Step 1: Configure Provider in Supabase Dashboard

  1. Go to your Supabase project dashboard
  2. Navigate to Authentication > Providers
  3. Enable the providers you want to use
  4. Configure the provider with the required credentials (Client ID, Client Secret)
  5. Add your app's redirect URL (e.g., https://yourapp.com/auth/callback)

Step 2: Implement Sign In with Social Provider

// Sign in with Google
const signInWithGoogle = async () => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'google',
    options: {
      redirectTo: 'https://yourapp.com/dashboard',
    },
  });
  
  if (error) {
    console.error('Error signing in with Google:', error.message);
    return null;
  }
  
  return data;
};

// Sign in with GitHub
const signInWithGitHub = async () => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'github',
    options: {
      redirectTo: 'https://yourapp.com/dashboard',
    },
  });
  
  // Handle response
};

// Sign in with Facebook
const signInWithFacebook = async () => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'facebook',
    options: {
      redirectTo: 'https://yourapp.com/dashboard',
      scopes: 'email',
    },
  });
  
  // Handle response
};

Step 3: Handle the Redirect

When the user is redirected back to your application, you need to handle the authentication:

// In your callback page component
useEffect(() => {
  const { data: authListener } = supabase.auth.onAuthStateChange(
    async (event, session) => {
      if (event === 'SIGNED_IN' && session) {
        // User has been signed in, redirect to dashboard
        window.location.href = '/dashboard';
      }
    }
  );

  return () => {
    authListener.subscription.unsubscribe();
  };
}, []);

Phone Authentication

Supabase also supports phone authentication using SMS one-time passwords (OTP):

// Request OTP
const requestOTP = async (phone) => {
  const { data, error } = await supabase.auth.signInWithOtp({
    phone,
  });
  
  if (error) {
    console.error('Error requesting OTP:', error.message);
    return false;
  }
  
  return true;
};

// Verify OTP
const verifyOTP = async (phone, token) => {
  const { data, error } = await supabase.auth.verifyOtp({
    phone,
    token,
    type: 'sms',
  });
  
  if (error) {
    console.error('Error verifying OTP:', error.message);
    return null;
  }
  
  return data;
};

Multi-Factor Authentication (MFA)

For applications requiring higher security, Supabase supports multi-factor authentication:

// Enroll factor (e.g., TOTP for authenticator apps)
const enrollFactor = async () => {
  const { data, error } = await supabase.auth.mfa.enroll({
    factorType: 'totp',
  });
  
  if (error) {
    console.error('Error enrolling MFA factor:', error.message);
    return null;
  }
  
  // data.totp.qr_code contains the QR code to scan with authenticator app
  // data.totp.secret contains the secret key
  return data;
};

// Verify factor
const verifyFactor = async (factorId, challengeId, code) => {
  const { data, error } = await supabase.auth.mfa.challenge({
    factorId,
    challengeId,
    code,
  });
  
  if (error) {
    console.error('Error verifying factor:', error.message);
    return false;
  }
  
  return true;
};

// Unenroll factor
const unenrollFactor = async (factorId) => {
  const { data, error } = await supabase.auth.mfa.unenroll({
    factorId,
  });
  
  if (error) {
    console.error('Error unenrolling factor:', error.message);
    return false;
  }
  
  return true;
};

Custom Authentication Flow with JWT

For more complex authentication scenarios, you can implement custom JWT authentication:

// Verify a custom JWT token
const verifyToken = async (token) => {
  const { data, error } = await supabase.auth.verifyOtp({
    token_hash: token,
    type: 'jwt',
  });
  
  if (error) {
    console.error('Error verifying token:', error.message);
    return null;
  }
  
  return data;
};

Managing User Sessions

Supabase provides methods to manage user sessions:

// Get the current session
const getCurrentSession = async () => {
  const { data: { session } } = await supabase.auth.getSession();
  return session;
};

// Get the current user
const getCurrentUser = async () => {
  const { data: { user } } = await supabase.auth.getUser();
  return user;
};

// Refresh session
const refreshSession = async () => {
  const { data, error } = await supabase.auth.refreshSession();
  
  if (error) {
    console.error('Error refreshing session:', error.message);
    return null;
  }
  
  return data;
};

// Sign out
const signOut = async () => {
  const { error } = await supabase.auth.signOut();
  
  if (error) {
    console.error('Error signing out:', error.message);
    return false;
  }
  
  return true;
};

Implementing Auth State Listeners

To keep your UI in sync with the authentication state, use auth state listeners:

// In a React component
useEffect(() => {
  const { data: authListener } = supabase.auth.onAuthStateChange(
    (event, session) => {
      switch (event) {
        case 'SIGNED_IN':
          // Handle sign in
          setUser(session?.user || null);
          break;
        case 'SIGNED_OUT':
          // Handle sign out
          setUser(null);
          break;
        case 'USER_UPDATED':
          // Handle user update
          setUser(session?.user || null);
          break;
        case 'PASSWORD_RECOVERY':
          // Handle password recovery
          navigate('/reset-password');
          break;
        default:
          break;
      }
    }
  );

  return () => {
    authListener.subscription.unsubscribe();
  };
}, []);

Securing Your Application with Row Level Security

Authentication is only part of the security equation. To ensure users can only access their own data, implement Row Level Security (RLS) policies:

-- Enable RLS on a table
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- Create a policy that allows users to read only their own profile
CREATE POLICY "Users can view their own profile" 
  ON profiles FOR SELECT 
  USING (auth.uid() = user_id);

-- Create a policy that allows users to update only their own profile
CREATE POLICY "Users can update their own profile" 
  ON profiles FOR UPDATE 
  USING (auth.uid() = user_id);

Best Practices for Supabase Authentication

  1. Always use HTTPS: Ensure your application uses HTTPS to prevent man-in-the-middle attacks.

  2. Implement proper error handling: Provide user-friendly error messages without revealing sensitive information.

  3. Set appropriate token expiration: Configure session timeouts based on your security requirements.

  4. Use refresh tokens: Implement token refresh to maintain user sessions securely.

  5. Validate user input: Always validate and sanitize user input to prevent injection attacks.

  6. Implement rate limiting: Protect against brute force attacks by limiting authentication attempts.

  7. Consider MFA for sensitive applications: For applications handling sensitive data, implement multi-factor authentication.

  8. Regularly audit user accounts: Periodically review user accounts and permissions.

Conclusion

Supabase provides a comprehensive authentication system that supports various strategies to meet different application needs. Whether you need simple email/password authentication, passwordless magic links, social logins, or multi-factor authentication, Supabase has you covered.

By leveraging these authentication methods and following security best practices, you can create secure, user-friendly authentication experiences for your applications. The integration with Supabase's Row Level Security ensures that your data remains protected while providing a seamless user experience.

As you build your next application with Supabase, consider which authentication strategy best fits your users' needs and security requirements. The right approach can significantly enhance both security and user experience, contributing to the overall success of your application.