Vercel deployment issue with google oauth

I have checked my Vercel production environment variables multiple times—they are all perfectly configured.
My Google Cloud OAuth credentials are also correctly set up and match what I used in the code.

However, when I click the “Sign in with Google” button in production, I am redirected back to the login page instead of going to the dashboard. The URL shows the following query parameter:

js

url.searchParams.set("error", "unauthenticated");

The same configuration works perfectly in my local environment—I’m able to sign in with Google and access the dashboard.

There is no other error or access block shown from google.

Here’s my lib/auth.js and middleware.js code for reference. Please help me fix the issue.

lib/auth.js
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  pages: {
    signIn: "/login",
  },
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user?.email) {
        token.email = user.email;
      }
      return token;
    },
    async session({ session, token }) {
      if (token?.email) {
        session.user.email = token.email;
      }
      return session;
    },
  },

});

middleware.js
import { getToken } from "next-auth/jwt";
import { NextResponse } from "next/server";

const ALLOWED_EMAILS = ["admin@example.com"];

export async function middleware(request) {
  const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET });
  const email = token?.email;

  if (!token) {
    const url = new URL("/login", request.url);
    url.searchParams.set("error", "unauthenticated");
    return NextResponse.redirect(url);
  }
  
  if (!email || !ALLOWED_EMAILS.includes(email)) {
    const url = new URL("/login", request.url);
    url.searchParams.set("error", "unauthorized");
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};
1 Like

Hey! I came across this thread and wanted to know how things were going :slight_smile:

Could you check the following?

  • Have you set NEXTAUTH_SECRET in your Vercel production environment?
  • Is NEXTAUTH_URL set to your exact production domain (e.g., https://yourdomain.vercel.app)?
  • Are GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET correctly added to your Vercel environment variables?
  • Did you redeploy after updating your environment variables?
  • In Google Cloud Console, is your Authorized JavaScript origin set to your production URL?
  • Is your Authorized redirect URI exactly https://yourdomain.vercel.app/api/auth/callback/google?
  • Does the domain in Google OAuth settings match your Vercel production domain exactly?
  • Did you generate a valid NEXTAUTH_SECRET (e.g., using openssl rand -base64 32)?
  • Is the same NEXTAUTH_SECRET available in production?
  • Have you checked your Vercel function logs for authentication or environment variable errors?
  • Are there any warnings about missing or invalid environment variables?
  • Does the “unauthenticated” or JWT validation error persist after correcting your secrets?
  • Are all required variables set specifically in the Production environment, not just Preview or Development?