4/ NextAuth with GoogleProvider: signing in
Tags: nextauth, googleprovider, nextjs
Published: 2024-04-01 16:18:04

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)](https://github.com/peterlidee/NNAS/tree/basicgoogleprovider).

## Install

Install `NextAuth` in the **frontend folder**:

```
npm i next-auth
```

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:

```ts
// 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 };
```

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.

```ts
// 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 ?? '',
    }),
  ],
};
```

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](https://dev.to/peterlidee/next-nextauth-strapi-setting-up-our-project-4jc7).

By the way, to read the `NextAuth` docs on this, just google `NextAuth provider google` and that will lead you to the [docs page](https://next-auth.js.org/providers/google) 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
```

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

```
openssl rand -base64 32
```

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:

```jsx
// 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>
  );
}
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0p1i12bhqhpymuc2ns0.png)

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](https://dev.to/peterlidee/next-nextauth-strapi-setting-up-our-project-4jc7))
- 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.

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

const { data: session, status, update } = useSession();
```

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:

```ts
{
  user?: {
    name?: string | null
    email?: string | null
    image?: string | null
  }
  expires?: string
}
```

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!

```tsx
import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/authOptions';

export default async function MyComponent() {
  const session = await getServerSession(authOptions);
  // ...
}
```

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](https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#using-third-party-packages-and-providers). The solution is to import `SessionProvider` in a client component and immediately export it like this:

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

'use client';
import { SessionProvider } from 'next-auth/react';
export default SessionProvider;
```

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

```tsx
// 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>
  );
}
```

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](https://www.paypal.com/donate/?hosted_button_id=4D78YQU4V5NEJ).