0
# Middleware and Route Protection
1
2
Middleware functions for protecting routes and implementing authorization logic in Next.js applications. Provides flexible authentication checks and custom authorization patterns at the edge.
3
4
## Capabilities
5
6
### WithAuth Middleware
7
8
Main middleware function with multiple overloads for different usage patterns and authentication requirements.
9
10
```typescript { .api }
11
/**
12
* Middleware that checks if user is authenticated/authorized
13
* @param args - Various argument patterns for different use cases
14
* @returns Middleware function or middleware response
15
*/
16
function withAuth(): ReturnType<NextMiddlewareWithAuth>;
17
function withAuth(req: NextRequestWithAuth): ReturnType<NextMiddlewareWithAuth>;
18
function withAuth(req: NextRequestWithAuth, event: NextFetchEvent): ReturnType<NextMiddlewareWithAuth>;
19
function withAuth(req: NextRequestWithAuth, options: NextAuthMiddlewareOptions): ReturnType<NextMiddlewareWithAuth>;
20
function withAuth(middleware: NextMiddlewareWithAuth): NextMiddlewareWithAuth;
21
function withAuth(middleware: NextMiddlewareWithAuth, options: NextAuthMiddlewareOptions): NextMiddlewareWithAuth;
22
function withAuth(options: NextAuthMiddlewareOptions): NextMiddlewareWithAuth;
23
24
type NextMiddlewareWithAuth = (
25
request: NextRequestWithAuth,
26
event: NextFetchEvent
27
) => NextMiddlewareResult | Promise<NextMiddlewareResult>;
28
29
type NextMiddlewareResult = ReturnType<NextMiddleware> | void;
30
```
31
32
**Usage Examples:**
33
34
```typescript
35
// Basic authentication check (middleware.ts)
36
export { default } from "next-auth/middleware";
37
38
export const config = {
39
matcher: ["/dashboard/:path*", "/profile/:path*"]
40
};
41
42
// Custom middleware with options
43
import { withAuth } from "next-auth/middleware";
44
45
export default withAuth({
46
pages: {
47
signIn: "/login",
48
error: "/auth-error",
49
},
50
});
51
52
export const config = {
53
matcher: ["/admin/:path*"]
54
};
55
56
// Advanced authorization logic
57
import { withAuth } from "next-auth/middleware";
58
59
export default withAuth(
60
function middleware(req) {
61
// Custom middleware logic after authentication
62
console.log("Authenticated user:", req.nextauth.token?.email);
63
64
// Additional checks based on route
65
if (req.nextUrl.pathname.startsWith("/admin") &&
66
req.nextauth.token?.role !== "admin") {
67
return Response.redirect(new URL("/unauthorized", req.url));
68
}
69
},
70
{
71
callbacks: {
72
authorized: ({ token, req }) => {
73
// Custom authorization logic
74
if (req.nextUrl.pathname.startsWith("/admin")) {
75
return token?.role === "admin";
76
}
77
return !!token; // Just require authentication for other routes
78
},
79
},
80
}
81
);
82
83
export const config = {
84
matcher: ["/dashboard/:path*", "/admin/:path*"]
85
};
86
```
87
88
### Middleware Options
89
90
Configuration options for customizing middleware behavior and authentication checks.
91
92
```typescript { .api }
93
/**
94
* Configuration options for NextAuth middleware
95
*/
96
interface NextAuthMiddlewareOptions {
97
/** Custom page URLs for authentication flow */
98
pages?: AuthOptions["pages"];
99
/** Custom cookie configuration */
100
cookies?: Partial<Record<keyof Pick<keyof AuthOptions["cookies"], "sessionToken">, Omit<CookieOption, "options">>>;
101
/** JWT configuration for token decoding */
102
jwt?: Partial<Pick<JWTOptions, "decode">>;
103
/** Authorization callback functions */
104
callbacks?: {
105
authorized?: AuthorizedCallback;
106
};
107
/** Secret for JWT decoding (defaults to NEXTAUTH_SECRET) */
108
secret?: string;
109
}
110
111
type AuthorizedCallback = (params: {
112
/** JWT token (null if not authenticated) */
113
token: JWT | null;
114
/** Incoming request object */
115
req: NextRequest;
116
}) => Awaitable<boolean>;
117
```
118
119
### Extended Request Type
120
121
NextRequest extended with authentication information for use in middleware.
122
123
```typescript { .api }
124
/**
125
* NextRequest extended with NextAuth token information
126
*/
127
interface NextRequestWithAuth extends NextRequest {
128
/** NextAuth authentication data */
129
nextauth: {
130
/** JWT token (null if not authenticated) */
131
token: JWT | null;
132
};
133
}
134
```
135
136
### Custom Authorization Patterns
137
138
Advanced authorization patterns for complex access control requirements.
139
140
```typescript { .api }
141
/**
142
* Authorization utilities and patterns
143
*/
144
interface AuthorizationPatterns {
145
/** Role-based access control */
146
checkRole: (token: JWT | null, requiredRole: string) => boolean;
147
/** Permission-based access control */
148
checkPermission: (token: JWT | null, permission: string) => boolean;
149
/** Multi-factor authentication check */
150
requireMFA: (token: JWT | null) => boolean;
151
/** Time-based access control */
152
checkTimeRestriction: (token: JWT | null, allowedHours: number[]) => boolean;
153
}
154
```
155
156
**Usage Examples:**
157
158
```typescript
159
// Role-based middleware
160
import { withAuth } from "next-auth/middleware";
161
162
export default withAuth({
163
callbacks: {
164
authorized: ({ token, req }) => {
165
// Admin routes require admin role
166
if (req.nextUrl.pathname.startsWith("/admin")) {
167
return token?.role === "admin";
168
}
169
170
// Manager routes require manager or admin role
171
if (req.nextUrl.pathname.startsWith("/manager")) {
172
return ["admin", "manager"].includes(token?.role as string);
173
}
174
175
// Other protected routes just need authentication
176
return !!token;
177
},
178
},
179
});
180
181
// Permission-based middleware
182
export default withAuth({
183
callbacks: {
184
authorized: ({ token, req }) => {
185
const pathname = req.nextUrl.pathname;
186
const permissions = token?.permissions as string[] || [];
187
188
// Check specific permissions for different routes
189
if (pathname.startsWith("/users")) {
190
return permissions.includes("user:read");
191
}
192
193
if (pathname.startsWith("/reports")) {
194
return permissions.includes("reports:read");
195
}
196
197
return !!token;
198
},
199
},
200
});
201
202
// Multi-factor authentication requirement
203
export default withAuth({
204
callbacks: {
205
authorized: ({ token, req }) => {
206
if (!token) return false;
207
208
// Require MFA for sensitive routes
209
if (req.nextUrl.pathname.startsWith("/settings/security")) {
210
return token.mfaVerified === true;
211
}
212
213
return true;
214
},
215
},
216
});
217
218
// Time-based access control
219
export default withAuth({
220
callbacks: {
221
authorized: ({ token, req }) => {
222
if (!token) return false;
223
224
// Business hours restriction for admin routes
225
if (req.nextUrl.pathname.startsWith("/admin")) {
226
const hour = new Date().getHours();
227
const businessHours = [9, 10, 11, 12, 13, 14, 15, 16, 17]; // 9 AM - 5 PM
228
return businessHours.includes(hour);
229
}
230
231
return true;
232
},
233
},
234
});
235
```
236
237
### Conditional Route Protection
238
239
Patterns for protecting specific routes or route groups with different requirements.
240
241
```typescript { .api }
242
/**
243
* Route protection patterns
244
*/
245
interface RouteProtectionPatterns {
246
/** Protect based on URL pattern */
247
protectByPattern: (pathname: string, patterns: string[]) => boolean;
248
/** Protect based on HTTP method */
249
protectByMethod: (method: string, allowedMethods: string[]) => boolean;
250
/** Protect based on query parameters */
251
protectByQuery: (searchParams: URLSearchParams, requiredParams: string[]) => boolean;
252
}
253
```
254
255
**Usage Examples:**
256
257
```typescript
258
// Different protection levels by route
259
import { withAuth } from "next-auth/middleware";
260
261
export default withAuth(
262
function middleware(req) {
263
const { pathname } = req.nextUrl;
264
const token = req.nextauth.token;
265
266
// Public API endpoints - no authentication required
267
if (pathname.startsWith("/api/public")) {
268
return;
269
}
270
271
// Protected API endpoints - authentication required
272
if (pathname.startsWith("/api/protected")) {
273
if (!token) {
274
return Response.redirect(new URL("/api/auth/signin", req.url));
275
}
276
}
277
278
// Admin API endpoints - admin role required
279
if (pathname.startsWith("/api/admin")) {
280
if (token?.role !== "admin") {
281
return new Response("Forbidden", { status: 403 });
282
}
283
}
284
}
285
);
286
287
// Method-based protection
288
export default withAuth(
289
function middleware(req) {
290
const { method } = req;
291
const token = req.nextauth.token;
292
293
// Only authenticated users can make POST, PUT, DELETE requests
294
if (["POST", "PUT", "DELETE"].includes(method) && !token) {
295
return new Response("Authentication required", { status: 401 });
296
}
297
298
// Only admins can make DELETE requests
299
if (method === "DELETE" && token?.role !== "admin") {
300
return new Response("Admin access required", { status: 403 });
301
}
302
}
303
);
304
305
// Query parameter based protection
306
export default withAuth(
307
function middleware(req) {
308
const { searchParams } = req.nextUrl;
309
const token = req.nextauth.token;
310
311
// Sensitive operations require additional verification
312
if (searchParams.get("action") === "delete-account") {
313
if (!token?.emailVerified) {
314
return Response.redirect(new URL("/verify-email", req.url));
315
}
316
}
317
318
// Admin operations require admin role
319
if (searchParams.get("admin") === "true") {
320
if (token?.role !== "admin") {
321
return new Response("Admin access required", { status: 403 });
322
}
323
}
324
}
325
);
326
```
327
328
### Error Handling and Redirects
329
330
Custom error handling and redirect patterns for authentication failures.
331
332
```typescript { .api }
333
/**
334
* Error handling patterns for middleware
335
*/
336
interface MiddlewareErrorHandling {
337
/** Handle authentication failures */
338
handleAuthError: (error: string, req: NextRequest) => Response;
339
/** Handle authorization failures */
340
handleAuthzError: (reason: string, req: NextRequest) => Response;
341
/** Custom redirect logic */
342
customRedirect: (destination: string, req: NextRequest) => Response;
343
}
344
```
345
346
**Usage Examples:**
347
348
```typescript
349
// Custom error pages and redirects
350
import { withAuth } from "next-auth/middleware";
351
352
export default withAuth(
353
function middleware(req) {
354
// Custom logic here
355
},
356
{
357
pages: {
358
signIn: "/auth/signin",
359
error: "/auth/error",
360
},
361
callbacks: {
362
authorized: ({ token, req }) => {
363
const { pathname } = req.nextUrl;
364
365
// Handle different authorization scenarios
366
if (pathname.startsWith("/admin")) {
367
if (!token) {
368
// Will redirect to signIn page
369
return false;
370
}
371
372
if (token.role !== "admin") {
373
// Custom unauthorized handling
374
return false; // This will redirect to signIn, but we can handle it in middleware
375
}
376
377
return true;
378
}
379
380
return !!token;
381
},
382
},
383
}
384
);
385
386
// Advanced error handling
387
export default withAuth(
388
function middleware(req) {
389
const { pathname } = req.nextUrl;
390
const token = req.nextauth.token;
391
392
// Custom error responses
393
if (pathname.startsWith("/api/")) {
394
if (!token) {
395
return new Response(
396
JSON.stringify({ error: "Authentication required" }),
397
{
398
status: 401,
399
headers: { "Content-Type": "application/json" }
400
}
401
);
402
}
403
404
if (pathname.startsWith("/api/admin") && token.role !== "admin") {
405
return new Response(
406
JSON.stringify({ error: "Insufficient permissions" }),
407
{
408
status: 403,
409
headers: { "Content-Type": "application/json" }
410
}
411
);
412
}
413
}
414
415
// Custom page redirects
416
if (pathname.startsWith("/admin") && token?.role !== "admin") {
417
return Response.redirect(new URL("/unauthorized", req.url));
418
}
419
}
420
);
421
```
422
423
### Middleware Configuration
424
425
Next.js middleware configuration for route matching and optimization.
426
427
```typescript { .api }
428
/**
429
* Next.js middleware configuration
430
*/
431
interface MiddlewareConfig {
432
/** Route patterns to match */
433
matcher: string | string[];
434
/** Missing routes configuration */
435
missing?: Array<{
436
type: "header" | "query" | "cookie";
437
key: string;
438
value?: string;
439
}>;
440
/** Has routes configuration */
441
has?: Array<{
442
type: "header" | "query" | "cookie";
443
key: string;
444
value?: string;
445
}>;
446
}
447
```
448
449
**Usage Examples:**
450
451
```typescript
452
// Comprehensive matcher patterns
453
export const config = {
454
matcher: [
455
// Protect all dashboard routes
456
"/dashboard/:path*",
457
458
// Protect admin routes
459
"/admin/:path*",
460
461
// Protect API routes except public ones
462
"/api/((?!public|auth).)*",
463
464
// Protect specific pages
465
"/profile",
466
"/settings/:path*",
467
]
468
};
469
470
// Conditional matching with has/missing
471
export const config = {
472
matcher: "/api/:path*",
473
has: [
474
{
475
type: "header",
476
key: "authorization",
477
}
478
]
479
};
480
481
// Advanced matching patterns
482
export const config = {
483
matcher: [
484
// Match all routes except specific patterns
485
"/((?!api/auth|api/public|_next/static|_next/image|favicon.ico).*)",
486
]
487
};
488
```
489
490
## Types
491
492
### Middleware Types
493
494
```typescript { .api }
495
interface NextFetchEvent {
496
waitUntil: (promise: Promise<any>) => void;
497
}
498
499
type NextMiddleware = (
500
request: NextRequest,
501
event: NextFetchEvent
502
) => NextResponse | Response | null | undefined | void;
503
504
interface CookieOption {
505
name: string;
506
options: CookieSerializeOptions;
507
}
508
509
interface CookieSerializeOptions {
510
domain?: string;
511
expires?: Date;
512
httpOnly?: boolean;
513
maxAge?: number;
514
path?: string;
515
priority?: "low" | "medium" | "high";
516
sameSite?: boolean | "lax" | "strict" | "none";
517
secure?: boolean;
518
}
519
```
520
521
### Authentication State Types
522
523
```typescript { .api }
524
interface AuthenticationState {
525
isAuthenticated: boolean;
526
user: JWT | null;
527
hasRole: (role: string) => boolean;
528
hasPermission: (permission: string) => boolean;
529
}
530
531
type AuthorizationResult = boolean | Response | NextResponse;
532
533
interface AuthorizationContext {
534
token: JWT | null;
535
request: NextRequest;
536
pathname: string;
537
method: string;
538
searchParams: URLSearchParams;
539
}
540
```