Resources
scripts/ auth-checklist.sh references/ decision-tree.md
Authentication Implementation Workflow
This skill orchestrates complete authentication implementation from discovery through testing. It replaces library-specific skills (clerk, nextauth, lucia, auth0, firebase-auth, supabase-auth, passport) with a unified workflow that adapts to your stack.
When to Use This Skill
Use when implementing:
-
User login and sign-up flows
-
Session management (cookies, JWT, server-side sessions)
-
OAuth integration (Google, GitHub, etc.)
-
Protected routes and API endpoints
-
Middleware-based authentication
-
Role-based access control (RBAC)
-
Token refresh mechanisms
-
Password reset flows
Prerequisites
Before starting:
-
Understand project framework (Next.js, Remix, Express, etc.)
-
Have database schema planned (if using database-backed auth)
-
Know authentication approach (managed service vs self-hosted)
-
Access to environment variable configuration
Workflow Steps
Step 1: Discovery
Use discover to understand existing authentication patterns:
discover: queries: - id: existing-auth type: grep pattern: "(useAuth|getSession|withAuth|requireAuth|protect|authenticate)" glob: "/*.{ts,tsx,js,jsx}" - id: middleware-files type: glob patterns: - "/middleware.{ts,js}" - "/auth//.{ts,js}" - "/_middleware.{ts,js}" - id: session-handling type: grep pattern: "(session|jwt|token|cookie)" glob: "/.{ts,tsx,js,jsx}" - id: protected-routes type: grep pattern: "(protected|private|requireAuth|withAuth)" glob: "**/*.{ts,tsx,js,jsx}" verbosity: files_only
What to look for:
-
Existing auth hooks, utilities, or middleware
-
Session storage mechanism (cookies, localStorage, server-side)
-
Protected route patterns
-
Auth provider setup (if using third-party)
Decision Point: If auth is already partially implemented, read existing files to understand the pattern before extending it.
Step 2: Detect Stack
Use detect_stack to determine framework and identify auth approach:
detect_stack: path: "."
Framework Detection:
-
Next.js (App Router): Use middleware.ts + Server Actions + cookies
-
Next.js (Pages Router): Use getServerSideProps + API routes + NextAuth
-
Remix: Use loader/action auth + session cookies
-
Express/Fastify: Use middleware + session store
-
tRPC: Use context + middleware
-
GraphQL: Use context + directives
Consult Decision Tree: Read references/decision-tree.md to choose between managed (Clerk, Auth0), self-hosted (NextAuth, Lucia), or serverless (Supabase Auth) based on framework and requirements.
Step 3: Implementation Planning
Based on framework and decision tree, plan which files to create/modify:
Common Files Needed:
Middleware (middleware.ts , auth.middleware.ts )
-
Intercept requests
-
Verify authentication status
-
Redirect unauthenticated users
Auth Utilities (lib/auth.ts , utils/auth.ts )
-
Session creation/validation
-
Token generation/verification
-
Password hashing (bcrypt, argon2)
Auth API Routes (/api/auth/login , /api/auth/signup , /api/auth/logout )
-
Handle authentication requests
-
Set session cookies
-
Return auth state
Protected Route Wrappers (withAuth , requireAuth )
-
HOCs or server utilities
-
Check auth before rendering
-
Redirect or return 401
Client Hooks (useAuth , useSession )
-
Access current user
-
Manage client-side auth state
-
Trigger login/logout
Framework-Specific Patterns:
Next.js App Router:
// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) { const token = request.cookies.get('session')?.value;
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); }
return NextResponse.next(); }
export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] };
Remix:
// app/utils/session.server.ts import { createCookieSessionStorage } from '@remix-run/node';
const { getSession, commitSession, destroySession } = createCookieSessionStorage({ cookie: { name: '__session', httpOnly: true, secure: process.env.NODE_ENV === 'production', secrets: [process.env.SESSION_SECRET], sameSite: 'lax' } });
export { getSession, commitSession, destroySession };
Express:
// middleware/auth.ts import jwt from 'jsonwebtoken'; import type { Request, Response, NextFunction } from 'express';
export function requireAuth(req: Request, res: Response, next: NextFunction) { const token = req.headers.authorization?.replace(/^bearer\s+/i, '');
if (!token) { return res.status(401).json({ error: 'Unauthorized' }); }
try { const payload = jwt.verify(token, process.env.JWT_SECRET!); req.user = payload; next(); } catch (err) { return res.status(401).json({ error: 'Invalid token' }); } }
Step 4: Write Configuration and Core Files
Use precision_write in batch mode to create auth infrastructure:
precision_write: files: - path: "middleware.ts" content: | # Framework-specific middleware (see patterns above) - path: "lib/auth.ts" content: | # Session validation, token generation - path: "app/api/auth/login/route.ts" content: | # Login endpoint implementation - path: "app/api/auth/signup/route.ts" content: | # Sign-up endpoint with validation - path: "app/api/auth/logout/route.ts" content: | # Logout and session cleanup verbosity: minimal
CRITICAL: No Placeholders
-
Implement full validation (zod, yup, etc.)
-
Include proper error handling
-
Hash passwords with bcrypt/argon2
-
Use secure session configuration
-
Add CSRF protection where applicable
Step 5: Install Dependencies
Use precision_exec to install required packages:
precision_exec: commands: # For JWT-based auth - cmd: "npm install jsonwebtoken bcryptjs" - cmd: "npm install -D @types/jsonwebtoken @types/bcryptjs"
# For session-based auth
- cmd: "npm install express-session connect-redis"
- cmd: "npm install -D @types/express-session"
# For managed services
- cmd: "npm install @clerk/nextjs" # Clerk
- cmd: "npm install next-auth" # NextAuth
- cmd: "npm install better-auth" # Better Auth (replaces deprecated Lucia)
verbosity: minimal
Run Database Migrations (if needed):
precision_exec: commands: - cmd: "npx prisma migrate dev --name add_user_auth" timeout_ms: 60000 - cmd: "npx prisma generate" verbosity: standard
Step 6: Security Verification
Use analysis-engine tools to verify security:
- Scan for Hardcoded Secrets:
scan_for_secrets: paths: - "lib/auth.ts" - "app/api/auth/**/*.ts" - "middleware.ts"
Expected Result: Zero secrets found. All API keys, JWT secrets, and credentials must be in .env files.
If secrets found:
-
Move to .env or .env.local
-
Use process.env.VAR_NAME to access
-
Add to .gitignore if not already present
- Audit Environment Variables:
env_audit: check_documented: true
Expected Result: All auth-related env vars documented in .env.example or README.
Required Variables (typical):
-
JWT_SECRET or SESSION_SECRET
-
DATABASE_URL (if using database)
-
OAuth credentials (GOOGLE_CLIENT_ID , GITHUB_CLIENT_SECRET , etc.)
-
NEXTAUTH_URL and NEXTAUTH_SECRET (for NextAuth)
- Validate Implementation:
Use precision_grep to validate critical security patterns
precision_grep: queries: - id: password_hashing pattern: "bcrypt|argon2|hashPassword" path: "lib" glob: "/*.ts" - id: httpOnly_cookies pattern: "httpOnly.true|httpOnly:\strue" path: "." glob: "/.ts" - id: csrf_protection pattern: "csrf|CsrfToken|verifyCsrfToken" path: "." glob: "**/.ts" output: format: "count_only"
Step 7: Protected Routes Implementation
Create route protection utilities:
Server-Side Protection (Next.js App Router):
// lib/auth.ts import { cookies } from 'next/headers'; import { redirect } from 'next/navigation';
export async function requireAuth() { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value;
if (!session) { redirect('/login'); }
const user = await validateSession(session); if (!user) { redirect('/login'); }
return user; }
Client-Side Hook (React):
// hooks/useAuth.ts import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation';
export function useAuth(options?: { redirectTo?: string }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const router = useRouter();
useEffect(() => { fetch('/api/auth/me') .then(res => res.ok ? res.json() : null) .then(data => { if (!data && options?.redirectTo) { router.push(options.redirectTo); } else { setUser(data); } }) .finally(() => setLoading(false)); }, [options?.redirectTo, router]);
return { user, loading }; }
Apply to Routes:
Use precision_edit to add auth checks to existing routes:
precision_edit: files: - path: "app/dashboard/page.tsx" edits: - find: "export default function DashboardPage()" replace: | export default async function DashboardPage() { const user = await requireAuth(); hints: near_line: 1
Step 8: Test Implementation
Use suggest_test_cases to generate auth-specific test scenarios:
suggest_test_cases: file: "lib/auth.ts" category: "authentication"
Expected Test Cases:
-
Valid login with correct credentials
-
Login failure with incorrect password
-
Sign-up with valid data
-
Sign-up with duplicate email
-
Protected route redirects unauthenticated users
-
Session token refresh
-
Logout clears session
-
CSRF token validation
-
Password reset flow
Run Validation Script:
bash scripts/auth-checklist.sh .
Expected Exit Code: 0 (all checks pass)
Manual Testing Checklist:
-
Sign up new user
-
Verify password is hashed in database
-
Log in with credentials
-
Access protected route (should succeed)
-
Log out
-
Access protected route (should redirect to login)
-
Test OAuth flow (if implemented)
-
Verify session persists across page reloads
-
Test token expiration and refresh
Common Patterns by Framework
Next.js (App Router)
Session Management:
-
Use cookies() from 'next/headers'
-
Set httpOnly, secure, sameSite cookies
-
Validate in middleware.ts and Server Components
Protected Routes:
-
Server-side: await requireAuth() in page/layout
-
Client-side: useAuth() hook with redirectTo
-
API routes: check cookies in route handlers
OAuth:
-
Use NextAuth for simplicity
-
Or implement manual OAuth flow with redirect URIs
Remix
Session Management:
-
Use createCookieSessionStorage
-
Validate in loaders
-
Commit session in actions
Protected Routes:
-
Check session in loader, throw redirect() if unauthenticated
-
Use getSession() utility consistently
OAuth:
-
Use remix-auth strategies
-
Handle callbacks in dedicated routes
Express/Fastify
Session Management:
-
Use express-session + Redis store
-
Or JWT tokens in Authorization header
Protected Routes:
-
Middleware functions: requireAuth, optionalAuth
-
Apply to specific routes or globally
OAuth:
-
Use passport.js strategies
-
Configure serialize/deserialize user
Error Handling
Follow error-recovery protocol for auth failures:
Login Failures
try { const user = await validateCredentials(email, password); await createSession(user.id); return { success: true }; } catch (err) { if (err instanceof InvalidCredentialsError) { // Don't leak whether email exists return { error: 'Invalid email or password' }; } if (err instanceof UserLockedError) { return { error: 'Account locked. Contact support.' }; } throw err; // Unexpected error }
Session Validation Failures
export async function validateSession(token: string) { try { const payload = jwt.verify(token, process.env.JWT_SECRET!); return await getUserById(payload.userId); } catch (err) { if (err instanceof jwt.TokenExpiredError) { return null; // Let caller handle (refresh or re-login) } if (err instanceof jwt.JsonWebTokenError) { return null; // Invalid token } throw err; // Unexpected error } }
Database Errors
try { const user = await db.user.create({ data: { email, passwordHash } }); } catch (err) { if (err.code === 'P2002') { // Prisma unique constraint violation return { error: 'Email already registered' }; } throw err; }
Security Checklist
Before marking implementation complete:
-
Passwords hashed with bcrypt (cost 10+) or argon2
-
Session tokens are random (crypto.randomBytes) or signed JWT
-
Cookies are httpOnly, secure (in prod), sameSite: 'lax' or 'strict'
-
CSRF protection enabled (for cookie-based auth)
-
Rate limiting on login/signup endpoints
-
Input validation on all auth endpoints
-
No sensitive data in error messages
-
Environment variables documented in .env.example
-
No hardcoded secrets in source code
-
Session expiration configured (1-7 days typical)
-
Logout clears all auth tokens/sessions
-
OAuth redirect URIs whitelisted
References
-
references/decision-tree.md
-
Managed vs self-hosted vs serverless comparison
-
.goodvibes/memory/patterns.json
-
Project-specific auth patterns
-
scripts/auth-checklist.sh
-
Automated validation script
Troubleshooting
"Session not persisting across requests"
Likely Causes:
-
Cookie not being set (check response headers)
-
httpOnly preventing client-side access (expected)
-
sameSite: 'strict' blocking cross-origin (use 'lax')
-
Cookie domain mismatch
Fix:
-
Check cookie configuration in auth code
-
Verify cookies are in response: precision_exec: { cmd: "curl -v localhost:3000/api/auth/login" }
-
Ensure middleware reads cookies correctly
"JWT token invalid after server restart"
Likely Causes:
-
JWT_SECRET changes between restarts
-
Secret not in .env file
-
Different secret in different environments
Fix:
-
Move JWT_SECRET to .env: JWT_SECRET=<random-256-bit-hex>
-
Generate with: openssl rand -hex 32
-
Never commit .env to git
"OAuth callback fails with redirect_uri_mismatch"
Likely Causes:
-
Callback URL not whitelisted in OAuth provider dashboard
-
http vs https mismatch
-
Port number missing
Fix:
-
Check OAuth provider settings
-
Add callback URL exactly as it appears in error
-
For local dev, use http://localhost:3000/api/auth/callback/google (example)
"Protected routes not redirecting"
Likely Causes:
-
Middleware not configured correctly
-
Matcher pattern doesn't match route
-
Session validation failing silently
Fix:
-
Check middleware.ts config.matcher
-
Add logging to middleware to verify it's running
-
Test session validation independently
Next Steps After Implementation
Add role-based access control (RBAC):
-
Add role field to User model
-
Create permission checking utilities
-
Protect admin routes
Implement password reset:
-
Generate reset tokens
-
Send email with reset link
-
Validate token and update password
Add email verification:
-
Generate verification tokens on signup
-
Send verification email
-
Verify token and mark user as verified
Set up refresh tokens:
-
Issue short-lived access tokens (15 min)
-
Issue long-lived refresh tokens (7 days)
-
Rotate refresh tokens on use
Add audit logging:
-
Log all login attempts (success and failure)
-
Track IP addresses and user agents
-
Monitor for suspicious activity
Summary
This workflow provides:
-
Framework-agnostic authentication implementation
-
Security-first approach with validation
-
Integration with GoodVibes precision tools
-
Automated verification via scripts
-
Error handling and troubleshooting
Follow each step sequentially, using precision tools for all file operations and validation.