Supabase Auth is a fully managed authentication service built on top of GoTrue. It handles user storage, session management, and provider integrations — with pre-built UI components and tight Next.js integration. This guide covers setup for the three most common patterns: email magic links, social login, and JWT session handling.

Installation

npm install @supabase/supabase-js @supabase/ssr
// lib/supabase/server.ts — server-side client (App Router)
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: (cookies) => cookies.forEach(({ name, value, options }) =>
          cookieStore.set(name, value, options)
        ),
      },
    }
  );
}
// app/api/auth/magic-link/route.ts
import { createClient } from '@/lib/supabase/server';
 
export async function POST(req: Request) {
  const { email } = await req.json();
  const supabase = createClient();
 
  const { error } = await supabase.auth.signInWithOtp({
    email,
    options: {
      emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
    },
  });
 
  if (error) return Response.json({ error: error.message }, { status: 400 });
  return Response.json({ message: 'Check your email' });
}
// app/auth/callback/route.ts — handles the magic link redirect
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
 
export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get('code');
 
  if (code) {
    const supabase = createClient();
    await supabase.auth.exchangeCodeForSession(code);
  }
 
  return NextResponse.redirect(`${origin}/dashboard`);
}

Recipe 2: Social Login (Google, GitHub)

// Client component — sign in button
'use client';
import { createBrowserClient } from '@supabase/ssr';
 
export function SignInWithGoogle() {
  const supabase = createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
 
  const handleSignIn = async () => {
    await supabase.auth.signInWithOAuth({
      provider: 'google',
      options: {
        redirectTo: `${location.origin}/auth/callback`,
      },
    });
  };
 
  return <button onClick={handleSignIn}>Sign in with Google</button>;
}
Enable each social provider in the Supabase Dashboard under Authentication > Providers. For Google, you need a Google Cloud OAuth 2.0 client ID and secret. For GitHub, create a GitHub OAuth App and paste the credentials into Supabase.

Recipe 3: Protecting Routes with Middleware

// middleware.ts — redirect unauthenticated users
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: (c) => c.forEach(({name,value,options}) =>
                   response.cookies.set(name, value, options)) } }
  );
 
  const { data: { user } } = await supabase.auth.getUser();
 
  if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
 
  return response;
}
 
export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};

Reading the Session Server-Side

// app/dashboard/page.tsx — server component
import { createClient } from '@/lib/supabase/server';
import { redirect } from 'next/navigation';
 
export default async function Dashboard() {
  const supabase = createClient();
  const { data: { user } } = await supabase.auth.getUser();
 
  if (!user) redirect('/login');
 
  return <div>Hello {user.email}</div>;
}
Always use supabase.auth.getUser() server-side — not getSession(). getUser() makes a network request to verify the token with Supabase's servers; getSession() only reads the local cookie and can be spoofed.
Metadata Value
Title Supabase Auth Setup: Social Login, Magic Links, and JWT Sessions in Next.js
Tool Supabase
Primary SEO keyword supabase auth next.js
Secondary keywords supabase magic link, supabase social login, supabase middleware next.js, supabase session
Estimated read time 9 minutes
Research date 2026-04-14