Back • 27/08/2024
How to Setup NextAuth Password with Database (Next App Directory)
Written by Melvyn Malherbe on 27/08/2024
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.
Ce tutoriel vous aide à configurer NextAuth avec un mot de passe et la structure du répertoire NextJS App. Si vous recherchez la version du répertoire de pages, consultez mon post précédent.
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 { handlers, auth: baseAuth } = NextAuth((request) => ({
adapter: PrismaAdapter(prisma),
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
}
});
],
session: {
strategy: "database",
},
secret: env.NEXTAUTH_SECRET,
}));
Then you can use the handlers
method in the app/api/auth/[...nextauth]/route.ts
:
import { handlers } from '@/lib/auth/auth';
export const { GET, POST } = handlers;
Authorize function
To create our password strategy, we will need to add a event
function to our config.
You will need to add the signIn
function like this:
export const { handlers, auth: baseAuth } = NextAuth((request) => ({
events: {
async signIn({ user }) {
// 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 { handlers, auth: baseAuth } = NextAuth((request) => ({
events: {
async signIn({ user }) {
if (!request) {
return;
}
if (request.method !== "POST") {
return;
}
const currentUrl = request.url;
// We check if the user login with credentials
if (!currentUrl.includes("credentials")) {
return;
}
if (!currentUrl.includes("callback")) {
return;
}
},
},
});
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.
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 { handlers, auth: baseAuth } = NextAuth((request) => ({
callbacks: {
async signIn({ user }) {
if (!request) {
return;
}
if (request.method !== "POST") {
return;
}
const currentUrl = request.url;
// We check if the user login with credentials
if (!currentUrl.includes("credentials")) {
return;
}
if (!currentUrl.includes("callback")) {
return;
}
// We create a session
const uuid = nanoid();
const expireAt = addDays(new Date(), 14);
// I use prisma for the example
await prisma.session.create({
data: {
sessionToken: uuid,
userId: user.id ?? "",
expires: expireAt,
},
});
const cookieList = cookies();
const AUTH_COOKIE_NAME =
env.NODE_ENV === "development"
? "authjs.session-token"
: "__Secure-authjs.session-token";
// Create and set a cookie with the exact same value
cookieList.set(AUTH_COOKIE_NAME, uuid, {
expires: expireAt,
path: "/",
sameSite: "lax",
httpOnly: true,
secure: env.NODE_ENV === "production",
});
return;
},
},
});
In this code, I generate a nanoid
that will act as a token. I use the library nanoid
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.
Override jwt
If you do everything above, it will not work. NextAuth will delete the token because he doesn't reconnaît it.
To fix this, we will disable the jwt
method :
export const { handlers, auth: baseAuth } = NextAuth((request) => ({
// ...
jwt: {
encode() {
return "";
},
async decode() {
return null;
},
};
}));
With this code, NextAuth will correctly log in the user with the cookie created together.
Finish
I hope this article will help you make your authentication work with NextJS 14.