auth-implementation

Installation
SKILL.md

Auth Implementation

Comprehensive guide for implementing authentication and authorization in web applications. Covers session-based auth, JWT, OAuth 2.0, SSO, API keys, RBAC/ABAC, and security hardening across all major frameworks and libraries.

Decision Matrix

Use this table to pick the right approach and library for your stack:

Use Case Framework Recommended Approach Library Reference
Full-stack app, social login Next.js App Router OAuth + sessions Auth.js v5 NEXTAUTH-AUTHJS.md
Full-stack app, email/password + social Next.js / SvelteKit Session-based Better Auth BETTER-AUTH.md
Full-stack, maximum control Next.js / SvelteKit / Astro Session-based Lucia v3 LUCIA-AUTH.md
API backend, social login Express / Fastify Strategy-based Passport.js PASSPORT-EXPRESS.md
Microservice API Any JWT (access + refresh) jose / jsonwebtoken CUSTOM-JWT.md
SaaS, managed auth Any Hosted OAuth/OIDC Clerk / Auth0 / Supabase Auth OAUTH-OIDC.md
Django app Django Session-based django.contrib.auth + allauth FRAMEWORK-PATTERNS.md
Python API FastAPI JWT / OAuth2 python-jose + Depends() FRAMEWORK-PATTERNS.md
PHP app Laravel Session / token Sanctum / Fortify / Breeze FRAMEWORK-PATTERNS.md
Go service net/http / Chi / Gin Session or JWT gorilla/sessions / golang-jwt FRAMEWORK-PATTERNS.md
Enterprise SSO Any SAML / OIDC Provider-specific SDK OAUTH-OIDC.md
Machine-to-machine Any API keys or Client Credentials Custom CUSTOM-JWT.md
Need RBAC/permissions Any Role-permission model CASL / Casbin / custom RBAC-ABAC.md

Implementation Workflow

Follow these 5 steps for any auth implementation:

Step 1: Detect Stack

Read package.json, requirements.txt, go.mod, composer.json, or framework config to identify:

  • Framework and version (Next.js 14+, Express, Django, FastAPI, Laravel, Go, SvelteKit)
  • Database and ORM (Prisma, Drizzle, SQLAlchemy, GORM, Eloquent)
  • Existing auth code (check for auth middleware, session config, JWT imports)

Step 2: Choose Approach

Use the Decision Matrix above. Key factors:

  • Server-rendered app → session-based (cookies)
  • SPA + API → JWT (access + refresh tokens) or BFF pattern
  • Mobile + API → JWT with secure token storage
  • Multiple providers needed → Auth.js, Better Auth, or Passport.js
  • Enterprise / compliance → managed provider (Clerk, Auth0) or OIDC
  • Maximum control → Lucia or custom JWT

Step 3: Database Schema

Set up auth tables. See DATABASE-SCHEMAS.md for full schemas.

Core tables needed:

  • users — id, email, password_hash, email_verified, created_at
  • sessions — id, user_id, expires_at (for session-based)
  • accounts — id, user_id, provider, provider_account_id (for OAuth)

Step 4: Implement

Use the relevant reference file for step-by-step implementation. Install dependencies, configure auth, add routes, protect endpoints.

Step 5: Harden

Apply SECURITY-CHECKLIST.md. At minimum:

  • CSRF protection enabled
  • Cookies: HttpOnly, Secure, SameSite=Lax
  • Password hashing: argon2id or bcrypt
  • Rate limiting on login/register/reset
  • Input validation on all auth endpoints

Session-Based Auth (Quick Start)

Session-based auth stores session ID in a cookie. Server holds session data.

When to use: Server-rendered apps, full-stack frameworks, when you need easy revocation.

Express + express-session

import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient({ url: process.env.REDIS_URL });
await redisClient.connect();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET!, // min 32 chars, random
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
  },
}));

Next.js (iron-session)

// lib/session.ts
import { getIronSession } from 'iron-session';
import { cookies } from 'next/headers';

export async function getSession() {
  return getIronSession<SessionData>(await cookies(), {
    password: process.env.SESSION_SECRET!,
    cookieName: 'app-session',
    cookieOptions: {
      secure: process.env.NODE_ENV === 'production',
      httpOnly: true,
      sameSite: 'lax',
    },
  });
}

JWT Auth (Quick Start)

JWT auth uses signed tokens. Access token (short-lived) + refresh token (long-lived).

When to use: APIs consumed by SPAs, mobile apps, microservices. See CUSTOM-JWT.md.

Token Architecture

Access Token:  Short-lived (15 min), in memory or Authorization header
Refresh Token: Long-lived (7-30 days), HttpOnly cookie, rotated on use

Node.js (jose library)

import { SignJWT, jwtVerify } from 'jose';

const secret = new TextEncoder().encode(process.env.JWT_SECRET);

// Create access token
async function createAccessToken(userId: string, role: string) {
  return new SignJWT({ sub: userId, role })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('15m')
    .sign(secret);
}

// Verify token
async function verifyToken(token: string) {
  const { payload } = await jwtVerify(token, secret);
  return payload;
}

Middleware Pattern

async function authMiddleware(req: Request, next: Function) {
  const token = req.headers.get('Authorization')?.replace('Bearer ', '');
  if (!token) return new Response('Unauthorized', { status: 401 });
  try {
    const payload = await verifyToken(token);
    req.user = payload;
    return next();
  } catch {
    return new Response('Invalid token', { status: 401 });
  }
}

OAuth 2.0 / Social Login (Quick Start)

OAuth lets users sign in with existing accounts (Google, GitHub, etc.).

Flow: Authorization Code + PKCE (recommended for all clients):

1. Client generates code_verifier + code_challenge
2. Redirect to provider: /authorize?response_type=code&code_challenge=...
3. User authenticates with provider
4. Provider redirects back with authorization code
5. Server exchanges code + code_verifier for tokens
6. Server creates session or issues own JWT

See OAUTH-OIDC.md for provider-specific setup.

Provider Quick Reference

Provider Install Config Needed Reference
Auth.js v5 npm i next-auth@beta auth.ts + providers + adapter NEXTAUTH-AUTHJS.md
Better Auth npm i better-auth auth.ts + DB + plugins BETTER-AUTH.md
Lucia v3 npm i lucia Adapter + session config LUCIA-AUTH.md
Passport.js npm i passport passport-local Strategies + serialize PASSPORT-EXPRESS.md
Clerk npm i @clerk/nextjs CLERK_* env vars OAUTH-OIDC.md
Auth0 npm i @auth0/nextjs-auth0 AUTH0_* env vars OAUTH-OIDC.md
Supabase Auth npm i @supabase/supabase-js Supabase project URL + key OAUTH-OIDC.md
Firebase Auth npm i firebase Firebase config OAUTH-OIDC.md
Django allauth pip install django-allauth INSTALLED_APPS + providers FRAMEWORK-PATTERNS.md
Laravel Sanctum composer require laravel/sanctum Publish config + middleware FRAMEWORK-PATTERNS.md

Database Schema Essentials

See DATABASE-SCHEMAS.md for full schemas.

Prisma (Core Tables)

model User {
  id            String    @id @default(cuid())
  email         String    @unique
  passwordHash  String?
  emailVerified DateTime?
  name          String?
  role          String    @default("user")
  sessions      Session[]
  accounts      Account[]
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
}

model Session {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  expiresAt DateTime
  createdAt DateTime @default(now())
  @@index([userId])
}

model Account {
  id                String @id @default(cuid())
  userId            String
  user              User   @relation(fields: [userId], references: [id], onDelete: Cascade)
  provider          String
  providerAccountId String
  accessToken       String?
  refreshToken      String?
  expiresAt         Int?
  @@unique([provider, providerAccountId])
  @@index([userId])
}

Middleware & Route Protection

Next.js App Router — middleware.ts

import { NextRequest, NextResponse } from 'next/server';

const protectedPaths = ['/dashboard', '/settings', '/api/protected'];
const authPaths = ['/login', '/register'];

export function middleware(request: NextRequest) {
  const sessionToken = request.cookies.get('session-token')?.value;
  const { pathname } = request.nextUrl;

  const isProtected = protectedPaths.some(p => pathname.startsWith(p));
  const isAuthPage = authPaths.some(p => pathname.startsWith(p));

  if (isProtected && !sessionToken) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  if (isAuthPage && sessionToken) {
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }
  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/settings/:path*', '/api/protected/:path*', '/login', '/register'],
};

Express Middleware

function requireAuth(req, res, next) {
  if (!req.session?.userId) {
    return res.status(401).json({ error: 'Authentication required' });
  }
  next();
}

function requireRole(...roles: string[]) {
  return (req, res, next) => {
    if (!roles.includes(req.session.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Usage
app.get('/admin', requireAuth, requireRole('admin'), adminHandler);

RBAC Quick Start

See RBAC-ABAC.md for full patterns.

Simple Role Check

// Define roles and permissions
const PERMISSIONS = {
  admin: ['read', 'write', 'delete', 'manage_users'],
  editor: ['read', 'write'],
  viewer: ['read'],
} as const;

function hasPermission(role: string, permission: string): boolean {
  return PERMISSIONS[role]?.includes(permission) ?? false;
}

// Middleware
function requirePermission(permission: string) {
  return (req, res, next) => {
    if (!hasPermission(req.user.role, permission)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

Security Checklist (Condensed)

See SECURITY-CHECKLIST.md for full details.

# Check Priority Notes
1 Password hashing: argon2id or bcrypt (cost ≥ 10) Critical Never SHA-256/MD5
2 Cookies: HttpOnly, Secure, SameSite=Lax Critical Prevents XSS token theft
3 CSRF protection on state-changing endpoints Critical Double-submit or SameSite
4 Rate limit login attempts (5/min per IP+user) Critical Prevents brute force
5 Rate limit password reset (3/hour per email) High Prevents email flooding
6 Validate redirect URLs (allowlist origins) High Prevents open redirect
7 Token expiry: access 15min, refresh 7-30d High Limit blast radius
8 Refresh token rotation + family detection High Detect token theft
9 Email verification before full access Medium Prevents fake accounts
10 Account lockout after N failed attempts Medium With exponential backoff
11 HTTPS everywhere (HSTS header) Critical No mixed content
12 Input validation on email, password High Zod / Yup / server-side
13 Secure password reset flow (time-limited token) High Token expires in 1h
14 Audit log for auth events Medium Login, logout, password change
15 MFA/2FA for sensitive operations Medium TOTP or WebAuthn

Common Pitfalls

# Pitfall Fix
1 Storing JWT in localStorage Use HttpOnly cookie or in-memory only
2 No refresh token rotation Rotate on every use, detect reuse
3 Long-lived access tokens (>1h) Keep to 15min, use refresh tokens
4 Comparing passwords with === Use timing-safe comparison (crypto.timingSafeEqual)
5 Not validating redirect URIs Allowlist of origins, never open redirect
6 Session fixation after login Regenerate session ID after authentication
7 Missing CSRF on cookie-based auth Add CSRF token or use SameSite=Strict
8 Exposing user enumeration Same response for "user not found" and "wrong password"
9 No rate limiting on auth endpoints Rate limit by IP + username combination
10 Storing unhashed API keys Store hash, show key only once on creation
11 Using deprecated crypto (MD5, SHA-1) Use argon2id for passwords, SHA-256+ for tokens
12 Not revoking sessions on password change Invalidate all sessions except current

MFA / 2FA Overview

TOTP (Time-based One-Time Password)

// Libraries: otpauth (Node.js), pyotp (Python)
import { TOTP } from 'otpauth';

const totp = new TOTP({
  issuer: 'MyApp',
  label: user.email,
  algorithm: 'SHA1',
  digits: 6,
  period: 30,
  secret: generateSecret(),
});

// Generate QR code URI
const uri = totp.toString(); // otpauth://totp/MyApp:user@email.com?...

// Verify code
const isValid = totp.validate({ token: userCode, window: 1 }) !== null;

WebAuthn / Passkeys

Use @simplewebauthn/server + @simplewebauthn/browser for passwordless auth. See SECURITY-CHECKLIST.md for implementation steps.

References Index

File Topic When to Read
NEXTAUTH-AUTHJS.md Auth.js v5 + Next.js Next.js app with social login
BETTER-AUTH.md Better Auth Next.js/Svelte with plugin system
LUCIA-AUTH.md Lucia v3 Maximum control, session-based
PASSPORT-EXPRESS.md Passport.js Express/Fastify backend
CUSTOM-JWT.md Custom JWT Microservice API, no framework auth
OAUTH-OIDC.md OAuth 2.0 / OIDC Social login, SSO, managed providers
DATABASE-SCHEMAS.md DB schemas Setting up auth tables
SECURITY-CHECKLIST.md Security hardening After initial implementation
RBAC-ABAC.md Authorization Adding roles/permissions
FRAMEWORK-PATTERNS.md Framework-specific Django, FastAPI, Laravel, Go, SvelteKit
Related skills

More from sattva2020/skills

Installs
2
GitHub Stars
1
First Seen
Mar 29, 2026