or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-providers.mdcore-authentication.mddatabase-integration.mdindex.mdjwt-management.mdmiddleware-protection.mdreact-integration.mdserver-side-sessions.md

middleware-protection.mddocs/

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

```