Back • 10/09/2023
How to Setup NextAuth Password with Database (Next Page Directory)
Written by Melvyn Malherbe on 10/09/2023
If you go to the NextAuth Credentials section, you will see that the functionality is "discouraged" to limit its usage.
Also, you'll need to set up the JWT
strategy to make it work.
But... how do we use it with our database? What if we don't want to change to JWT
and want flexibility?
I'll help you set it up with your database in this post.
Setup
To set up the NextAuth Credentials, we will follow the basic steps! In your NextAuth config, add the Credentials Provider:
import CredentialsProvider from 'next-auth/providers/credentials';
export const authOptions: (
req?: NextApiRequest,
res?: NextApiResponse
) => AuthOptions = (req, res) => ({
// ...
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text', placeholder: 'your email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
// your logic with your database here
}
});
],
});
Note something important: I use an arrow function that takes req
and res
to have access to the req
and res
of the API route.
To make it work, you will need to use it in the pages/api/auth/[...nextauth].ts
file like this:
import { NextApiRequest, NextApiResponse } from 'next';
import NextAuth from 'next-auth';
import { authOptions } from '~/lib/nextauth';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
return NextAuth(req, res, authOptions(req, res));
}
Because to handle the database authentication, we will need the req
and the res
of the API route.
Authorize function
To create our password strategy, we will need to add a callback
function to our config.
You will need to add the signIn
function like this:
export const authOptions: (
req?: NextApiRequest,
res?: NextApiResponse
) => AuthOptions = (req, res) => ({
// ...
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
// your logic here
},
},
});
In this function, we will start by knowing if the signIn
method was called with the credentials
options, because we won't handle any other case.
export const authOptions: (
req?: NextApiRequest,
res?: NextApiResponse
) => AuthOptions = (req, res) => ({
// ...
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
if (!req || !res) return true;
if (
!req.query.nextauth?.includes('callback') ||
!req.query.nextauth?.includes('credentials') ||
req.method !== 'POST' ||
!user
) {
return true;
}
},
},
});
We check if there is no callback OR if there is no credentials
in the query OR if the method is not POST
, we return true
to let NextAuth handle the authentication.
If there is no user, it means that the user is not found in the database, so we return true
to let NextAuth handle the authentication.
export const authOptions: (
req?: NextApiRequest,
res?: NextApiResponse
) => AuthOptions = (req, res) => ({
// ...
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
if (!req || !res) return true;
if (
!req.query.nextauth?.includes('callback') ||
!req.query.nextauth?.includes('credentials') ||
req.method !== 'POST'
) {
return true;
}
},
},
});
And now, we will need to do exactly like NextAuth does with other methods to handle authentication with a password.
First, at the start of my file, I had this:
const tokenName =
env.NODE_ENV === 'development'
? 'next-auth.session-token'
: '__Secure-next-auth.session-token';
The tokenName
in the Cookie
has a different name if we are in development or production. So we will need to do the same thing here.
export const authOptions: (
req?: NextApiRequest,
res?: NextApiResponse
) => AuthOptions = (req, res) => ({
// ...
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
if (!req || !res) return true;
if (
!req.query.nextauth?.includes('callback') ||
!req.query.nextauth?.includes('credentials') ||
req.method !== 'POST'
) {
return true;
}
const uuid = uuidv4();
// 7 days
const expireAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
// Create a session with YOUR database, for the example, I use Prisma
await prisma.session.create({
data: {
sessionToken: uuid,
userId: user.id,
expires: expireAt,
},
});
// Create a cookie
const cookie = new Cookie(req, res, {
// ⚠️ Important to use secure in production or it won't work
secure: process.env.NODE_ENV === 'production',
});
cookie.set(tokenName, uuid, {
expires: expireAt,
secure: env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
});
return false;
},
},
});
In this code, I generate a uuid
that will act as a token. I use the library uuid
to do that.
Then I generate an expireAt
date that represents 7 days after today, and I create a session in
prisma using the sessionToken
as uuid
, the userId
, and the expireAt
date.
Then I create a cookie with the Cookie
library, and I set the tokenName
as the uuid
, and I set the expireAt
date.
Next, I need to add a Cookie
to the res
object, so I use the library Cookie
to do that. Don't forget to add the secure
option to true
if you are in production.
I finally set
the cookie with the tokenName
as the uuid
, the expireAt
date, the secure
option, the sameSite
option, and the path
option.
With all of this, your authentication will work in NextJS 12 and NextJS 13.
Finish
I hope this article will help you make your authentication work with NextJS 12 and NextJS 13.