Complete reference for server-side authentication in Supabase: client types, user retrieval methods, cookie handling, and middleware patterns.
Complete reference for server-side authentication in Supabase: client types, user retrieval methods, cookie handling, and middleware patterns.
BeforeMerge offers hundreds of code review rules, guides, and detection patterns to help your team ship better code.
Server-side auth in Supabase requires careful handling of cookies, client types, and session management.
Uses the user's session cookie. All queries are filtered through RLS.
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export function createClient() {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => cookieStore.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
},
},
}
);
}Bypasses RLS. Use only for server-side mutations where you've already verified authorization.
import { createClient } from "@supabase/supabase-js";
export function createAdminClient() {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{ auth: { persistSession: false } }
);
}Never expose SUPABASE_SERVICE_ROLE_KEY to the client.
| Method | Validates token? | Use case |
|---|---|---|
getUser() |
Yes (hits Supabase auth server) | Server Components, Server Actions, Middleware |
getSession() |
No (reads JWT locally) | When you only need claims and trust the cookie |
Always use getUser() for authorization decisions. getSession() does not verify the JWT signature server-side and can be spoofed.
const { data: { user }, error } = await supabase.auth.getUser();
if (!user) redirect("/login");Refresh the session on every request to prevent expiration:
// middleware.ts
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
let response = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => request.cookies.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) => {
request.cookies.set(name, value);
response.cookies.set(name, value, options);
});
},
},
}
);
await supabase.auth.getUser();
return response;
}Key cookie options for production:
httpOnly: true — prevents JavaScript accesssecure: true — HTTPS onlysameSite: "lax" — CSRF protection while allowing redirectspath: "/" — available across the entire sitemaxAge: 60 * 60 * 24 * 7 — 7-day session lifetime