Setup Guide for NextAuth with Google and Credentials Providers in Next.js 13

Karthick Ragavendran
6 min readNov 18, 2023

--

This guide provides concise steps to integrate NextAuth in a Next.js 13 (app directory) application, using Google and Credentials Providers for authentication. The goal is to enable both Google sign-in and traditional email/password authentication in your Next.js project. Let's dive into the setup process, focusing on practical implementation and code examples.

Prerequisites

Before starting, ensure you have the following:

  • A Next.js 13 project setup with App directory enabled.
  • A Google Cloud account for obtaining OAuth credentials.

To get your Google OAuth credentials:

  1. Go to the Google Cloud Console.
  2. Create a new project or select an existing one.
  3. Navigate to ‘APIs & Services’ > ‘Credentials’.
  4. Click on ‘Create Credentials’ and select ‘OAuth client ID’.
  5. Set up the consent screen, and choose ‘Web application’ as the application type.
  6. Add your application’s URI to ‘Authorized redirect URIs’, typically http://localhost:3000/api/auth/callback/google for local development.
  7. Once created, you’ll get your Client ID and Client Secret.

Installing NextAuth

First, install NextAuth in your Next.js project:

npm install next-auth

Next, configure the basic structure in your authOptions file, using the code provided:

import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import CredentialsProvider from 'next-auth/providers/credentials'
export const authOptions = {
// Providers array will be configured in the next steps
providers: [],
session: {
strategy: 'jwt'
},
// Additional configuration will be added here
}
export const getAuth = () => getServerSession(authOptions)

This sets up NextAuth with an empty array for providers, which we’ll fill in the following sections, and specifies JWT for session management. The getAuth function is for server-side session retrieval.

Configuring Google Provider

To integrate Google authentication, update the providers array in authOptions:

import GoogleProvider from 'next-auth/providers/google'
// ... other importsexport const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
// ... other providers
],
// ... other configurations
}

In this setup:

  • clientId and clientSecret are the OAuth credentials obtained from Google Cloud Console.
  • Store these credentials in environment variables (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET) for security.

5. Setting Up Credentials Provider

For email/password authentication, add the Credentials Provider:

import CredentialsProvider from 'next-auth/providers/credentials'
import bcrypt from 'bcrypt'
// ... other importsexport const authOptions = {
providers: [
// ... GoogleProvider configuration
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Add logic to verify credentials here
if (!credentials) return null
const { email, password } = credentials
// Fetch user and password hash from your database
// Example: const user = await getUserByEmail(email)
if (user && bcrypt.compareSync(password, user.passwordHash)) {
return { id: user.id, name: user.name, email: user.email }
} else {
throw new Error('Invalid credentials')
}
},
}),
],
// ... other configurations
}

Key points:

  • Define credentials with email and password fields.
  • Implement the authorize function to verify user credentials.
  • Fetch the user’s hashed password from your database.
  • Use bcrypt to compare the provided password with the stored hash.
  • If valid, return the user object; otherwise, throw an error.

This configuration enables users to log in with their email and password, while Google Provider allows for Google account sign-ins. The next sections will cover integrating these into your Next.js application routes and managing user sessions.

Detailed Breakdown of authOptions in NextAuth

Lets have a closer look at theauthOptions structure and its components:

export const authOptions: NextAuthOptions = {
providers: [
// GoogleProvider and CredentialsProvider configurations
],
session: {
strategy: 'jwt',
maxAge: 1 * 24 * 60 * 60, // 1 day
},
jwt: {
// JWT encoding and decoding configurations
},
callbacks: {
// signIn, session callbacks
},
pages: {
signIn: '/signIn', // Custom sign-in page
},
}

In this setup:

  • providers array includes Google and Credentials Providers.
  • session configures JWT session management.
  • jwt handles encoding and decoding of JWT tokens.
  • callbacks allow custom handling of signIn and session events.
  • pages lets you specify custom pages for sign-in.

Authentication Routes

Next, set up the authentication routes in your Next.js application. This involves creating API routes that handle authentication requests:

// src/app/api/auth/[...nextauth]/route.ts
import { authOptions } from '@foundation/common/src/authOptions'
import NextAuth from 'next-auth'
const handler = NextAuth(authOptions)export { handler as GET, handler as POST }

In this code:

  • Import NextAuth and authOptions.
  • Create a handler with NextAuth(authOptions).
  • Export the handler as the default export for handling GET and POST requests on /api/auth/*.

These routes work with our authOptions configuration to handle user authentication. When a user attempts to sign in or interact with the authentication system, these API routes process the requests according to our configuration.

We can have a route to get back the token generated for the authenticated users.

// src/app/api/auth/token/route.ts

import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'

export async function GET(req: NextRequest, res: NextResponse) {
const getCookies = cookies()
const nextAuthSession = getCookies.get('next-auth.session-token')?.value || ''

return NextResponse.json(nextAuthSession)
}

The Flow

It begins with the user’s choice between Google or email/password authentication. For Google authentication, the user is redirected to Google and back, while for email/password, the credentials are verified through the authorize function. This process ensures that only valid credentials grant access, and upon successful authentication, the user's session is created and managed for subsequent application use.

|
|-- User Initiates Authentication
| |-- Via Google Provider
| | `-- Redirect to Google -> User Authenticates -> Redirect back to Application
| |
| `-- Via Credentials Provider
| `-- User Enters Email and Password
| `-- authorize Function
| |-- If Valid: User Authenticated
| `-- If Invalid: Error Returned
|
|-- Post-Authentication
| `-- signIn Callback Triggered
|
|-- Session Creation and Management
| `-- session Callback Updates Session Information
|
`-- User Access
`-- User Fully Authenticated and Accesses Application

UI components

The SignIn component is a key interface element where users interact with the authentication system. It uses form management to handle user inputs for email and password, and provides an option to sign in with Google.

// signIn.tsx
export const SignIn = () => {
const { register, handleSubmit } = useFormSignin()

return (
<AuthLayout title="Login">
<form onSubmit={handleSubmit(({ email, password }) => signIn('credentials', { email, password }))}>
<Label title="Email">
<Input {...register('email')} placeholder="Email" />
</Label>
<Label title="Password">
<Input {...register('password')} type="password" placeholder="Password" />
</Label>
<Button type="submit">Submit</Button>
</form>

<Link href="/register">Register</Link>

<button onClick={() => signIn('google')}>Sign in with Google</button>
</AuthLayout>
)
}

// register.tsx

export const Register = () => {
const { register, handleSubmit } = useFormRegister()

return (
<AuthLayout title="Register">
<form
onSubmit={handleSubmit(async ({ email, password, name }) => {
// Backend registration call
const user = await fetchGraphQL({
document: CreateUserWithCredentialsDocument,
variables: { email, password, name },
})

// Handle registration response
if (user.error) {
toast({ title: user.error })
} else if (user.data) {
toast({ title: `User created. 🎉` })
signIn('credentials', { email, password, callbackUrl: '/' })
}
})}
>
<Input {...register('email')} placeholder="Email" />
<Input {...register('password')} type="password" placeholder="••••••" />
<Input {...register('name')} placeholder="Name" />
<Button type="submit">Submit</Button>
</form>

<Link href="/signIn">Already have an account? Sign in</Link>
<button onClick={() => signIn('google')}>Sign in with Google</button>
</AuthLayout>
)
}

On the registration page, we do the following,

  • User inputs for email, password, and name.
  • Submission handling that interacts with our own backend for registration.
  • Feedback for success or error during the registration process.
  • Links and options for alternative sign-in methods.

Understanding the Scope of This Guide

Before we dive into the details of setting up authentication in Next.js using NextAuth, it’s important to note that this guide focuses specifically on the front-end configuration of authentication mechanisms. Key components not covered in this guide include:

  • Database Schema: The backend setup for storing user credentials, especially for the Credentials Provider, is not covered. A complete implementation requires a database to store encrypted passwords and user details.
  • Backend Registration Routes: The guide does not include backend routes necessary for user registration and management, which are critical for a fully functional authentication system.
  • Complete Backend Integration: While we discuss the front-end aspects of authentication, integrating these with a backend service is beyond the scope of this article.

This guide is part of a larger setup and is intended to provide a starting point for integrating authentication in a Next.js application. For a comprehensive implementation, additional backend development and database setup are required.

You can see the full working implementation in this mono repo repository. https://github.com/karthickthankyou/foundationX

Link for repository: https://github.com/karthickthankyou/foundationX

Thank you for reading. I appreciate your time and interest in my work. For more full-fledged open-source products, You can follow me on Github or LinkedIn.

Happy coding. 🎉

--

--

Karthick Ragavendran

Fullstack engineer | React, Typescript, Redux, JavaScript, UI, Storybook, CSS, UX, Cypress, CI/CD.