🎯

middleware-protection

🎯Skill

from dadbodgeoff/drift

VibeIndex|
What it does

Protects Next.js routes declaratively with centralized authentication middleware, supporting public, protected, and role-based access routes.

πŸ“¦

Part of

dadbodgeoff/drift(69 items)

middleware-protection

Installation

npm installInstall npm package
npm install -g driftdetect
npm installInstall npm package
npm install -g driftdetect@latest
npm installInstall npm package
npm install -g driftdetect-mcp
Claude Desktop ConfigurationAdd this to your claude_desktop_config.json
{ "mcpServers": { "drift": { "command": "driftdetect-mcp" } } ...
πŸ“– Extracted from docs: dadbodgeoff/drift
5Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Protect routes with Next.js middleware. Check authentication once, protect routes declaratively. Supports public routes, protected routes, and role-based access.

Overview

# Middleware Route Protection

Check auth once, protect routes declaratively.

When to Use This Skill

  • Need to protect multiple routes
  • Want centralized auth checking
  • Tired of repeating auth logic in every route
  • Need role-based access control

Core Concepts

  1. Middleware intercepts - All requests pass through middleware
  2. Declarative routes - Define protected/public routes in config
  3. Session refresh - Keep sessions alive automatically
  4. Consistent errors - API routes get JSON, pages get redirects

TypeScript Implementation

middleware.ts

```typescript

// middleware.ts

import { createServerClient } from '@supabase/ssr';

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

// Routes that require authentication

const PROTECTED_ROUTES = [

'/dashboard',

'/settings',

'/api/user',

'/api/predictions',

];

// Routes that are always public

const PUBLIC_ROUTES = [

'/',

'/login',

'/signup',

'/api/health',

'/api/public',

];

export async function middleware(request: NextRequest) {

const pathname = request.nextUrl.pathname;

// Skip static files and Next.js internals

if (

pathname.startsWith('/_next') ||

pathname.startsWith('/favicon') ||

pathname.match(/\.(svg|png|jpg|jpeg|gif|webp|ico)$/)

) {

return NextResponse.next();

}

// Skip explicitly public routes

if (PUBLIC_ROUTES.some(route => pathname === route || pathname.startsWith(route + '/'))) {

return NextResponse.next();

}

// Create response that we'll modify

let response = NextResponse.next({ request });

// Create Supabase client with cookie handling

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)

);

response = NextResponse.next({ request });

cookiesToSet.forEach(({ name, value, options }) =>

response.cookies.set(name, value, options)

);

},

},

}

);

// Refresh session (important for SSR)

const { data: { user } } = await supabase.auth.getUser();

// Check if route requires auth

const requiresAuth = PROTECTED_ROUTES.some(route =>

pathname === route || pathname.startsWith(route + '/')

);

if (requiresAuth && !user) {

// API routes: return 401 JSON

if (pathname.startsWith('/api/')) {

return NextResponse.json(

{

error: 'Authentication required',

code: 'AUTH_REQUIRED',

loginUrl: '/login',

},

{ status: 401 }

);

}

// Pages: redirect to login with return URL

const url = request.nextUrl.clone();

url.pathname = '/login';

url.searchParams.set('redirectTo', pathname);

return NextResponse.redirect(url);

}

// Add user ID to headers for downstream use

if (user) {

response.headers.set('x-user-id', user.id);

}

return response;

}

export const config = {

matcher: [

'/((?!_next/static|_next/image|favicon.ico|.\\.(?:svg|png|jpg|jpeg|gif|webp)$).)',

],

};

```

Using User ID in API Routes

```typescript

// app/api/user/route.ts

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

import { createServerSupabaseClient } from '@/lib/supabase-server';

export async function GET(request: NextRequest) {

// User ID was added by middleware

const userId = request.headers.get('x-user-id');

if (!userId) {

return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

}

const supabase = await createServerSupabaseClient();

const { data: profile } = await supabase

.from('user_profiles')

.select('*')

.eq('id', userId)

.single();

return NextResponse.json({ profile });

}

```

Role-Based Protection

```typescript

// middleware.ts (extended)

const ROUTE_ROLES: Record = {

'/admin': ['admin'],

'/dashboard': ['user', 'admin'],

'/api/admin': ['admin'],

};

// After getting user, check role

if (user) {

const userRole = user.user_metadata?.role || 'user';

const requiredRoles = Object.entries(ROUTE_ROLES)

.find(([route]) => pathname.startsWith(route))?.[1];

if (requiredRoles && !requiredRoles.includes(userRole)) {

if (pathname.startsWith('/api/')) {

return NextResponse.json(

{ error: 'Forbidden', code: 'FORBIDDEN' },

{ status: 403 }

);

}

return NextResponse.redirect(new URL('/unauthorized', request.url));

}

}

```

Pattern-Based Routes

```typescript

// For more complex route matching

const PROTECTED_PATTERNS = [

/^\/dashboard(\/.*)?$/,

/^\/api\/user\/.*$/,

/^\/settings$/,

/^\/api\/v\d+\/private\/./, // /api/v1/private/, /api/v2/private/*

];

const requiresAuth = PROTECTED_PATTERNS.some(pattern =>

pattern.test(pathname)

);

```

Python Implementation (FastAPI)

```python

# middleware/auth.py

from fastapi import Request, HTTPException

from fastapi.responses import RedirectResponse

from starlette.middleware.base import BaseHTTPMiddleware

from typing import Set

PROTECTED_ROUTES: Set[str] = {

"/dashboard",

"/settings",

"/api/user",

}

PUBLIC_ROUTES: Set[str] = {

"/",

"/login",

"/signup",

"/api/health",

}

class AuthMiddleware(BaseHTTPMiddleware):

async def dispatch(self, request: Request, call_next):

path = request.url.path

# Skip public routes

if path in PUBLIC_ROUTES or any(path.startswith(r + "/") for r in PUBLIC_ROUTES):

return await call_next(request)

# Check if route requires auth

requires_auth = path in PROTECTED_ROUTES or any(

path.startswith(r + "/") for r in PROTECTED_ROUTES

)

if not requires_auth:

return await call_next(request)

# Get user from session/token

user = await get_user_from_request(request)

if not user:

if path.startswith("/api/"):

raise HTTPException(

status_code=401,

detail={

"error": "Authentication required",

"code": "AUTH_REQUIRED",

"login_url": "/login",

}

)

return RedirectResponse(f"/login?redirectTo={path}")

# Add user to request state

request.state.user = user

request.state.user_id = user.id

return await call_next(request)

async def get_user_from_request(request: Request):

"""Extract and validate user from request."""

token = request.cookies.get("session") or request.headers.get("Authorization")

if not token:

return None

# Validate token and return user

return await validate_session(token)

```

```python

# Using in routes

from fastapi import Request, Depends

@app.get("/api/user/profile")

async def get_profile(request: Request):

user_id = request.state.user_id

profile = await db.get_profile(user_id)

return {"profile": profile}

```

Error Response Format

```typescript

// Consistent error format for API routes

interface AuthError {

error: string;

code: 'AUTH_REQUIRED' | 'SESSION_EXPIRED' | 'FORBIDDEN';

message?: string;

loginUrl: string;

}

// 401 - Not authenticated

{

"error": "Authentication required",

"code": "AUTH_REQUIRED",

"loginUrl": "/login"

}

// 403 - Authenticated but not authorized

{

"error": "Forbidden",

"code": "FORBIDDEN",

"message": "Admin access required"

}

```

Best Practices

  1. Refresh sessions - Call getUser() in middleware to refresh
  2. Pass user ID - Add to headers for downstream routes
  3. JSON for APIs - Never redirect API routes
  4. Return URL - Include redirectTo param for login redirects
  5. Skip static - Don't process static files

Common Mistakes

  • Redirecting API routes (should return 401 JSON)
  • Not refreshing session in middleware
  • Processing static files through auth
  • Missing return URL on login redirect
  • Not handling role-based access

Related Skills

  • [Supabase Auth](../supabase-auth/)
  • [JWT Auth](../jwt-auth/)
  • [Row Level Security](../row-level-security/)

More from this repository10

🎯
feature-flags🎯Skill

Enables controlled feature rollouts, A/B testing, and selective feature access through configurable flags for gradual deployment and user targeting.

🎯
design-tokens🎯Skill

Generates a comprehensive, type-safe design token system with WCAG AA color compliance and multi-framework support for consistent visual design.

🎯
file-uploads🎯Skill

Securely validates, scans, and processes file uploads with multi-stage checks, malware detection, and race condition prevention.

🎯
ai-coaching🎯Skill

Guides users through articulating creative intent by extracting structured parameters and detecting conversation readiness.

🎯
environment-config🎯Skill

Validates and centralizes environment variables with type safety, fail-fast startup checks, and multi-environment support.

🎯
community-feed🎯Skill

Generates efficient social feed with cursor pagination, trending algorithms, and engagement tracking for infinite scroll experiences.

🎯
cloud-storage🎯Skill

Enables secure, multi-tenant cloud file storage with signed URLs, direct uploads, and visibility control for user-uploaded assets.

🎯
email-service🎯Skill

Simplifies email sending, templating, and tracking with robust SMTP integration and support for multiple email providers and transactional workflows.

🎯
error-sanitization🎯Skill

Sanitizes error messages by logging full details server-side while exposing only generic, safe messages to prevent sensitive information leakage.

🎯
batch-processing🎯Skill

Optimizes database operations by collecting and batching independent records, improving throughput by 30-40% with built-in fallback processing.