FrontendRouting
Middleware
Authentication middleware for route protection.
The proxy (Next.js 16's name for middleware) runs on every matching request before the page renders, enforcing authentication on protected routes and saving refreshed tokens.
Configuration
// src/proxy.ts
export async function proxy(request: NextRequest, event: NextFetchEvent) {
const response = await auth(request, event);
// When Auth.js has just refreshed the tokens, apply the new cookie to this
// request too, so the page or route handling it reads the new tokens.
// ...
}It runs Auth.js's authorized callback (in lib/auth/providers.ts), and the jwt callback, which refreshes the access token a minute before it expires. A refreshed token can be saved to the cookie only here, so every route that uses the backend is in the matcher.
Protected Routes
export const config = {
matcher: [
"/pricing",
"/dashboard/:path*",
"/api/workspaces/:path*",
"/api/chats/:path*",
"/api/invitations/:path*",
"/api/subscriptions/:path*",
"/api/account/:path*",
"/api/transcribe-audio",
"/api/workspaces/:path*/translate",
],
};Route Protection Behavior
| Route Pattern | Behavior |
|---|---|
/dashboard/* | Redirect to /login if signed out |
/api/workspaces/*, /api/chats/*, /api/subscriptions/*, /api/account/* | Return 401 if signed out |
/pricing | Always accessible; the session is read so the page knows who's looking |
/login, /register | Always accessible (not in matcher) |
/, /features, /blog/* | Always accessible (not in matcher) |
How It Works
- Request hits a matched route
- Auth.js reads the session cookie, refreshing the tokens if they're about to expire
- If signed in: the request proceeds, with the refreshed cookie if there is one
- If not: redirects to
/login(pages) or returns 401 (API routes)
Additional Auth Checks
Beyond middleware, individual pages perform their own auth checks for defense in depth:
// Server component pattern
export default async function ProtectedPage() {
const user = await getCurrentUserServer();
if (!user) redirect("/login");
// Page content...
}API route handlers also validate the JWT token:
// API route pattern
export async function GET() {
const token = await getAccessToken();
if (!token) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
// ...
}This layered approach ensures protection even if middleware is bypassed or misconfigured.