Google One Tap Authentication in Next.js
Jakaria Masum
Posted on November 26, 2024
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:
- A Next.js project set up
- NextAuth.js installed in your project
- 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,
};
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 };
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"
/>
);
}
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>
);
}
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>
);
}
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
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.
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!
Posted on November 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.