Setting Up NextAuth, GitHub, and Prisma: A Step-by-Step Guide

Lee Robinson

Paul Berthelot / April 04, 2023

6 min read–––

Hey there! Today, I want to share my experience with NextAuth.js. As you know, authentication can be quite challenging, but NextAuth.js is the game-changer we've all been waiting for! This incredible library streamlines authentication in Next.js applications, allowing you to seamlessly work with various providers like GitHub, Google, and many more.

Let's dive into how NextAuth.js operates. First, you'll need to create a [...nextauth].ts file in your project's pages/api/auth folder. This file serves as an API endpoint, and NextAuth.js manages all aspects of authentication, from signing in and signing out to handling OAuth flows.

One aspect of NextAuth.js that I truly appreciate is the choice between JSON Web Tokens (JWT) and session-based authentication. With JWT, authentication data is stored in a secure token that's exchanged between the client and the server. Session-based authentication stores the authentication data on the server, with the client holding a session ID.

For most applications, I recommend to choose session-based token. With session-based authentication, the server can store more user information, and managing user sessions becomes a breeze. Additionally, I can invalidate a user's session server-side if needed, which greatly enhances security.

Now let's setting up NextAuth, GitHub, and Prisma in your Next.js application. So, let's dive right in.

Before we begin, make sure you have Next.js, Prisma, and NextAuth installed in your project. If you haven't done that yet, go ahead and follow the official installation guides for each of them.

Alright, now that we're all set, let's take a look at the [...nextauth].ts file in your project's pages/api/auth folder:

pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { prisma } from "#/lib/prismaclient";

export const authOptions = {
  callbacks: {
    // 'user' represents the user object stored in our database.
    // By customizing the session object, we can selectively pass
    // the desired user information to the client and throughout
    // our API.
    async session({ session, token, user }: any) {
      session.user.id = user.id;
      return session;
    },
  },
  adapter: PrismaAdapter(prisma),
  secret: process.env.SECRET,
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
  ],
};

export default NextAuth(authOptions);

We start by importing the required packages: NextAuth, GithubProvider, PrismaAdapter, and our Prisma client. Then, we define the authOptions object, which will be used to configure our NextAuth instance.

  1. callbacks: We define a custom session callback to include the user's ID in the session object (the informations you want to put in the session will depend on your application use case).
  2. adapter: We use the PrismaAdapter and pass in our Prisma client to integrate with the Prisma ORM.
  3. secret: We set the application secret using an environment variable. To generate a secret just run openssl rand -base64 32 in your terminal.
  4. providers: We define the GitHub provider by passing in the GitHub app's client ID and secret.

Finally, we export our NextAuth instance with the configured authOptions.

Now, let's take a look at the #/lib/prismaclient.ts file:

lib/prismaclient.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = global as unknown as { prisma: PrismaClient };

export const prisma = globalForPrisma.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

In this file, we import the PrismaClient from the @prisma/client package. We then create a global object to store the Prisma client instance, which helps prevent multiple instances of the Prisma client in development.

We export the prisma object, which is either the existing instance or a new one. Finally, we set the global prisma object to the instance we just created if we're in a non-production environment. This code was taken from the official documentation of Prisma.

The PrismaAdapter we use in the [...nextauth].ts file needs the following schemas. Note that you can add more user attributes depending of your application use case.

prisma/schema.prisma
datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id            String           @id @default(cuid())
  name          String?
  email         String?          @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?
  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
  @@index(userId)
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index(userId)
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

Now let's see how to create a GitHub Application ID and secret for NextAuth.js authentication. These credentials will enable users to log in to your application using their GitHub accounts. Let's get started!

  1. First, head over to the GitHub Developer settings page by visiting https://github.com/settings/developers. Here, you'll find an overview of your existing OAuth apps (if you have any). To create a new one, click on the "New OAuth App" button.

  2. Fill out the "Application name," "Homepage URL," and "Application description" fields with the relevant information for your app. The "Homepage URL" should be the URL of your app's landing page (e.g., https://your-app-domain.com).

  3. Next, enter the "Authorization callback URL." This is where GitHub will redirect the user after they've successfully authorized your app. For NextAuth.js, the callback URL should be in the format: https://your-app-domain.com/api/auth/callback/github.

  4. Click the "Register application" button to create your new OAuth app. After registration, you'll be redirected to your app's settings page.

  5. On the app settings page, you'll find your newly generated "Client ID" and "Client Secret." Make sure to keep these credentials safe, as they're essential for authenticating users with GitHub through NextAuth.js.

Now that you have your GitHub Application ID and secret, you can plug them into your NextAuth.js configuration. In your [...nextauth].ts file, update the providers array with the GithubProvider and include your clientId and clientSecret:

pages/api/auth/[...nextauth].ts
providers: [
  GithubProvider({
    clientId: process.env.GITHUB_ID,
    clientSecret: process.env.GITHUB_SECRET,
  }),
],

To work with local and production environment, you will need to create two OAuth apps. For the local development, use this url format: http://localhost:3000/api/auth/callback/github.

And that's it! You now have a working setup for NextAuth, GitHub, and Prisma in your Next.js application. With this setup, you can easily manage user authentication using GitHub and handle your application's data with Prisma.

Subscribe to the newsletter

Get emails from me about web development, tech trends, and advices for founders.

24 subscribers