Skip to main content

NextAuth with Google Provider and Prisma Adapter

Overview

Welcome to the world of seamless authentication using NextAuth.js with the Google Provider and Prisma Adapter. In this guide, we'll walk you through the process step by step, ensuring you grasp each concept fully. Whether you're new to NextAuth or a seasoned developer, you're in for an enjoyable journey filled with insights and links to fine-tune your authentication flow to match your project's unique requirements.

Prerequisites

Before we dive into the implementation details, ensure you have a basic understanding of NextAuth.js, Google Cloud, and Prisma. If you need a quick refresher, feel free to explore the relevant documentation: NextAuth.js, Google Cloud, and Prisma.

Google Cloud Setup

To get started, let's connect your project to Google Cloud. Follow these steps:

  1. Open the Google Cloud Console.
  2. Select your project from the list of available projects, or create a new one if needed.
  3. Refer to this video guide for a complete setup tutorial for Google Cloud.
  4. When creating your credentials, ensure that the "Authorized redirect URIs" include both your full domain and the callback path. Examples:
    • For production: https://{YOUR_DOMAIN}/api/auth/callback/google
    • For development: http://localhost:3000/api/auth/callback/google
  5. After creating an OAuth client, Google Cloud Console will provide you with a CLIENT_ID and CLIENT_SECRET. Keep these safe and store them securely. You can save them in your .env file or adapt to your preferred configuration as shown below:
GOOGLE_CLIENT_ID= YOUR_CLIENT_ID_GOES_HERE // your CLIENT_ID provided by Google Cloud Console
GOOGLE_CLIENT_SECRET= YOUR_CLIENT_SECRET_GOES_HERE // your CLIENT_SECRET provided by Google Cloud Console

Prisma Adapter Setup

Caution

If you are using SQLite database with Prisma, then you need to remove @db.Text text from the prisma/schema.prisma file.

Please follow all the steps provided in the official NextAuth's PrismaAdapter.

In the package.json file, we have defined where the Prisma schema is. Here is the example code:

"prisma": {
"schema": "./src/prisma/schema.prisma"
}

In the src/prisma/schema.prisma file, we used SQLite database as the provider, with env("DATABASE_URL") specified as the URL in the datasource db. Here is the example code:

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}

In the code above, we have used env("DATABASE_URL") as the URL. This URL must be defined in the .env file. Here is the example code:

DATABASE_URL=file:./dev.db

However, you may alter the above settings to as per your database.

You are free to add or remove tables and columns as per your requirements in the src/prisma/schema.prisma file.

After saving your schema, use the Prisma CLI to generate the Prisma Client by running the following command:

npx prisma generate

To configure your database to use the new schema (e.g. creating tables and columns), run the following command:

pnpm migrate

If you want to watch the preview of the Prisma database, you may run the following command:

npx prisma studio

Initialize NextAuth.js

Next.js 13.2 introduced Route Handlers, the preferred way to handle REST-like requests in App Router (app/).

You can initialize NextAuth.js with a Route Handler too, very similar to API Routes.

src/app/api/auth/[...nextauth]/route.ts
// Third-party Imports
import NextAuth from 'next-auth'

// Lib Imports
import { authOptions } from '@/libs/auth'

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

Internally, NextAuth.js detects that it is being initialized in a Route Handler (by understanding that it is passed a Web Request instance, and will return a handler that returns a Response instance. A Route Handler file expects you to export some named handler functions that handle a request and return a response. NextAuth.js needs the GET and POST handlers to function properly, so we export those two.

info

Technically, in a Route Handler, the api/ prefix is not necessary, but we decided to keep it required for an easier migration.

Please refer to the NextAuth official documentation for advanced initialization.

auth.ts file

The implementation of the authentication logic should be performed within the src/libs/auth.ts file. It is advisable to tailor this file to suit the specific requirements of your project. The necessary configurations and instructions for customizing can be located in the NextAuth official documentation.

Before you embark on your journey of customization, take a moment to understand the contents and functioning of the src/libs/auth.ts file. Let's dive in!

note

All the options used in the src/libs/auth.ts file are outlined and explained below. Please refer to the official NextAuth documentation for a comprehensive list of available options.

providers

We utilize the GoogleProvider which enables the handling of sign-in through Google. Multiple authentication providers can be configured simultaneously.

The following options are available within GoogleProvider:

  • clientId: The client ID of your Google Cloud project
  • clientSecret: The client secret of your Google Cloud project.

Kindly consult the official NextAuth documentation for more providers options.

secret

It is a random string used to hash tokens, sign/encrypt cookies and generate cryptographic keys.

If the NEXTAUTH_SECRET is set as an environment variable, it is not necessary to define this option. A secret can be generated by visiting the documentation link provided below.

Kindly consult the official NextAuth documentation for more details on secret.

session

`strategy`

You are required to make a choice on how you wish to preserve the user session. The default option is jwt, which involves the storage of an encrypted JWT (JWE) within a session cookie. If you elect to use an adapter, the default setting will be changed to database instead. It is still possible to explicitly specify jwt and retain a JWT session. If the option database is chosen, the session cookie will only hold a sessionToken value, which will then be used to retrieve the session information from the database.

`maxAge`

The duration of an idle session until it expires and becomes invalid. It accepts a number in seconds.

Kindly consult the official NextAuth documentation for more session options.

pages

Specify URLs to be used if you want to create custom sign in, sign out and error pages. Pages specified will override the corresponding built-in page.

Kindly consult the official NextAuth documentation for more pages options.

callbacks

Callbacks are asynchronous functions you can use to control what happens when an action is performed. Callbacks are extremely powerful, especially in scenarios involving JSON Web Tokens as they allow you to implement access controls without a database and to integrate with external databases or APIs.

`jwt`

This callback is called whenever a JSON Web Token is created (i.e. at sign in) or updated (i.e whenever a session is accessed in the client). The returned value will be encrypted, and it is stored in a cookie.

When utilizing the jwt strategy within the session option, the jwt() callback will be executed prior to the session() callback. The data returned by the authorize function in the providers option will be passed to the jwt() callback in the form of the token.

To include custom parameters in the session() callback, they must be added to the token in the jwt() callback, which will then be transferred to the session() callback for further processing.

Kindly consult the official NextAuth documentation for more details on jwt() callback.

`session`

The session callback is called whenever a session is checked. By default, only a subset of the token is returned for increased security. If you want to make something available you added to the token (like access_token and user.id from above) via the jwt() callback, you have to explicitly forward it here to make it available to the client.

Kindly consult the official NextAuth documentation for more details on session() callback.

Kindly consult the official NextAuth documentation for more callbacks options.

We hope that with the information provided, you have now acquired a comprehensive understanding of all the options available within the auth.ts or auth.js file.

Login Form

We can create a login form which calls the signIn function from the next-auth/react library. The signIn function will call the login API and return the user data if the credentials are valid.

Here is the example code for the login page:

'use client'

// Third-party Imports
import { signIn } from 'next-auth/react'

const Login = () => {
return (
<button onClick={() => signIn('google')}>Login with google</button>
)
}

export default Login

Extending NextAuth Types for Custom User Fields

In your dynamic and vibrant app, you might have a unique role field nestled within your user object. Fear not, for we shall seamlessly guide you in extending the NextAuth types to gracefully accommodate this distinct feature. 🎩 ✨

The first stop on our customization tour is the next-auth.d.ts file. This TypeScript declaration file plays a crucial role in modifying the types specified by NextAuth. Locate or create this file in the root directory of your project. Don't worry, you don't need a magic wand – just a few keyboard strokes! ⌨️ 🔮

Let's say your user objects have a charming role field that you'd like to include in the NextAuth types. This field adds a touch of distinction to your users. To achieve this, follow these simple steps:

  1. Open/create next-auth.d.ts file.
  2. Your declaration should look something like this:
import 'next-auth/jwt'
import { DefaultSession } from 'next-auth'

declare module 'next-auth/jwt' {
type JWT = {
role: string
}
}

declare module 'next-auth' {
type Session = {
user: {
role: string
} & DefaultSession['user']
}

type User = {
role: string
}
}

After adding the role field declaration, the NextAuth types are now aware of your custom user data.

Now that you've extended the NextAuth types, it's time to put your custom field to good use in your authentication flow.

First let's add role into the user session, open src/libs/auth.ts file and add role in JWT and Session callbacks like following:

src/libs/auth.ts
callbacks: {
/*
* While using `jwt` as a strategy, `jwt()` callback will be called before
* the `session()` callback. So we have to add custom parameters in `token`
* via `jwt()` callback to make them accessible in the `session()` callback
*/
async jwt({ token, user }) {
if (user) {
/*
* For adding custom parameters to user in session, we first need to add those parameters
* in token which then will be available in the `session()` callback
*/
token.name = user.name
token.role = user.role
}

return token
},
async session({ session, token }) {
if (session.user) {
// ** Add custom params to user in session which are added in `jwt()` callback via `token` parameter
session.user.name = token.name
session.user.role = token.role
}

return session
}
}

Whenever you're accessing user data through NextAuth, you can access the role field like a pro:

// Accessing user data with the role field
import { useSession } from 'next-auth/react';

// Inside your component...
const { data: session } = useSession();
const userRole = session?.user?.role || 'defaultRole';
info

Please note that customization of NextAuth to meet specific requirements, such as modifying the session strategy, adjusting session expiration times, designating authentication pages, and customizing callback functions, are not considered part of the support. These elements will vary based on the specific implementation.

  • To implement Prisma (SQLite) with Next.js, check out this video
  • To implement NextAuth with PrismaAdapter (Planetscale), check out this video
  • To implement NextAuth with CredentialProvider, refer to this article.