Marketplace auth
Authentication and access control skill for Next.js 15 + Supabase applications. Use when implementing user authentication, protecting routes, managing user sessions, enforcing role-based access control (admin/member), or working with multi-tenant family-based data isolation. Covers login/logout, registration with email verification, OAuth (GitHub), route protection for Server Components and Server Actions, admin-only features, and multi-tenant data access patterns.
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/bom-98/auth" ~/.claude/skills/aiskillstore-marketplace-auth-f1d158 && rm -rf "$T"
skills/bom-98/auth/SKILL.mdAuthentication & Access Control
This skill provides workflows for implementing authentication and access control in this Next.js 15 + Supabase application using server-side auth with httpOnly cookies, hybrid route protection, and multi-tenant family-based data isolation.
System Overview
- Auth Provider: Supabase Auth with httpOnly cookies
- Architecture: Next.js 15 App Router with Server Components and Server Actions
- Route Protection: Hybrid approach (page-level auth checks, not middleware-only)
- Multi-Tenancy: Family-based data isolation with RLS policies
- Roles: Admin (first user in family) and Member
Core Workflows
Protecting a New Route
To protect a route from unauthenticated users:
- Import
fromrequireAuthRedirect@/lib/auth/server-auth - Call
at the start of the componentawait requireAuthRedirect() - User will be redirected to
if not authenticated/login
import { requireAuthRedirect } from '@/lib/auth/server-auth'; export default async function ProtectedPage() { await requireAuthRedirect(); // User guaranteed authenticated here return <YourContent />; }
To protect an entire route group, add this to the layout component. All child routes will inherit the protection.
Protecting a Server Action
To require authentication in a Server Action:
- Import
fromrequireAuth@/lib/auth/server-auth - Call
at the start of the actionconst user = await requireAuth() - Action will throw
if user not authenticatedUnauthorizedError
'use server'; import { requireAuth } from '@/lib/auth/server-auth'; export async function myAction() { const user = await requireAuth(); // Proceed with authenticated action }
Getting Current User Data
- Auth user (email, id), returns null if not logged ingetCurrentUser()
- Extended profile (role, familyId, firstName, lastName, active)getUserData()
- Just the family IDgetCurrentFamilyId()
All use React
cache() - multiple calls in same request return cached value.
Implementing Admin-Only Features
To restrict a page to admins:
import { requireAdminRedirect } from '@/lib/auth/server-auth'; export default async function AdminPage() { await requireAdminRedirect(); // User guaranteed to be admin return <AdminPanel />; }
To restrict a Server Action to admins:
'use server'; import { requireAdmin } from '@/lib/auth/server-auth'; export async function adminAction() { await requireAdmin(); // Throws if not admin // Proceed }
To conditionally show admin UI:
import { isAdmin } from '@/lib/auth/server-auth'; export default async function Page() { const userIsAdmin = await isAdmin(); return ( <> <RegularContent /> {userIsAdmin && <AdminControls />} </> ); }
Enforcing Multi-Tenant Data Access
To ensure users only access their own family's data:
'use server'; import { requireFamilyAccess } from '@/lib/auth/server-auth'; export async function updateFamilyData(familyId: string, data: any) { await requireFamilyAccess(familyId); // Throws if user not in this family // User guaranteed to belong to this family await updateDatabase(familyId, data); }
When fetching current user's family data, use
getCurrentFamilyId() instead - no need for requireFamilyAccess since it's their own family.
Adding a New Authentication Page
To create a new auth page (login, register, password reset):
- Create page under
src/app/(auth)/page-name/ - The
group layout automatically redirects authenticated users to(auth)/dashboard - Create corresponding Server Action in
fileactions.ts - Import and use Supabase client:
const supabase = await createClient()
Pages in
(auth) group are automatically protected from authenticated users - they'll be redirected to dashboard if already logged in.
Implementing Login/Logout
Use
supabase.auth.signInWithPassword() for login and supabase.auth.signOut() for logout. See references/patterns.md for complete code examples.
Adding OAuth Providers
- Enable provider in Supabase Dashboard
- Use
supabase.auth.signInWithOAuth({ provider: 'github', options: {...} }) - Callback handled automatically by
src/app/auth/callback/route.ts
See
references/patterns.md for full implementation examples.
Security Requirements
Token Validation
- Always use
to validate tokens (revalidates with server)supabase.auth.getUser() - Never use
in server code (can be spoofed)supabase.auth.getSession()
Server-Side Only
- All auth helpers in
are server-side onlysrc/lib/auth/server-auth.ts - Never import these in Client Components
- Never add
directive to'use server'
(breaks class exports)server-auth.ts
Middleware
- Middleware automatically refreshes tokens on every request
- Uses
fromupdateSession()src/lib/supabase/middleware.ts - Calls
to revalidate tokensgetUser() - No additional token refresh logic needed
Reference Files
For detailed information, see:
- Complete file structure and organizationreferences/file-tree.md
- Security best practices and requirementsreferences/security.md
- Code examples and common patternsreferences/patterns.md
- Authentication flow diagramsreferences/flows.md
Quick Reference
Key Files:
src/lib/auth/server-auth.ts (helpers), src/middleware.ts (token refresh), src/app/dashboard/layout.tsx (dashboard protection)
Environment:
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, NEXT_PUBLIC_SITE_URL
Database:
auth.users (Supabase auth), public.families (households), public.users (profiles)
Common Issues
'use server' export error: Remove
'use server' from server-auth.ts - these are utilities, not actions
Middleware redirect fails: Use
requireAuth() in Server Actions - middleware redirects don't work with POST
Multi-tenant access denied: Use
requireFamilyAccess(familyId) to validate family ownership