From cklph-nextjs
Peter's Supabase auth flows for Next.js — sign-in, OAuth callback, password reset, multi-tenant gating. Use when wiring sign-up/sign-in, the OAuth callback route, password reset, or any redirect-if-unauthenticated logic.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cklph-nextjs:authThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`security` covers per-mutation auth (`getUser()`, rate-limit, CSRF). This skill is the surrounding flow — how users get a session and where auth gates live.
security covers per-mutation auth (getUser(), rate-limit, CSRF). This skill is the surrounding flow — how users get a session and where auth gates live.
await supabase.auth.getUser(). It validates the token with the auth server and refreshes as a side effect.useUser() hook for reads. For data fetches, go through authFetcher from data-layer — it owns the 401 → refresh → retry path.Never getSession() on the server. It reads a cookie without validating it. Stale tokens slip through, RLS becomes a lie.
OAuth providers redirect to app/auth/callback/route.ts, which exchanges the code for a session and redirects.
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 createServerSupabaseClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) return NextResponse.redirect(`${origin}${next}`);
}
return NextResponse.redirect(`${origin}/auth/error`);
}
PKCE is handled by @supabase/ssr. The exchange must run server-side (HttpOnly cookie).
Email+password (signUp → signInWithPassword), magic link (signInWithOtp), OAuth (signInWithOAuth) — all three land in app/auth/callback/route.ts.
Two pages: Request — resetPasswordForEmail(email, { redirectTo }). Reset — user arrives with a recovery session, call updateUser({ password }). After success, call signOut({ scope: "others" }) to invalidate other sessions. A reset that leaves stolen sessions alive is a half-fix.
RLS predicates include both auth.uid() AND organization_id. Verify membership in the service layer too — RLS is the net, not the gate. Store current_organization_id in a cookie or user metadata; never trust the request body.
middleware.ts or app/(authed)/layout.tsx. One boundary check.requireAuth (see api-routes, security).getSession() server-side; auth checks scattered in leaf components.auth.uid() predicate.organization_id from the request body.Pair with security for the mutation checklist, data-layer for authFetcher's 401 handling, api-routes for requireAuth.
npx claudepluginhub chykalophia/cklph-marketplace --plugin cklph-nextjsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.