How to add Google authentication to your Next.js + Liveblocks app with NextAuth.js

Learn how to use NextAuth.js to integrate Google authentication with your Next.js + Liveblocks application—enabling personalization with users' names, photos, and more throughout your product.

How to add Google authentication to your Next.js + Liveblocks app with NextAuth.js

Why authenticate with Google?

Many users own or use Google products, making it practical to use Google for Single Sign-On (SSO) and authentication. SSO provides access to valuable metadata that we can use for personalization in collaborative workspaces. Additionally, implementing Single Sign-On improves security by allowing users to bypass entering their password repeatedly.

Using NextAuth.js to add Google authentication to our Next.js application

Because we build the majority of our Liveblocks examples with Next.js (including the Starter Kit), we recommend using Next.js providers for authentication. If you don’t want to use NextAuth.js, you can use the Google Platform Library for Google Single Sign On. Authentication providers and the client library are the only officially supported ways to include Google SSO as of March 2023.

Updating an existing Next.js + Liveblocks app

We will be updating the Live Avatar Example to include Google authentication. The Live Avatar Example is a Next.js application that uses Liveblocks to create a collaborative avatar stack. The application is a great starting point for adding authentication because it is a simple application that uses Liveblocks to create a collaborative experience. These are the steps we will take to add Google authentication to the Live Avatar Example:

Obtaining Google credentials

NextAuth.js has the concept of Providers: services such as GitHub, Google, etc. utilizing OAuth, which allows you to log in to your application using existing credentials.

Similar to our Starter Kit guide in which we detail how to get authentication credentials for GitHub and Auth0, we will create Google credentials to enable Google as a provider.

  1. First, navigate to Google Console. If you do not already have an account setup, you need to create a Google account and an associated project which is where you will generate credentials from.

  2. Once you have created the new project, navigate to APIs and Services. From the lefthand navigation, select Credentials and then + CREATE CREDENTIALS from the top portion of the page. Select "CONFIGURE CONSENT SCREEN."

  3. Set the "User Type" as External, and then Save. On the page that opens, fill out your application information, setting your application domain to http://localhost:3000 (or your production domain). Once you have filled out the information, click Save.

  4. The next page that will open is Scopes. Here, you can update the scopes to access different information from a user's account. For more information, you can review the Google documentation. For this example, we will use and userinfo.profile (these pieces include the profile image, email, and name).

  5. On the Test Users screen, you can add the Google accounts of people you will allow to access your app. If you do not add a user to your list, they can still log in with Google; however, their profile image will be empty.

  6. Navigate back to Credentials and select + CREATE CREDENTIALS and select OAuth client ID. On the next page, select Web application as the Application type.

  7. Name your client, and update Authorized JavaScript origin to http://localhost:3000 and Authorized redirect URIs to http://localhost:3000/api/auth/callback/google. Note that we use localhost:3000 for local development, but you will want to update this portion of the URI when your app is in production. At this point, you will want to save your CLIENT_ID and CLIENT_SECRET. The CLIENT_SECRET will not be revealed again; if you lose the value, you will have to regenerate it.

Using Credentials in Liveblocks app

Now that we have the credentials for Google, we can bring them into our Liveblocks application and use this information to set up our NextAuth.js provider and populate our avatar profiles.

For this example, we will use the Live Avatar Stack as the baseline for implementing authentication. After cloning the Liveblocks repo, you should navigate to the Live Avatar example like so:

npx create-liveblocks-app@latest --example nextjs-live-avatars --api-key --install

First run npm install next-auth, npm install @svgr/webpack --save-dev, and finally, npm install from this directory level to install all package dependencies necessary for this example.

Generate two encrypted tokens for your Next.js app: a JWT_SECRET and a NEXTAUTH_SECRET. The JWT_SECRET is used for local development with Next.js, while the NEXTAUTH_SECRET is used in production.

To generate these values, we can run the following command: openssl rand -base64 32 and copy the output of each for adding to your env.local file.

In your env.local file, add the following values, and populate them:

$LIVEBLOCKS_SECRET_KEY="<your secret from the dashboard>"GOOGLE_CLIENT_ID="<your client id from the google console>"GOOGLE_CLIENT_SECRET="<your client secret from the google console>"NEXTAUTH_SECRET="<your NextAuth.js secret generated in the console in the previous step>"NEXTAUTH_URL="http://localhost:3000"JWT_SECRET="<your JWT secret generated in the console in the preivious step>"

Updating the NextAuth.js configuration

Next we will create the next-auth-d.ts file which will expose the SessionType to submodules across our app:

import { User } from "../types";import NextAuth from "next-auth";
declare module "next-auth" { /** * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context */ interface Session { user: User; }}

The User type defined above is not defined by default, so we should add its definition to a new types.ts file at the root directory level.

export type User = {  name: string;  email: string;  image: string;};

We will now add the Google Provider to pages/api/auth/[...nextauth].ts. This file configures the authorization to Google, which will be called when we implement sign-in and sign-out functionality further on in this tutorial.

import { authorize } from "@liveblocks/node";import { NextApiRequest, NextApiResponse } from "next";import NextAuth from "next-auth";import { Session } from "next-auth";import GoogleProvider from "next-auth/providers/google";
export const authOptions = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, authorization: { params: { prompt: "consent", access_type: "offline", response_type: "code", }, }, }), ], secret: process.env.JWT_SECRET,};
export default NextAuth(authOptions);

Using NextAuth.js to expose the session object across the app

Now that we have updated our authentication provider and ensured the types defined from NextAuth.js are accessible, we will wrap our app in the SessionProvider. Wrapping our application with the SessionProvider makes the session object that NextAuth.js generates available across the various components of your app. In our case, the session object would include your Google email, name, and profile image.

Update pages/_app.tsx to look like the following:

import { AppProps } from "next/app";import Head from "next/head";import { Session } from "next-auth";import { SessionProvider } from "next-auth/react";import "../styles/globals.css";
function App({ Component, pageProps }: AppProps<{ session: Session }>) { return ( <> <SessionProvider session={pageProps.session}> <Component {...pageProps} /> </SessionProvider> </> );}export default App;

Updating the Liveblocks demo authentication with values from NextAuth.js

At this point, we still have a few wiring pieces to hook up, namely, authentication from Liveblocks where we will use the information provided by Google to update our Liveblocks users, which will populate our Avatars.

We will be updating the file pages/api/auth.ts to do so.

First, we will create a utility function to import into our auth file. If you’re familiar with the Starter Kit, you may have realized that this function is also used there.

Create the following file in the pages/api/auth directory:

import type { GetServerSidePropsContext } from "next";import { unstable_getServerSession } from "next-auth/next";import { authOptions } from "../../../pages/api/auth/[...nextauth]";
export function getServerSession( req: GetServerSidePropsContext["req"], res: GetServerSidePropsContext["res"]) { return unstable_getServerSession(req, res, authOptions);}

Now in pages/auth/auth.ts, update the auth function to use the session and to pass that information to Liveblocks. We’re removing the random names and instead using the profile data from Google.

Your auth.ts file should look like the following:

import { authorize } from "@liveblocks/node";import { NextApiRequest, NextApiResponse } from "next";import { getServerSession } from "./auth/getServerSession";import { User } from "../../types";
const API_KEY = process.env.LIVEBLOCKS_SECRET_KEY;
export default async function auth(req: NextApiRequest, res: NextApiResponse) { if (!API_KEY) { return res.status(403).end(); } const session = await getServerSession(req, res);
const anonymousUser: User = { name: "Anonymous", email: "none", image: "N/A", }; const { name, email, image } = session?.user ?? anonymousUser;
// We're generating users and avatars here based off Google SSO metadata. //This is where you assign the // user based on their real identity from your auth provider. // See for more information const response = await authorize({ room:, secret: API_KEY, userId: `user-${email}`, userInfo: { name: name, picture: image, }, }); return res.status(response.status).end(response.body);}

Create redirect functionality when a user is not signed in

Now that we have established connections between Liveblocks and Google, we need to bring sign-in and redirect functionality into our room. First, we will set up a redirect that checks for a user’s session before allowing them into a Liveblocks room, and then we will create a sign-in page.

Update pages/index.tsx to getServerSideProps and enable redirect.

import React, { useMemo } from "react";import { Avatar } from "../components/Avatar";import Button from "../components/Button";import { RoomProvider, useOthers, useSelf } from "../liveblocks.config";import { GetServerSideProps } from "next";import { useRouter } from "next/router";import { getServerSession } from "./api/auth/getServerSession";import { signOut } from "next-auth/react";import styles from "./index.module.css";
function Example() { const users = useOthers(); const currentUser = useSelf(); const hasMoreUsers = users.length > 3;
return ( <div className="flex h-screen flex-col place-content-center place-items-center space-y-5"> <div className="flex w-full select-none place-content-center place-items-center"> <div className="flex pl-3"> {users.slice(0, 3).map(({ connectionId, info }) => { return ( <Avatar key={connectionId} picture={info.picture} name={} /> ); })}
{hasMoreUsers && ( <div className={styles.more}>+{users.length - 3}</div> )}
{currentUser && ( <div className="relative ml-8 first:ml-0"> <Avatar picture={} name="You" /> </div> )} </div> </div> <div> <Button onClick={() => signOut()}>Sign Out</Button> </div> </div> );}
export default function Page() { const roomId = useOverrideRoomId("nextjs-live-avatars");
return ( <RoomProvider id={roomId} initialPresence={{}}> <Example /> </RoomProvider> );}
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const session = await getServerSession(req, res);
if (!session) { return { redirect: { permanent: false, destination: "/signin", }, }; }
return { props: {}, };};

Update the referrer policy on the Avatar image for local development

When running the app locally, you may notice that the avatar images are not loading. This is because the referrer policy is set to strict-origin-when-cross-origin by default. To fix this, update the referrer policy on the Avatar component to no-referrer in components/Avatar.tsx:

import React from "react";import styles from "./Avatar.module.css";
const IMAGE_SIZE = 48;
export function Avatar({ picture, name }) { return ( <div className={styles.avatar} data-tooltip={name}> <img src={picture} height={IMAGE_SIZE} width={IMAGE_SIZE} className={styles.avatar_picture} referrerPolicy="no-referrer" /> </div> );}

Create the Google SVG icon

Before creating the sign-in page, we will add the Google SVG and configure our local environment to use it.

Create a new folder called public in the project's root, and inside it, create a folder called icons. Inside the icons folder, create a file called google.svg and copy the following code into it:

<svg aria-hidden="true" focusable="false" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns=""><path fill-rule="evenodd" clip-rule="evenodd" d="M17.64 9.204c0-.638-.057-1.251-.164-1.84H9v3.48h4.844a4.14 4.14 0 0 1-1.796 2.717v2.258h2.908c1.702-1.566 2.684-3.874 2.684-6.615Z" fill="#4285F4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.259c-.805.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18Z" fill="#34A853"/><path fill-rule="evenodd" clip-rule="evenodd" d="M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332Z" fill="#FBBC05"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.462.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58Z" fill="#EA4335"/></svg>

Create the next.config.js file, which handles the advanced configuration for loading SVGs during local development:

module.exports = {  webpack(config) {    config.module.rules.push({      test: /\.svg$/,      use: ["@svgr/webpack"],    });
return config; },};

Create the sign-in page

Now we need to create a sign-in page. Users will be redirected to the sign-in page if they are not logged in. We will use the next-auth/react package to handle the authentication.

Create the signin page:

import styles from "../components/SignIn.module.css";
import { GetServerSideProps } from "next";import { getProviders } from "next-auth/react";import { useSession, signIn } from "next-auth/react";import { getServerSession } from "./api/auth/getServerSession";import Button from "../components/Button";import GoogleIcon from "../public/icons/google.svg";
interface Props { providers: Awaited<ReturnType<typeof getProviders>>;}
export default function login({ providers }: Props) { const { data: session } = useSession(); if (!session) return ( <div className={styles.signin}> <Button className={styles.googlebutton} onClick={() => signIn("google")} > <GoogleIcon /> Sign in with Google </Button> </div> );}
// If not logged in redirect to signinexport const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const session = await getServerSession(req, res); const providers = await getProviders(); console.log(providers);
if (session) { return { redirect: { permanent: false, destination: "/", }, }; }
return { props: { providers }, };};

Create the SignIn css module:

.signin {  position: absolute;  width: 100vw;  height: 100vh;  display: flex;  flex-direction: column;  place-content: center;  place-items: center;  background-color: rgb(var(--color-surface));}
.googlebutton { user-select: none; gap: 0.5rem;}

We have completed the process of styling and adding the sign-in and sign out functionality to the app. Now that we have completed these steps, we can run the app and see our changes.

npm run dev

Open the console to localhost:3000, and you will see the sign-in screen.

We hope that this tutorial gives you the tools to populate avatars in a way that enhances your collaborative experience while improving your understanding of how authentication can work with NextAuth.js providers. Additionally, the Starter Kit is pre-configured with NextAuth.js, which means you can follow this tutorial to integrate personal data from Google into the preconfigured avatars. If you want to checkout the source code for this example, you can view it on GitHub. If you have any feedback or suggestions for future updates, please don’t hesitate to reach out to us via email, Twitter, or in our Discord community.