Skill
auth
Supabase authentication flows in Next.js App Router. Use when building signup, login, logout, password reset, OAuth, or protected routes.
From saas-toolkitInstall
1
Run in your terminal$
npx claudepluginhub mickaelmamani/saas-toolkit --plugin saas-toolkitTool Access
This skill is limited to using the following tools:
ReadWriteEditGrepGlobBashWebSearchWebFetchmcp__supabase
Skill Content
/auth — Supabase Auth for Next.js
Implement Supabase authentication for Next.js App Router using @supabase/ssr.
Setup
1. Install dependencies
npm install @supabase/supabase-js @supabase/ssr
2. Environment variables
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
3. Create Supabase clients
Server client (lib/supabase/server.ts):
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll(); },
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
},
},
}
);
}
Browser client (lib/supabase/client.ts):
import { createBrowserClient } from '@supabase/ssr';
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
Middleware client (lib/supabase/middleware.ts):
import { createServerClient } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return request.cookies.getAll(); },
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value }) =>
request.cookies.set(name, value)
);
supabaseResponse = NextResponse.next({ request });
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options)
);
},
},
}
);
const { data: { user } } = await supabase.auth.getUser();
if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
const url = request.nextUrl.clone();
url.pathname = '/login';
return NextResponse.redirect(url);
}
return supabaseResponse;
}
4. Middleware auth guard
middleware.ts (project root):
import { updateSession } from '@/lib/supabase/middleware';
import type { NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};
5. Protected route layout
app/(protected)/layout.tsx:
import { createClient } from '@/lib/supabase/server';
import { redirect } from 'next/navigation';
export default async function ProtectedLayout({ children }) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect('/login');
return <>{children}</>;
}
Auth Flows
OAuth callback
app/auth/callback/route.ts:
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');
const next = searchParams.get('next') ?? '/dashboard';
if (code) {
const supabase = await createClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) return NextResponse.redirect(`${origin}${next}`);
}
return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}
Email/password signup (Server Action)
'use server';
import { createClient } from '@/lib/supabase/server';
import { redirect } from 'next/navigation';
export async function signup(formData: FormData) {
const supabase = await createClient();
const { error } = await supabase.auth.signUp({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) return { error: error.message };
redirect('/check-email');
}
Password reset
'use server';
import { createClient } from '@/lib/supabase/server';
export async function resetPassword(formData: FormData) {
const supabase = await createClient();
const { error } = await supabase.auth.resetPasswordForEmail(
formData.get('email') as string,
{ redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback?next=/update-password` }
);
if (error) return { error: error.message };
return { success: true };
}
Session management
- Sessions are automatically refreshed by the middleware on every request
- Always use
getUser()(notgetSession()) for auth checks —getUser()validates the JWT server-side - Sign out:
await supabase.auth.signOut()thenrouter.refresh()
Rules
- NEVER use
createClientfrom@supabase/supabase-jsdirectly — always use@supabase/ssr - NEVER use
getSession()for authorization checks — usegetUser() - Always implement middleware for session refresh
- Protect routes at the layout level, not per-page
- Use Server Actions for auth mutations (signup, login, reset)
Similar Skills
Stats
Parent Repo Stars0
Parent Repo Forks0
Last CommitFeb 11, 2026