Supabase Server-Side Auth Reference
Server-side auth in Supabase requires careful handling of cookies, client types, and session management.
Client Types
createClient (Authenticated)
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)
);
},
},
}
);
}
createAdminClient (Service Role)
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.
getUser() vs getSession()
| 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");
Middleware Pattern
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;
}
Cookie Configuration
Key cookie options for production:
httpOnly: true — prevents JavaScript access
secure: true — HTTPS only
sameSite: "lax" — CSRF protection while allowing redirects
path: "/" — available across the entire site
maxAge: 60 * 60 * 24 * 7 — 7-day session lifetime