Supabase Auth provides open-source authentication that integrates seamlessly with your PostgreSQL database.
Installation
npm install @mastra/auth-supabase
Configuration
Create Supabase Project
Sign up at Supabase and create a project. Get your:
Project URL
Anon/Public Key
Set Environment Variables
SUPABASE_URL = https://your-project.supabase.co
SUPABASE_ANON_KEY = your-anon-key
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
Supabase project URL. Defaults to process.env.SUPABASE_URL
Supabase anon/public key. Defaults to process.env.SUPABASE_ANON_KEY
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' ,
});
Magic Link
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:
Token is not expired (default: 1 hour)
Anon key matches your Supabase project
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