Setup Guide for NextAuth with Google and Credentials Providers in Next.js 13
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:
- Go to the Google Cloud Console.
- Create a new project or select an existing one.
- Navigate to ‘APIs & Services’ > ‘Credentials’.
- Click on ‘Create Credentials’ and select ‘OAuth client ID’.
- Set up the consent screen, and choose ‘Web application’ as the application type.
- Add your application’s URI to ‘Authorized redirect URIs’, typically
http://localhost:3000/api/auth/callback/google
for local development. - Once created, you’ll get your
Client ID
andClient 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
andclientSecret
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
andauthOptions
. - 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. 🎉