DEV Community

Peter Jacxsens
Peter Jacxsens

Posted on • Updated on

4/ NextAuth with GoogleProvider: signing in

In this chapter we're going to install NextAuth and setup a basic example with Google provider. Note that our Next app only has a single page, the home page (/app/index) and a single component <Navbar /> that has one link to the home page. The finished code for this chapter is available on github (branch: basicgoogleprovider).

Install

Install NextAuth in the frontend folder:

npm i next-auth
Enter fullscreen mode Exit fullscreen mode

Next, we setup a route handler for NextAuth. We create a route.ts file in a new folder src/app/api/auth/[...nextauth]/route.ts. The [...nextauth] folder is a catch all route in Next. This means that all requests to api/auth or to for example api/auth/local/register will be handled by our newly created route.ts route handler. Put this inside the handler:

// frontend/src/app/api/auth/[...nextauth]/route.ts

import NextAuth from 'next-auth';
import { authOptions } from './authOptions';

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
Enter fullscreen mode Exit fullscreen mode

Create the authOptions.ts file that gets referred to above.

Note here: authOptions is just an object with configuration and customizations properties. Most tutorials just write it as an object literal in the route handler. However, when running Next build it gave me a TypeScript error at a certain point. One of the NextAuth authors suggested to place the authOptions in a separate file. This did solved the problem and that is why we are putting authOptions in a separate file.

// frontend/src/app/api/auth/[...nextauth]/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 ?? '',
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

What happens here: we add our first provider to the providers list. We then configure GoogleProvider that we import from NextAuth. The client id and secret come from the .env file that we wrote in chapter 1.

By the way, to read the NextAuth docs on this, just google NextAuth provider google and that will lead you to the docs page where you will see this setup.

Finally, add following lines to your .env.local file: (This isn't strictly required in development but will prompt warnings in your terminal.)

# frontend/.env.local

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=EDITME->somesecret
Enter fullscreen mode Exit fullscreen mode

Where NEXTAUTH_SECRET is a secret you need to generate. For windows user, open bash and enter following:

openssl rand -base64 32
Enter fullscreen mode Exit fullscreen mode

Copy the return string and paste it into your .env.local file. Other platforms I'm not sure, look it up.

Our first login with GoogleProvider in NextAuth

We are now ready for our first login. Yes, it is that easy. We create a login button:

// src/components/header/SignInButton.tsx

'use client';

import { signIn } from 'next-auth/react';

export default function SignInButton() {
  return (
    <button
      type='button'
      className='bg-sky-400 rounded-md px-4 py-2'
      onClick={() => signIn()}
    >
      sign in
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Firstly, we make it a client component because our button needs an event handler. In the onClick event we pass signIn. This is a function from NextAuth that when called will start the sign in procedure.

We only have our homepage right now but that is ok. In our current NextAuth configuration there is no custom login page and NextAuth will redirect us to a NextAuth default login page. We will customize this later with our own page. Let's run our Next frontend and click the sign in button. We are redirected to http://localhost:3000/api/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2F (with callback parameter to home page):

default signin page nextauth

This is the default login page NextAuth provides and there are some things we need to say about this:

  1. Obviously, it not a nice design.
  2. You cannot change this page with f.e. other styling.
  3. If you're not coding along you won't know this but clicking the login button triggered a full page reload (F5 reload).

Conclusion, this is not something you would want to use in production but here, it does serve some purpose. Let's click the Sign in with Google link and see what happens:

  • We are redirected to https://accounts.google.com/o/oauth2/v2/auth/... (only on first login)
  • We are asked what google account we want to use. (only on first login)
  • We are asked if we want to login to -appname- and that means that Google will share following data -some data-. (only on first login) (These align with the settings we made in chapter 1, Google OAuth setup)
  • On clicking continue we are redirected to our frontend homepage localhost:3000.

If we logout and login again (which we can't yet), the first 3 steps will be skipped. This is just the classic authentication flow with Google as I'm sure we've all done.

But, it works, so yay! What we're missing is feedback. Are we logged in or not? We are but we don't know and that is our next step.

NextAuth Session

We are now signed in using NextAuth. Practically, that means that NextAuth verified us with Google and then set some cookies, one with a JWT token. But, we don't have to worry about cookies.

NextAuth provides us with 2 ways to verify if a user is logged in:

  1. For client components: the useSession hook.
  2. For server components: the getServerSession function.

NextAuth useSession hook

useSession is a hook, so you can only use it in client components. It's just a hook, so you call it (no parameter required) and it returns an object that you destructure.

'use client';
import { useSession } from 'next-auth/react';

const { data: session, status, update } = useSession();
Enter fullscreen mode Exit fullscreen mode

For now we ignore status and update and focus on the data property that we rename to session. Our session will be either null/undefined (not logged in) or an object of type DefaultSession:

{
  user?: {
    name?: string | null
    email?: string | null
    image?: string | null
  }
  expires?: string
}
Enter fullscreen mode Exit fullscreen mode

This session interface can and will be customized, meaning we put more data on it.

NextAuth getServerSession() function

getServerSession is an async function that you use in server components or route handlers.

  • It takes one parameter, the authOptions object that we pass in our app/api/auth/[...nextAuth]/route.ts route handler.
  • It returns either null (not logged in) or the default session from just above.
  • Notice the async and await keywords!
import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/authOptions';

export default async function MyComponent() {
  const session = await getServerSession(authOptions);
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Let's add this to our code to get a better feel for it. Firstly though, to run useSession, we have to wrap our entire app into a provider that NextAuth gives us.

NextAuth SessionProvider

Things get a bit complicated here but in the end it is just a configuration so don't worry about it too much.

The problem is that the SessionProvider that NextAuth gives us is a client component because it uses React hooks. But, since NextAuth v4 is getting a bit older, it doesn't use the use client directive. This makes Next throw an error.

You can read more about this problem and how to solve it in the Next docs. The solution is to import SessionProvider in a client component and immediately export it like this:

// frontend/src/app/component/Provider.tsx

'use client';
import { SessionProvider } from 'next-auth/react';
export default SessionProvider;
Enter fullscreen mode Exit fullscreen mode

We now can use this <Provider /> component to wrap around our app inside our root layout file:

// frontend/src/app/layout.tsx

//...
import NavBar from '@/components/header/Navbar';
import { SessionProvider } from 'next-auth/react';
import { getServerSession } from 'next-auth';
import { authOptions } from './api/auth/[...nextauth]/authOptions';
//...

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const session = await getServerSession(authOptions);
  return (
    <html lang='en'>
      <body className={`${inter.className}  px-2 bg-zinc-200`}>
        <SessionProvider session={session}>
          <div className='max-w-6xl mx-auto'>
            <NavBar />
            <main className='my-4'>{children}</main>
          </div>
        </SessionProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Note how we made RootLayout async but again, don't worry to much about this, in the end it is just configuration. We continue in the next chapter.


If you want to support my writing, you can donate with paypal.

Top comments (0)