Supabase Auth signs people in with email and password, one-time magic links, phone codes or OAuth providers such as GitHub, all through the supabase.auth object in supabase-js. Each sign-in yields a session whose token reaches your database, where row level security policies compare auth.uid() with a user_id column to decide what that person may read.
| Method | supabase-js v2 call | Good for |
|---|---|---|
| Email and password | auth.signUp / auth.signInWithPassword | Classic accounts |
| Magic link | auth.signInWithOtp({ email }) | No passwords to forget |
| SMS or WhatsApp code | auth.signInWithOtp({ phone }) | Mobile-first products |
| OAuth provider | auth.signInWithOAuth({ provider }) | One-click sign-in with an existing account |
| Sign out | auth.signOut() | Ending the session |
Updated
Auth is the part of a Supabase project people most often wire up wrong: the sign-in works, but the data is wide open or the server trusts a cookie it should not. Pick a method below to get its code, then follow the policies section so each person only sees their own rows.
Create the account, then sign in. With confirmation on, data.session is null until the email link is clicked.
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const { data, error } = await supabase.auth.signUp({
email: "[email protected]",
password: "a-long-unique-passphrase",
options: { emailRedirectTo: "https://example.com/welcome" },
});
const { data: signedIn, error: signInError } = await supabase.auth.signInWithPassword({
email: "[email protected]",
password: "a-long-unique-passphrase",
});Run in the SQL editor. The default fills user_id from the token, and with check stops anyone inserting rows for another user.
create table public.notes (
id bigint generated always as identity primary key,
user_id uuid not null default auth.uid() references auth.users (id) on delete cascade,
body text not null
);
alter table public.notes enable row level security;
create policy "read own notes" on public.notes
for select to authenticated
using ((select auth.uid()) = user_id);
create policy "add own notes" on public.notes
for insert to authenticated
with check ((select auth.uid()) = user_id);
create policy "edit own notes" on public.notes
for update to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "delete own notes" on public.notes
for delete to authenticated
using ((select auth.uid()) = user_id);
After sign-in, supabase-js stores a short-lived access token and a refresh token, and refreshes the access token automatically. Every query you make through the same client sends the access token, which is how Postgres learns the user's ID.
In server-rendered apps the session lives in cookies instead. The Next.js authentication guide on this site covers that wiring, so this page stays with the client library and the database rules.
Hiding a button in the interface does not protect data, since anyone can call your API with the public key. Policies run inside Postgres on every request, which makes them the place where ownership is enforced.
Write one policy per action. A select policy uses using, while insert needs with check so people cannot create rows owned by someone else, and update usually needs both.
Both start from signInWithOtp. For email, the message carries a link when the template uses the confirmation URL and a code when it uses the token. Phone sign-in only supports codes, by SMS or WhatsApp.
Enable the provider in the dashboard with the client ID and secret from that provider, then call signInWithOAuth with its name and an optional redirectTo address. The person returns to your site already signed in.
Register onAuthStateChange. It reports events including INITIAL_SESSION, SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, USER_UPDATED and PASSWORD_RECOVERY, and returns a subscription you unsubscribe from when the component unmounts.
Once row level security is on, a table denies everything until a policy allows it. Add a select policy for the authenticated role that compares (select auth.uid()) with the owner column.
Both cover the common methods. The bigger difference is the database underneath; our Supabase vs Firebase comparison goes through that choice in detail.
Related: Supabase vs Firebase, Next.js authentication, Authentication basics, Accept payments on a website.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.