← Back to Tutorials Chapter 4

Authentication

Supabase Auth System Overview

Supabase Auth is built on GoTrue, a SWT (Self-contained Web Token) authentication server written in Go. It handles user sign-up, sign-in, session management, token refresh, and social logins. User records are stored in the auth.users table within your PostgreSQL database, and the system generates JWT tokens that include user identity claims.

The auth system integrates deeply with Row Level Security. When a user authenticates, Supabase sets the auth.uid() and auth.jwt() context in PostgreSQL, allowing your RLS policies to reference the current user directly. This means you can write policies like "users can only see their own data" without managing session state manually.

JWT Token Contents
When a user signs in, Supabase returns a JWT access token. This token contains the user's ID, email, role (authenticated or anon), and any custom claims you have added via user metadata. You can decode the JWT at jwt.io to inspect its payload. The token is sent with every API request in the Authorization header, allowing PostgREST to identify the user and enforce RLS.

Email / Password Authentication

Email and password auth is the most common authentication method. Supabase handles password hashing with bcrypt and provides methods for sign-up, sign-in, and sign-out.

Sign Up

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
})

The signUp method creates a new user in the auth.users table. By default, Supabase sends a confirmation email to verify the address. The user is created immediately but cannot sign in until they confirm their email. You can disable email confirmation in the Auth settings of the dashboard for development purposes.

Email Confirmation Default
By default, Supabase requires email confirmation. New users receive a verification email with a link. The user's email_confirmed_at field stays null until they click the link. For development, you can disable this requirement in Authentication > Settings > Disable email confirmation. In production, keep it enabled for security.

Sign In

const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password-123',
})

On successful sign-in, Supabase returns a session object containing an access token, refresh token, and user information. The client automatically stores the session and attaches the access token to subsequent API requests.

Sign Out

const { error } = await supabase.auth.signOut()

Calling signOut clears the local session and invalidates the refresh token on the server. After sign-out, subsequent API calls use the anon role with RLS restricted to public policies only.

Session Management

Supabase handles session persistence and token refresh automatically. When a user refreshes the page, the client restores the session from local storage. The access token expires after one hour, and the client auto-refreshes it using the refresh token before it expires.

const { data: { session } } = await supabase.auth.getSession()

supabase.auth.onAuthStateChange((event, session) => {
  console.log('Auth event:', event, 'Session:', session)
})

The onAuthStateChange listener fires on sign-in, sign-out, token refresh, and password recovery. Use it to update your UI when authentication state changes. The getSession method retrieves the current session, useful for checking auth status on page load.

Social Login Providers

Supabase supports over 50 OAuth providers including Google, GitHub, Twitter, Discord, and Microsoft. Configure providers in the Authentication > Providers section of the dashboard.

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
})

This redirects the user to GitHub for authorization. After successful authentication, GitHub redirects back to your site with a callback URL containing the auth code. Supabase exchanges this code for a session automatically.

Protected Routes Pattern

A common pattern is to protect pages by checking the session and redirecting unauthenticated users:

async function protectPage() {
  const { data: { session } } = await supabase.auth.getSession()
  if (!session) {
    window.location.href = '/login.html'
    return
  }
  document.getElementById('user-email').textContent = session.user.email
}

protectPage()

User Metadata

You can store additional user data in user_metadata during sign-up:

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
  options: {
    data: {
      full_name: 'Jane Doe',
      avatar_url: 'https://example.com/avatar.jpg'
    }
  }
})

Metadata is stored in the JWT token and accessible via session.user.user_metadata. Use it for display names, preferences, or profile data that does not need its own database table.

Auth UI Component

Supabase offers an Auth UI component that renders ready-made login forms for email/password and social providers. Install it with npm install @supabase/auth-ui-react @supabase/auth-ui-shared for React projects, or use the pre-built HTML forms from the Supabase documentation.

RLS and Auth Integration

The real power of Supabase Auth is its integration with Row Level Security. Within RLS policies, you can reference the authenticated user:

-- Allow users to read only their own todos
CREATE POLICY "Users can read own todos"
ON todos FOR SELECT
USING (auth.uid() = user_id);

-- Read user email via JWT
CREATE POLICY "Insert with user email"
ON profiles FOR INSERT
WITH CHECK (auth.jwt() ->> 'email' = email);

The auth.uid() function returns the user's UUID from the JWT. The auth.jwt() function returns the entire JWT payload as JSON. These functions are available inside any RLS policy and are automatically populated with the current user's information when they send a valid access token.

Exercise
Build a login/signup page with email and password. Create two HTML files: login.html with sign-in and sign-up forms, and profile.html that shows the user email after login. Use onAuthStateChange to redirect the user after sign-in. Use getSession to check if the user is already logged in when profile.html loads. Implement sign-out with a button. Test by creating a new account and signing in.