Google One Tap Authentication in Next.js
Jakaria Masum

Jakaria Masum @jakaria

About: Full-Stack Developer | MERN Stack | Next.js | TypeScript

Location:
Bogura, Bangladesh
Joined:
Jun 26, 2024

Google One Tap Authentication in Next.js

Publish Date: Nov 26 '24
15 4

Google One Tap is a streamlined authentication method that allows users to sign in to your application with a single tap, using their Google account. In this blog post, we'll walk through the process of implementing Google One Tap in a Next.js application using NextAuth.js.

Prerequisites

Before we begin, make sure you have:

  1. A Next.js project set up
  2. NextAuth.js installed in your project
  3. A Google Cloud Console project with OAuth 2.0 credentials

Step 1: Set up Auth Options for next-auth configuration

First, let's configure NextAuth.js to work with Google authentication.
Create a file src/utils/authOptions.ts:

import { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async signIn({ user, account }) {
      if (account?.provider === "google") {
        try {
          console.log("user:", user, "\naccount:", account);
          return true;
        } catch (error) {
          console.error("Error during OAuth login:", error);
          return false;
        }
      }
      return true;
    },
  },

  secret: process.env.NEXTAUTH_SECRET,
};
Enter fullscreen mode Exit fullscreen mode

In console.log you will get user account information like name,image,email etc which can be used for other works such stores in database.

Step 2: Set Up NextAuth.js

Create a file app/api/auth/[...nextauth]/route.ts:

import { authOptions } from "@/utils/authOptions";
import NextAuth from "next-auth";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Google One Tap Component

Next, let's create a GoogleOneTap component that will handle the Google One Tap functionality:

"use client";

import { useEffect, useCallback, useState } from "react";
import { signIn, useSession } from "next-auth/react";
import Script from "next/script";

declare global {
  interface Window {
    google: {
      accounts: {
        id: {
          initialize: (config: any) => void;
          prompt: (callback: (notification: any) => void) => void;
          cancel: () => void;
          revoke: (hint: string, callback: () => void) => void;
        };
      };
    };
  }
}

export default function GoogleOneTap() {
  const { data: session } = useSession();
  const [isGoogleScriptLoaded, setIsGoogleScriptLoaded] = useState(false);
  console.log(session);

  const handleCredentialResponse = useCallback((response: any) => {
    signIn("google", {
      credential: response.credential,
      redirect: false,
    }).catch((error) => {
      console.error("Error signing in:", error);
    });
  }, []);

  const initializeGoogleOneTap = useCallback(() => {
    if (window.google && !session) {
      try {
        window.google.accounts.id.initialize({
          client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
          callback: handleCredentialResponse,
          context: "signin",
          ux_mode: "popup",
          auto_select: false,
          use_fedcm_for_prompt: true,
        });

        window.google.accounts.id.prompt((notification: any) => {
          if (notification.isNotDisplayed()) {
            console.log(
              "One Tap was not displayed:",
              notification.getNotDisplayedReason()
            );
          } else if (notification.isSkippedMoment()) {
            console.log(
              "One Tap was skipped:",
              notification.getSkippedReason()
            );
          } else if (notification.isDismissedMoment()) {
            console.log(
              "One Tap was dismissed:",
              notification.getDismissedReason()
            );
          }
        });
      } catch (error) {
        if (
          error instanceof Error &&
          error.message.includes(
            "Only one navigator.credentials.get request may be outstanding at one time"
          )
        ) {
          console.log(
            "FedCM request already in progress. Waiting before retrying..."
          );
          setTimeout(initializeGoogleOneTap, 1000);
        } else {
          console.error("Error initializing Google One Tap:", error);
        }
      }
    }
  }, [session, handleCredentialResponse]);

  useEffect(() => {
    if (isGoogleScriptLoaded) {
      initializeGoogleOneTap();
    }
  }, [isGoogleScriptLoaded, initializeGoogleOneTap]);

  useEffect(() => {
    if (session) {
      // If user is signed in, cancel any ongoing One Tap prompts
      window.google?.accounts.id.cancel();
    }
  }, [session]);

  return (
    <Script
      src="https://accounts.google.com/gsi/client"
      async
      defer
      onLoad={() => setIsGoogleScriptLoaded(true)}
      strategy="afterInteractive"
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

If you want to make automatic signin using google then set auto_select into true.

Step 4: Integrate Google One Tap into Your main layout

Now, let's update your layout to include the Google One Tap component:

"use client";
import GoogleOneTap from "@/components/GoogleOneTap";
import { SessionProvider } from "next-auth/react";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <SessionProvider>
          {children}
          <GoogleOneTap />
        </SessionProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

The layout must be wrap with sessionProvider as it helps to detect the user information in our whole application.

Step 5: Set Up Home page to see the demo

"use client";

import { useSession, signOut } from "next-auth/react";

export default function Home() {
  const { data: session } = useSession();

  return (
    <div className="flex flex-col items-center justify-center min-h-screen py-2">
      <main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
        <h1 className="text-6xl font-bold">
          Welcome to{" "}
          <span className="text-blue-600">Google One tap login!</span>
        </h1>

        {session ? (
          <>
            <p className="mt-3 text-2xl">
              You are signed in as
              <div className="flex flex-col">
                <span>Name:{session.user?.name}</span>
                <span>Email: {session?.user?.email}</span>
              </div>
            </p>

            <button
              className="mt-4 px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
              onClick={() => signOut()}
            >
              Sign out
            </button>
          </>
        ) : (
          <p className="mt-3 text-2xl">
            You are not signed in. Google One Tap should appear shortly.
          </p>
        )}
      </main>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Step 5: Set Up .env file

Create a .env.local file in your project root and add the following variables:

GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
NEXTAUTH_SECRET=your_nextauth_secret
NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id
Enter fullscreen mode Exit fullscreen mode

Replace the placeholder values with your actual Google OAuth credentials.

Conclusion

With these steps, you've successfully implemented Google One Tap authentication in your Next.js application. Users can now sign in with a single click using their Google account.

Image description

Image description

Remember to handle error cases and edge scenarios in a production environment. Also, consider adding more robust user management features as needed for your application.

Happy coding!

Comments 4 total

  • RunningDog
    RunningDogJan 9, 2025

    Thanks a lot !!!!!!!!! It works!!!!!!!

  • Smart Learners
    Smart LearnersFeb 2, 2025

    Does this safe with this update?

    The Google Sign-In library optionally uses FedCM APIs, and their use will become a requirement. Conduct an impact assessment to confirm that user sign-in continues to function as expected.

    • Jakaria Masum
      Jakaria MasumFeb 7, 2025

      Thank you for your comment! Yes, Google One Tap login is safe with updates if you use the latest Google Sign-In library, which is maintained to align with FedCM and other evolving standards. Regularly check Google’s Identity Services documentation and test your implementation to ensure continued functionality. Let me know if you'd like further guidance!

  • Dinesh Kumar M
    Dinesh Kumar MFeb 15, 2025

    Hi Please help me in this issue while sigin using popup after its closing time the console error i am facing Cross-Origin-Opener-Policy policy would block the window.postMessage call. and also after call sign('google') its redirecting and asking again to login

Add comment