Back to blog
Article

Implementing Google OAuth Authentication With Auth.js Middleware in Next.js

Notes on wiring Google OAuth with Auth.js, protecting routes with middleware, and keeping the authentication flow readable.

Setting up authentication is rarely hard because of one line of code. It gets messy when sign-in, session handling, and route protection all start living in different places.

This post is a short version of the setup I like when I want Google OAuth in a Next.js app with Auth.js and route-level protection that stays understandable a few weeks later.

Start with a clear auth boundary

Before middleware, I like to keep the authentication setup in one place:

  • providers
  • callbacks
  • session strategy
  • exported helpers used by the rest of the app

That gives the rest of the codebase a small surface area instead of spreading auth-specific logic across pages and components.

Keep middleware focused

Middleware works best when it answers one question: can this request continue?

In practice that usually means:

import NextAuth from "next-auth";
import authConfig from "./auth.config";

const { auth } = NextAuth(authConfig);

export default auth((req) => {
  const isLoggedIn = Boolean(req.auth);
  const isProtectedRoute = req.nextUrl.pathname.startsWith("/dashboard");

  if (isProtectedRoute && !isLoggedIn) {
    return Response.redirect(new URL("/login", req.nextUrl));
  }
});

That shape is simple on purpose. Middleware should not become the place where every auth rule in the app gets reimplemented.

Separate public and protected routes early

It helps to decide this up front:

  • routes anyone can read
  • routes that require a session
  • auth routes such as login or callback pages

Once those buckets are explicit, redirects become predictable and the middleware stays small.

Why this structure works well

The biggest benefit is not only security. It is maintenance.

When auth is organized clearly:

  • route protection is easier to reason about
  • debugging redirect loops gets simpler
  • adding a new protected area feels mechanical instead of risky

Takeaway

The goal is a setup that remains boring after the first implementation. A clean Auth.js configuration plus narrow middleware logic usually gets there faster than a more clever structure.

I plan to publish more code-focused notes here as the blog grows, especially around authentication, Astro, and full stack project architecture.