Implement Google & GitHub Login in Next.js with NextAuth
30 October 202417 min read
Implementing user authentication is a foundational part of building secure and dynamic web applications. In this article, we’ll walk through how to set up authentication in a Next.js application, using NextAuth for seamless integration with Google and GitHub OAuth providers, and Prisma to manage our user data in a database. By the end of this guide, you’ll have a solid authentication setup with external OAuth providers that store user data efficiently.
We’ll use the newly released Next.js version 15, PostgreSQL set up via Docker, Prisma for type safety, and ShadcnUI for fast UI development. By the end, we'll have an application where users can authenticate via Google or GitHub, with their data instantly stored in our database.
Prerequisites
- Basic knowledge of Next.js - Familiarity with Next.js and its folder structure will help.
- Node.js and npm installed - You’ll need both installed on your local machine.
- A GitHub and Google account - For testing the OAuth flow. Prisma CLI - Make sure you have the Prisma CLI installed, as we’ll use it to set up and migrate the database.
- Docker: Install Docker to easily manage your PostgreSQL database in a containerized environment. Refer to the Docker documentation for installation instructions.
- PostgreSQL: Familiarity with PostgreSQL is beneficial, as we will use it as our database.
- Familiarity with Prisma: Basic knowledge of Prisma ORM for managing database operations will be useful.
Steps to Create User Authentication
Let’s dive into setting up the project and adding user authentication.
Step 1: Initialize Next.js Project
Start by creating a new Next.js project.
then
Once your project is created, install dependencies:
- next-auth: Handles authentication.
- @prisma/client and prisma: Used to interact with the database and manage user data.
Now, initialize your Prisma ORM project by generating a Prisma schema file using this command:
Step 2: Setup a Database
Before connecting our database, we first need to create it. To understand Docker better, you can refer to our article, How to Set Up a Local PostgreSQL Using Docker Compose.
Open the Docker application, and if you have other PostgreSQL containers running, stop them to avoid port conflicts. Then, create a docker-compose.yml
file and add the following code to it:
This Docker Compose file is configured to set up a PostgreSQL database container for your Next.js project with Prisma. Here’s a breakdown:
- image: postgres:latest pulls the latest PostgreSQL image from Docker Hub.
- container_name: Names the container next-oauth-prisma, which makes it easy to reference.
- environment: Sets PostgreSQL environment variables:
- POSTGRES_USER: the username (set here to postgres_user).
- POSTGRES_PASSWORD: the password (postgres_password).
- POSTGRES_DB: the name of the initial database to be created (postgres_auth).
- ports: Exposes the container’s PostgreSQL port (5432) to the host’s port 5432. This makes the database accessible from outside the container via
localhost:5432
.
volumes:
- postgres_data:/var/lib/postgresql/data: Mounts a named volume postgres_data to persist database data on the host, so data is preserved between container restarts.
- ./init.sql:/docker-entrypoint-initdb.d/init.sql: Mounts an optional SQL script (
init.sql
), which can initialize the database with predefined tables or values on first run. This script is run automatically by PostgreSQL at startup if present.
Run
Running docker-compose up -d
is a way to start the containers defined in your docker-compose.yml
file with some specific advantages:
- Detached Mode (
-d
): The-d
flag stands for "detached mode," meaning the containers will run in the background. This allows you to keep using your terminal for other commands while your Docker services are running. - Automatic Setup: This command automatically builds and starts up all the services defined in your
docker-compose.yml
file. In this case, it will set up and start the PostgreSQL database container with the specified configuration. - Persistent Services: Running the container in detached mode is ideal for databases and other services that need to run continuously, as they keep running even if you close the terminal. You can stop them later using
docker-compose down
. - Networking and Dependencies: If there are multiple services in your
docker-compose.yml
file that depend on each other,docker-compose up -d
will ensure they start in the correct order and are networked together as defined.
In short, using docker-compose up -d
is a convenient way to start your database in the background, letting you continue development without an open Docker terminal session.
Step 3: Configure Prisma
Configure Prisma by creating a .env
file in the root directory with the following:
Replace username
, password
, and postgres_auth
with your actual credentials.
Define the User Model in Prisma
Open prisma/schema.prisma
and define the User model for NextAuth. Also, configure Prisma to use PostgreSQL.
Each model represents a table in the database, with Prisma creating these tables based on the defined schema. Here’s a breakdown of each model:
User
Model
Defines a table for user information with fields for basic attributes like name
, email
, and role
. Key points include:
- id: A unique identifier with a default value generated by cuid().
- email: A unique email field.
- createdAt and updatedAt: Timestamps to track creation and update times, with updatedAt auto-updating on changes.
- Relationships with other models (
Account
andSession
) are represented as arrays. - @@map("users"): Renames the table in the database to users.
Account
Model
Stores information about users’ accounts for third-party providers like Google or GitHub:
- userId: Links to the
User
model through a foreign key, creating a relationship with theUser
table. - Other fields, like
access_token
,refresh_token
, andexpires_at
, store authentication data. - @@id([provider, providerAccountId]): Composite primary key across
provider
andproviderAccountId
fields, enforcing uniqueness for each account. - @@map("accounts"): Names the table accounts in the database.
Session
Model
Tracks each login session for users:
- sessionToken: A unique session identifier.
- expires: Indicates session expiry time.
- The
user
field defines a foreign key relationship to theUser
table, with sessions deleted if the associated user is removed. - @@map("sessions"): Names the table
sessions
in the database.
VerificationToken
Model
Used for managing passwordless login or account verification tokens:
identifier
andtoken
act as composite primary keys, ensuring uniqueness.- expires: Expiration timestamp for the token.
- @@map("verification_tokens"): Names the table
verification_tokens
.
This schema allows for efficient management of user data, third-party account integrations, user sessions, and account verifications for a complete authentication system.
Run the following commands to create and migrate your database:
Step 4: Install ShadcnUI Package
We won't be building the entire project; instead, we'll just create a navbar with a user dropdown menu. To do this, we only need two components from the Shadcn library: Button and Dropdown Menu.
Run the following command to install the required ShadcnUI components package:
Add ShadcnUI Components
and
Now we’ve added everything needed to our project. In the next section, we’ll start building our UI and connect to the database.
Try running the project with:
Note: The latest version of Next.js has a bug with webpack, causing the error: "Module parse failed: Bad character escape sequence." We've previously covered a solution for this issue — read more about it here.
Create UI
Here's what we aim to build:

To achieve this, we'll need the following components: logo
, header
, navbar
, and user-button
.
Logo component
Create logo.tsx file inside the ui folder:
User Button Component
Create user-button.tsx inside the components folder
Navbar Component
Our login component will have three states: loading, sign in, and an avatar with a dropdown menu. To set this up, we need to follow these steps:

Here's how to do it:
useSession
hook fetches the current session’s state and data, assigning it tosession
.- The
user
variable extracts theuser
object from the session data.
Rendering Logic:
If the user is logged in (user exists):
- The UserButton component is rendered, displaying user-related options (e.g., an avatar with a dropdown menu).
If the session is loading (user state is undetermined):
- It displays "Loading user..." to inform users that the status is being fetched.
If no user is logged in (and session isn’t loading):
- It shows a Sign in button, which triggers the signIn function when clicked.
To add the user
variable to the session
, we need to adjust our layout.tsx
slightly by wrapping it with our session HOC (Higher Order Component).
Layout Component
Declare a module augmentation for next-auth:
Create a file src/types/next-auth.d.ts
The Session
interface is modified to add a user
object that combines User
and the default structure of user in DefaultSession. This helps TypeScript understand that your session's user
now includes the additional properties you defined in the User
interface.
A new User
interface is defined, adding a role property (type string | null
) to store additional user role information. This is useful when you want to store user roles in the session for access control.
By adding these properties, you gain strongly typed access to user.role when working with NextAuth sessions, which makes it easier to implement role-based features or access control in your app.
Declare globalThis.prismaGlobal
Create a file src/lib/prisma.ts
In summary, this code helps avoid reinitializing Prisma in development by using a singleton pattern, optimizing both development and production performance.
All that’s left is to create the Header
component, where we’ll combine our logo and navbar, and then integrate it into layout.tsx
.
Header Component
Test it
Set Up NextAuth
Now, we’ll set up NextAuth to authenticate users using Google and GitHub. In the app/api/auth/[...nextauth]
directory, create a new folder called auth and a file called route.ts
.
In the root directory create 2 files: _middleware.ts and auth.ts
auth.ts
In this file:
- We define Google and GitHub as OAuth providers.
- We configure the PrismaAdapter to manage data in the database.
_middleware.ts
Add your Google and GitHub OAuth credentials to the .env file:
GitHub OAuth App
Creating a new GitHub OAuth App involves a few steps within the GitHub developer settings. Here’s a quick guide to help you set it up:
1. Go to GitHub Developer Settings:
- Log in to your GitHub account.
- Navigate to GitHub Developer Settings by clicking on your profile icon, selecting Settings, and then Developer settings from the sidebar.
2. Create a New OAuth App:
- In the OAuth Apps section, click on New OAuth App.
3. Fill in the OAuth App Details:
- Application name: Enter a name for your app, which users will see when authorizing.
- Homepage URL: Add the main URL for your application, such as
https://localhost:3000
. - Authorization callback URL: Enter the URL where GitHub should redirect users after they authorize, typically something like https://localhost:3000/api/auth/callback/github (depending on how you've set up your app to handle callbacks).
4. Register the Application:
- Once all fields are filled in, click Register application. GitHub will generate a Client ID and Client Secret for your OAuth app.
5. Secure the Client Secret:
- Copy the Client ID and Client Secret. Keep these safe, as you’ll need them to configure OAuth in your application, and don’t expose them publicly.
6. Use in Application:
In your application code, use the Client ID and Client Secret to configure GitHub as an authentication provider (for example, using NextAuth or a similar library).
Your GitHub OAuth App is now ready! You can use it to authenticate users through GitHub in your application.
Google OAuth App
To create a new Google OAuth App, follow these steps within the Google Cloud Console:
1. Go to the Google Cloud Console:
- Visit Google Cloud Console and log in with your Google account.
2. Create or Select a Project:
- From the top menu, click Select a project.
- Choose an existing project or click New Project to create a new one specifically for your app.
3. Enable the OAuth API:
- In the left sidebar, go to APIs & Services > Library.
- Search for Google Identity or OAuth and click Enable on the Google Identity API.
4. Configure the OAuth Consent Screen:
- Go to APIs & Services > OAuth consent screen.
- Select External if your app will be available to general users, or Internal if it’s restricted to your organization (G Suite users only).
- Fill out the App name, User support email, and any other required fields.
- In Scopes for Google APIs, add scopes relevant to your app (e.g., user profile data).
- Save and continue to configure the consent screen.
5. Create OAuth Credentials:
- Navigate to APIs & Services > Credentials and click on Create Credentials > OAuth Client ID.
- For Application type, choose Web application.
- Under Authorized redirect URIs, add the callback URL for your application. This is typically in the format:
https://localhost:3000/api/auth/callback/google
Replace localhost:3000 with your app’s actual domain.
6. Save Your Client ID and Client Secret:
- After creating the credentials, Google will provide a Client ID and Client Secret.
- Copy these, as you’ll need them to configure Google authentication in your app.
Your Google OAuth App is now configured and ready for use in your application.
We have one last step: configuring remote image handling in Next.js. To load images from external sources like Google and GitHub in our Next.js app, we need to set trusted image sources in the next.config.ts
configuration file. Here’s how:
The remotePatterns
array lists trusted sources for loading images from external domains. This is useful when users authenticate with Google or GitHub, allowing us to display their profile images securely.
Test and Verify Authentication
After setting up the database, NextAuth configurations, and UI, you can test the authentication workflow. When a user signs in with Google or GitHub, NextAuth will store their information in the database via Prisma, enabling a seamless authentication experience across sessions.
Wrapping Up
With this setup, you’ve established a robust user authentication system in Next.js using NextAuth and Prisma. This implementation is scalable, letting you expand user data management and customize the authentication experience as needed. Using NextAuth with Prisma offers flexibility, and the integration with OAuth providers like Google and GitHub provides a simple, secure way to handle authentication in modern web applications.
Happy coding!