HyperSaaS
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 PatternBehavior
/dashboard/*Redirect to /login if signed out
/api/workspaces/*, /api/chats/*, /api/subscriptions/*, /api/account/*Return 401 if signed out
/pricingAlways accessible; the session is read so the page knows who's looking
/login, /registerAlways accessible (not in matcher)
/, /features, /blog/*Always accessible (not in matcher)

How It Works

  1. Request hits a matched route
  2. Auth.js reads the session cookie, refreshing the tokens if they're about to expire
  3. If signed in: the request proceeds, with the refreshed cookie if there is one
  4. 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.

On this page