Back to blog
Guide 2026-05-17 6 min read

Auth patterns for Next.js App Router with glyde

The Next.js App Router introduced a constraint that breaks most auth libraries: Server Components cannot write cookies. Only Route Handlers and Server Actions can modify the cookie jar.

This means if your Server Component calls an API, gets a 401, refreshes the token, and tries to save the new token — it fails silently. The refresh succeeds but the cookie never updates.

The solution: proactive refresh in middleware

Instead of reacting to 401s, check the token before the page renders. Next.js middleware runs before every request, can read AND write cookies, and can make fetch calls.

// middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export async function middleware(request: NextRequest) {
  const access = request.cookies.get("access_token")?.value
  const refresh = request.cookies.get("refresh_token")?.value

  if (!access && refresh) {
    const res = await fetch(`${process.env.API_BASE_URL}/auth/refresh`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refresh }),
    })

    if (res.ok) {
      const { access: newToken } = await res.json()
      const response = NextResponse.next()
      response.cookies.set("access_token", newToken, {
        httpOnly: true, secure: true, sameSite: "lax", path: "/",
      })
      return response
    }

    // Refresh failed — session is dead
    const response = NextResponse.redirect(new URL("/login", request.url))
    response.cookies.delete("access_token")
    response.cookies.delete("refresh_token")
    return response
  }

  return NextResponse.next()
}

The tower/passenger pattern

With middleware handling refresh, your glyde instances become simple:

  • tower (server) — reads cookies, adds Authorization header via interceptor
  • passenger (client) — calls /api/proxy routes, redirects on 401

Neither instance needs to know about token refresh. Middleware handles it transparently before any code runs.

Why this is better than built-in refresh

  • No cookie-writing constraint — middleware can write cookies
  • No double-request penalty — token is fresh before SSR starts
  • No mutex/queue complexity — one refresh per request cycle
  • Framework-aligned — uses Next.js primitives, not library hacks

Key takeaway

Don't fight the framework. Next.js middleware exists precisely for this use case. Let glyde handle the HTTP layer (interceptors, errors, types) and let middleware handle the auth lifecycle (refresh, redirect, cookie management).