Supabase Login: Username & Password Authentication Guide

by Faj Lennon 57 views

Hey folks! Ever wondered how to set up Supabase login with a username and password? You're in the right place! Supabase is a fantastic open-source Firebase alternative that provides you with all the tools you need to build scalable and secure applications. One of the most common features you'll want to implement is user authentication. So, let's dive deep into how you can implement username and password authentication using Supabase. This comprehensive guide will walk you through each step, ensuring you have a solid understanding of the process.

Setting Up Your Supabase Project

First things first, you need a Supabase project. If you haven't already, head over to the Supabase website and create an account. Once you're logged in, you can create a new project. Give it a cool name and choose a region that's closest to your users for better latency. After creating your project, Supabase will provide you with a unique URL and an API key. Keep these handy; you'll need them later.

Once your project is set up, navigate to the Authentication section in the Supabase dashboard. Here, you'll find various authentication providers, including email/password, OAuth providers like Google and GitHub, and more. For our purpose, we'll focus on the email/password provider, which we'll adapt to use usernames instead of emails. Make sure that the email/password authentication is enabled. This is usually enabled by default, but it’s always a good idea to double-check. With email/password authentication enabled, Supabase provides you with built-in functions to handle user registration, login, and password resets. We’ll leverage these functions to create a custom username-based authentication system. This involves modifying the default behavior slightly, but the underlying principles remain the same. Supabase also offers row-level security, which allows you to define granular access control policies for your data. This ensures that users can only access and modify data that they are authorized to. By combining authentication with row-level security, you can build highly secure applications with Supabase.

Database Setup for Usernames

Now, let’s modify our database to store usernames. Supabase uses PostgreSQL, so you can easily create tables and define schemas. We’ll create a new table called users to store user information. This table will include columns for id, username, email, password, and any other relevant user data you want to store. Make sure to set the id column as the primary key and generate it using a UUID (Universally Unique Identifier) for better security and scalability.

Here’s an example of how you can create the users table using SQL in the Supabase dashboard:

CREATE TABLE users (
 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
 username VARCHAR(255) UNIQUE NOT NULL,
 email VARCHAR(255) UNIQUE NOT NULL,
 password VARCHAR(255) NOT NULL,
 created_at TIMESTAMPTZ DEFAULT NOW()
);

In this SQL code:

  • id is a UUID generated by the uuid_generate_v4() function, acting as the primary key.
  • username and email are unique to ensure no duplicates.
  • password stores the user's password (which we'll hash later for security).
  • created_at is a timestamp that records when the user was created.

It's crucial to add the UNIQUE constraint to both the username and email columns. This ensures that each user has a unique username and email address, preventing duplicate accounts and potential security vulnerabilities. Also, consider adding more fields to the users table based on your application's requirements. For example, you might want to include fields for first_name, last_name, profile_picture, or any other user-specific information.

Implementing User Registration

Alright, let's get into the code! We'll start with user registration. Since Supabase's built-in auth uses email, we'll create a custom function to handle username registration. This involves inserting the new user's data into our users table and then using Supabase's auth.api.createUser to create the user in the auth system.

Here’s a JavaScript example using the Supabase client library:

import { createClient } from '@supabase/supabase-js';
import bcrypt from 'bcryptjs';

const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseKey = 'YOUR_SUPABASE_ANON_KEY';
const supabase = createClient(supabaseUrl, supabaseKey);

async function registerUser(username, email, password) {
 try {
 // Hash the password
 const salt = await bcrypt.genSalt(10);
 const hashedPassword = await bcrypt.hash(password, salt);

 // Insert the user into the users table
 const { data, error } = await supabase
 .from('users')
 .insert([
 { username, email, password: hashedPassword },
 ]);

 if (error) {
 console.error('Error inserting user:', error);
 return { error: error.message };
 }

 // Create the user in Supabase auth (optional, if needed for other auth features)
 const { data: authData, error: authError } = await supabase.auth.signUp({
 email: email,
 password: password,
 });

 if (authError) {
 console.error('Error creating user in auth:', authError);
 // Optionally, remove the user from the users table if auth fails
 await supabase.from('users').delete().eq('id', data[0].id);
 return { error: authError.message };
 }

 return { data };
 } catch (error) {
 console.error('Registration failed:', error);
 return { error: error.message };
 }
}

export default registerUser;

In this code snippet:

  • We use bcryptjs to hash the password before storing it in the database. Never store passwords in plain text!
  • We insert the username, email, and hashed password into the users table.
  • We also use supabase.auth.signUp to create a user in Supabase's authentication system. This is optional but can be useful if you want to leverage other Supabase auth features, such as password resets or social logins.

Remember to replace 'YOUR_SUPABASE_URL' and 'YOUR_SUPABASE_ANON_KEY' with your actual Supabase URL and API key. Additionally, ensure you have installed the necessary dependencies by running npm install @supabase/supabase-js bcryptjs.

Implementing User Login

Next up, let's implement user login. This involves querying the users table for the provided username, verifying the password, and then creating a Supabase session for the user.

Here’s a JavaScript example:

import { createClient } from '@supabase/supabase-js';
import bcrypt from 'bcryptjs';

const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseKey = 'YOUR_SUPABASE_ANON_KEY';
const supabase = createClient(supabaseUrl, supabaseKey);

async function loginUser(username, password) {
 try {
 // Retrieve the user from the users table by username
 const { data, error } = await supabase
 .from('users')
 .select('*')
 .eq('username', username)
 .single();

 if (error) {
 console.error('Error retrieving user:', error);
 return { error: 'Invalid username or password' };
 }

 if (!data) {
 return { error: 'Invalid username or password' };
 }

 // Verify the password
 const passwordMatch = await bcrypt.compare(password, data.password);

 if (!passwordMatch) {
 return { error: 'Invalid username or password' };
 }

 // Create a Supabase session for the user
 const { data: sessionData, error: sessionError } = await supabase.auth.signInWithPassword({
 email: data.email,
 password: password,
 });

 if (sessionError) {
 console.error('Error creating session:', sessionError);
 return { error: sessionError.message };
 }

 return { data: sessionData };
 } catch (error) {
 console.error('Login failed:', error);
 return { error: error.message };
 }
}

export default loginUser;

In this code snippet:

  • We query the users table for the user with the provided username.
  • We use bcrypt.compare to verify that the provided password matches the hashed password stored in the database.
  • If the username and password are correct, we use supabase.auth.signInWithPassword to create a Supabase session for the user. Note that we use the user's email address here, as signInWithPassword requires an email.

Make sure to handle errors appropriately and provide informative error messages to the user. For example, if the username is not found, or if the password does not match, return a generic error message like "Invalid username or password" to avoid leaking information about your database.

Enhancing Security

Security is paramount when dealing with user authentication. Here are a few best practices to keep in mind:

  • Password Hashing: Always hash passwords before storing them in the database. Use a strong hashing algorithm like bcrypt or Argon2.
  • Input Validation: Validate all user inputs to prevent SQL injection and other security vulnerabilities.
  • Rate Limiting: Implement rate limiting to prevent brute-force attacks.
  • Row-Level Security (RLS): Use Supabase's RLS to control access to data based on user roles and permissions.
  • Regular Updates: Keep your dependencies up to date to patch any known security vulnerabilities.

By following these best practices, you can significantly improve the security of your authentication system and protect your users' data.

Conclusion

And there you have it! Implementing Supabase login with a username and password involves setting up your database, creating custom registration and login functions, and enhancing security. By following this guide, you can create a secure and scalable authentication system for your Supabase applications. Remember to always prioritize security and stay up-to-date with the latest best practices. Happy coding, and feel free to reach out if you have any questions!