Server-side authentication in Next.js

You can set up your Next.js App Router app to have access to the authentication state on the server.

Setup

Make sure your React providers and middleware are correctly set up first.

Require authentication for certain routes

By default, all routes can be accessed without authenticating. You can configure which routes require authentication in your middleware.ts:

middleware.ts
import {
  convexAuthNextjsMiddleware,
  createRouteMatcher,
  nextjsMiddlewareRedirect,
} from "@convex-dev/auth/nextjs/server";
 
const isSignInPage = createRouteMatcher(["/signin"]);
const isProtectedRoute = createRouteMatcher(["/product(.*)"]);
 
export default convexAuthNextjsMiddleware((request, { convexAuth }) => {
  if (isSignInPage(request) && convexAuth.isAuthenticated()) {
    return nextjsMiddlewareRedirect(request, "/product");
  }
  if (isProtectedRoute(request) && !convexAuth.isAuthenticated()) {
    return nextjsMiddlewareRedirect(request, "/signin");
  }
});
 
export const config = {
  // The following matcher runs middleware on all routes
  // except static assets.
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

In general, you'll likely want to redirect when an unauthenticated user tries to access a route that requires authentication.

To do this, you can pass a function to convexAuthNextjsMiddleware. This function can also be used to compose other middleware behaviors.

This function has as arguments the NextRequest, the NextFetchEvent, and the ConvexAuthNextjsContext. convexAuth.isAuthenticated() and convexAuth.getToken() function similarly to isAuthenticatedNextjs and convexAuthNextjsToken, but should be used in middleware to ensure they reflect any updates to the request context from convexAuthNextjsMiddleware.

Convex Auth provides an API and helper functions for implementing your middleware:

  • createRouteMatcher is a helper function that uses the same syntax (opens in a new tab) as the middleware config. You call it with a list of glob patterns, and it returns a function that given the NextRequest returns whether the route matches.

  • nextjsMiddlewareRedirect is a simple shortcut for triggering redirects:

    export function nextjsMiddlewareRedirect(
      request: NextRequest,
      pathname: string,
    ) {
      const url = request.nextUrl.clone();
      url.pathname = pathname;
      return NextResponse.redirect(url);
    }

    You can inline this code if you need more control over the target URL.

Configure cookie expiration

You can configure the expiration of the authentication cookie by passing a cookieConfig option to convexAuthNextjsMiddleware.

middleware.ts
export default convexAuthNextjsMiddleware(
  (request, { convexAuth }) => {
    // ...
  },
  { cookieConfig: { maxAge: 60 * 60 * 24 * 30 } },
); // 30 days

If you don't set this option, the cookie will be considered a "session cookie" and be deleted when the browser session ends, which depends from browser to browser.

Preloading and loading data

To preload or load data on your Next.js server from your Convex backend, you can use preloadQuery and fetchQuery (opens in a new tab) and the convexAuthNextjsToken function from @convex-dev/auth/nextjs/server:

app/TasksWrapper.tsx
import { convexAuthNextjsToken } from "@convex-dev/auth/nextjs/server";
import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import { Tasks } from "./Tasks";
 
export async function TasksWrapper() {
  const preloadedTasks = await preloadQuery(
    api.tasks.list,
    { list: "default" },
    { token: convexAuthNextjsToken() },
  );
  return <Tasks preloadedTasks={preloadedTasks} />;
}

Calling authenticated mutations and actions

You can call Convex mutations (opens in a new tab) and actions (opens in a new tab) from Next.js Server Actions (opens in a new tab) and POST or PUT Route Handlers (opens in a new tab).

app/example/page.tsx
import { api } from "@/convex/_generated/api";
import { fetchMutation, fetchQuery } from "convex/nextjs";
import { revalidatePath } from "next/cache";
 
export default async function PureServerPage() {
  const tasks = await fetchQuery(api.tasks.list, { list: "default" });
  async function createTask(formData: FormData) {
    "use server";
 
    await fetchMutation(
      api.tasks.create,
      {
        text: formData.get("text") as string,
      },
      { token: convexAuthNextjsToken() },
    );
    revalidatePath("/example");
  }
  // render tasks and task creation form
  return <form action={createTask}>...</form>;
}
⚠️

Security notice: ConvexAuthNextjsServerProvider uses cookies to store authentication state. Therefore to prevent CSRF attacks (opens in a new tab) you must not perform any side-effects from the Next.js server on GET requests. This means that only Convex queries are safe to call from Server Components and GET Route Handlers.

Essentially, a malicious site might cause your user's browser to make an authenticated GET request without the user's permission, but it won't be able to read the response. Outside of GET requests, Convex Auth makes authentication state available only to same-origin requests.

Convex Auth is not special here, and the same security considerations apply to most other authentication solutions.