Skip to main content
Supabase Auth provides open-source authentication that integrates seamlessly with your PostgreSQL database.

Installation

npm install @mastra/auth-supabase

Configuration

1

Create Supabase Project

Sign up at Supabase and create a project. Get your:
  • Project URL
  • Anon/Public Key
2

Set Environment Variables

.env
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
3

Import and Configure

import { MastraAuthSupabase } from '@mastra/auth-supabase';
import { Mastra } from '@mastra/core';

const auth = new MastraAuthSupabase();

const mastra = new Mastra({
  server: {
    auth,
  },
});

Configuration Options

url
string
required
Supabase project URL. Defaults to process.env.SUPABASE_URL
anonKey
string
required
Supabase anon/public key. Defaults to process.env.SUPABASE_ANON_KEY
name
string
default:"supabase"
Provider name identifier

Usage Examples

Basic Setup

import { Mastra } from '@mastra/core';
import { MastraAuthSupabase } from '@mastra/auth-supabase';
import { MastraServer } from '@mastra/hono';
import { Hono } from 'hono';

const mastra = new Mastra({
  server: {
    auth: new MastraAuthSupabase(),
  },
});

const app = new Hono();
const server = new MastraServer({ mastra, app });

Explicit Configuration

const auth = new MastraAuthSupabase({
  url: 'https://your-project.supabase.co',
  anonKey: 'your-anon-key',
  name: 'supabase-auth',
});

const mastra = new Mastra({
  server: {
    auth,
  },
});

Custom Authorization with Database

const auth = new MastraAuthSupabase({
  authorizeUser: async (user) => {
    // Query Supabase database for user permissions
    const { data, error } = await this.supabase
      .from('users')
      .select('isAdmin, isActive')
      .eq('id', user.id)
      .single();

    if (error || !data) {
      return false;
    }

    return data.isActive && data.isAdmin;
  },
});

const mastra = new Mastra({
  server: {
    auth,
  },
});

Client Integration

React with Supabase

import { createClient } from '@supabase/supabase-js';
import { useEffect, useState } from 'react';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export default function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Get current 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();
  }, []);

  if (!user) {
    return <AuthComponent />;
  }

  return (
    <div>
      <p>Welcome {user.email}</p>
      <button onClick={() => supabase.auth.signOut()}>Sign out</button>
      <MyMastraComponent />
    </div>
  );
}

Making Authenticated Requests

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

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

async function callMastraAPI() {
  const { data: { session } } = await supabase.auth.getSession();
  
  if (!session) {
    throw new Error('Not authenticated');
  }

  const response = await fetch('http://localhost:4111/api/mastra/agents', {
    headers: {
      'Authorization': `Bearer ${session.access_token}`,
    },
  });

  const data = await response.json();
  return data;
}

Authentication Methods

Email/Password

const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'secure-password',
});
const { data, error } = await supabase.auth.signInWithOtp({
  email: '[email protected]',
});

Social OAuth

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

Phone/SMS

const { data, error } = await supabase.auth.signInWithOtp({
  phone: '+1234567890',
});

User Object

The authenticated user object:
interface SupabaseUser {
  id: string; // UUID
  email?: string;
  phone?: string;
  email_confirmed_at?: string;
  phone_confirmed_at?: string;
  created_at: string;
  updated_at: string;
  user_metadata: Record<string, any>;
  app_metadata: Record<string, any>;
}

Database Integration

User Profiles Table

Create a profiles table that extends auth.users:
-- Create profiles table
create table public.users (
  id uuid references auth.users on delete cascade primary key,
  email text,
  full_name text,
  isAdmin boolean default false,
  isActive boolean default true,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null
);

-- Set up Row Level Security
alter table public.users enable row level security;

-- Policy: Users can view their own profile
create policy "Users can view own profile"
  on public.users for select
  using ( auth.uid() = id );

-- Function to handle new user creation
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
  insert into public.users (id, email, full_name)
  values (new.id, new.email, new.raw_user_meta_data->>'full_name');
  return new;
end;
$$;

-- Trigger to create profile on signup
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();

Authorization with RLS

const auth = new MastraAuthSupabase({
  authorizeUser: async (user) => {
    // Supabase RLS will automatically filter based on user.id
    const { data, error } = await this.supabase
      .from('users')
      .select('isAdmin, isActive')
      .eq('id', user.id)
      .single();

    return !error && data?.isActive === true;
  },
});

Authentication Flow

Best Practices

Enable Row Level Security

Always enable RLS on tables to enforce access control at the database level.

Use Anon Key on Client

Only use the anon/public key on the client side. Service role key should only be used server-side.

Profile Table Sync

Use triggers to keep profile tables in sync with auth.users.

Email Verification

Enable email confirmation in Supabase Dashboard for production.

Supabase Features

Built-in Features

  • Email/password authentication
  • Magic link authentication
  • Social OAuth (Google, GitHub, etc.)
  • Phone/SMS authentication
  • Multi-factor authentication
  • Row Level Security
  • Real-time subscriptions

Database Integration

  • PostgreSQL database included
  • Automatic user management
  • Built-in auth schema
  • Row Level Security policies
  • Custom user metadata

Troubleshooting

Token Verification Failed

Check that:
  1. Token is not expired (default: 1 hour)
  2. Anon key matches your Supabase project
  3. User is not deleted or disabled

RLS Policies

If database queries fail, verify RLS policies:
-- Check existing policies
select * from pg_policies where tablename = 'users';

Email Not Sending

Configure SMTP settings in Supabase Dashboard > Authentication > Email Templates.

Clerk

Managed authentication alternative

PostgreSQL Storage

Use PostgreSQL for storage

Supabase Docs

Official Supabase Auth documentation

Supabase Dashboard

Manage projects and users

Build docs developers (and LLMs) love