How to implement Google One Tap Sign-In with NextAuth in 2023

Alessandro Baccini
Geek Culture
Published in
4 min readFeb 28, 2023

--

This tutorial is based on this really good, yet a bit outdated, blog post from Fabrizio Ruggeri.

I will go through the steps illustrated by Fabrizio, adding more contextual information about NextAuth and fixing a few out-of-date details. I personally struggled a lot to follow his tutorial and I so I set out to make a more beginner-friendly version of it.

Overview

What we’ll need:

  • A custom CredentialsProvider
  • An invisible parent component to host the One Tap UI
  • A custom hook to implement the sign-in logic

Support libraries:

  • google-auth-library
  • @types/google-one-tap
  • prisma (i’m using prisma but you can use something else, the original example is with MongoDB)

What we’ll do:

We will create an invisible component, that hosts a custom hook. The custom hook, on render, uses NextAuth’s useSession to see if there’s an active session. If there is no active session it shows the Google One Tap UI.

When the user clicks on the sign-in button the NextAuth signIn function is called. This triggers a series of actions: checking if there is an existing session otherwise creating one, ditto for user and account (more info on this later).

This is pretty much it. Once the session is active NextAuth will be able to pick it up from anywhere in the client and the server with useSession and getServerSession.

Implementing a CustomCredentials Provider (backend)

The goal is to define what happens when a user tries to get authorized, in order to do this we need access to our database adapter, so in […nextauth].tsx we have to use the advanced initialization.

The most important things to understand here to keep the general thread are:

  • We are creating a custom Provider, with id ‘googleonetap
  • This Provider comes with a set of credentials that we define as a single field called credential of type text.
  • When an attempt at signing in is made with this provider, the authorize function is called and acredential must be supplied. The credential being a token generated by the Google backend (more on this in a minute).

Client Side Implementation (frontend)

If you are using TypeScript, before continuing make sure you have installed @types/google-one-tap.

We will follow Fabrizio’s example and use the Javascript API, as opposed to the HTML API, stowing the request for the Google user token in a custom hook so to hide any sensitive information.

Importing the Google library on the client

To make the google library available on time and make sure that the Google One Tap UI renders correctly, you have to add the import in the _document.tsx file. In fact, if you import it in _app.tsx the library sometimes won’t be loaded in time and raise all types of annoying errors.

For this we use the Next.js Script tag, as suggested by Fabrizio.

P.S. You may have seen this before implemented this way, placed in the document head:

<script src=”https://accounts.google.com/gsi/client" async defer></script>

I decided to go with the Script tag solution because more elegant and because it took advantage of framework-specific features.

The Custom Hook

This is where a good chunk of the magic happens. The key element here is the google.accounts.id.initialize method of the Google Auth Javascript API. As you can guess from the name, it initializes the Google One Tap UI and it can be rendered in two separate ways. One is to simply render it and the other one is to append it to an exsiting html node.

We’re going to use the second method. So that we don’t have to write the intialization code directly in the page but we can stow it away and then append the initialization logic to an html node/component of our choice.

Basically at every new page render, if there is no active session the Google One Tap UI will be rendered, and if the user clicks on it, the signIn function is called, passing the user token to the NextAuth backend, calling our CredentialsProvider googleonetap‘s authorize method and sending a request for the rest of the user’s info.

I got the idea for this imlementation from the NextAuth’s docs.

The invisible component

This component serves two purposes:

  1. calling the useOneTapSignin hook and thus intializing the Google One Tap UI
  2. Defining it’s own position on the screen, which will correspond with the position where the Google One Tap UI will appear. In the example I’m using Tailwind CSS but whatever design system you’re using I recommend you to make it sticky so that it doesn’t slide out of the viewport when users scroll down the page.

You should stick this component in your Layout component if you have one, otherwise in some other element that is present in every page.

--

--

Alessandro Baccini
Geek Culture

I encounter undocumented problems and I write notes in the form of posts. They're admittedly a a bit dry but I'm always open for clarification requests.