Hey Devs !
As a front-end developer my nightmare was to handle the user authentication.
With NextAuth.js (I will call it auth.js since they are changing their name) the user authentication handling is much easier. Let's take a look at how we handle it with Next.js
Here are the steps that you need to follow;
Create a next app
yarn create next-app
Go inside your project and add auth.js
yarn add next-auth
To implement the auth.js, we need to create an api folder in our pages directory and create the [...nextauth].js
/pages
/api
/auth
[...nextauth].js
In your pages/api/auth/[...nextauth].js
Since you can configure your NextAuth, we will create an authOptions object to store the configurations that we want aplly.
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials";
export const authOptions = {}
export default NextAuth(authOptions);
Now let's start to adding options
Since we are using custom backend, we are going to use CredentialsProvider and we will pass this to providers key. This key stores all of providers that you want to use in your application with their configurations.
providers: [
CredentialsProvider({
type: "credentials",
credentials: {
email: {
label: "Email",
type: "email",
},
password: { label: "Password", type: "password" },
},
],
...rest
If you want to use your own Sign In page, you do not need to pass email and password keys to credentials object. Anyway, we are going to use Auth.js's sign in page in this.
In this case we need the email address and password of user to authenticate so we pass email and password fields.
Now we all set in
[...nextAuth].js
to fetch login data to your custom backend now we need to pass
authorize
async authorize(credentials) {
const credentialDetails = {
email: credentials.email,
password: credentials.password,
};
const resp = await fetch(backendURL + "/auth/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(credentialDetails),
});
const user = await resp.json();
if (user.is_success) {
return user;
} else {
console.log("check your credentials");
return null;
}
},
In this case,
backendURL
auth/login
If your credentials are correct, the backend should response you a success message and you need to check it if its success or not.
For now the
[...nextAuth].js
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
const backendURL = process.env.NEXT_PUBLIC_BACKEND_URL;
export const authOptions = {
providers: [
CredentialsProvider({
type: "credentials",
credentials: {
email: {
label: "Email",
type: "email",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const credentialDetails = {
email: credentials.email,
password: credentials.password,
};
const resp = await fetch(backendURL + "/auth/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(credentialDetails),
});
const user = await resp.json();
if (user.is_success) {
console.log("nextauth daki user: " + user.is_success);
return user;
} else {
console.log("check your credentials");
return null;
}
},
}),
],
};
export default NextAuth(authOptions);
since we are using JWT , you need to pass session object with strategy key as:
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},
you can pass a maxAge to store your user in the web browser as authenticated as you want, here it stores for 30 days.
Do not forget that JWT maxAge can be also defined in the backend so you should consider the backend JWT maxAge when setting this value.
Implementing the callbacks to use session in client-side
Now you can fetch user in backend and your response should have some information that will be used in the client side while fetching some APIs.
With the help of callbacks, we will persist the backend access token to token provided by auth.js right after signin.
For this, we need to pass callbacks object to
[...nextauth].js
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.email = user.data.auth.email;
token.username = user.data.auth.userName;
token.userType = user.data.auth.userType;
token.accessToken = user.data.auth.token;
}
return token;
},
}
In this case, from the response provided by backend, user object has various values like email,userName,userType and token.
In jwt object we are calling the async function that stores our response in token.
After that, from frontend, we need to add the token to cookies in user session, like this :
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.email = user.data.auth.email;
token.username = user.data.auth.userName;
token.user_type = user.data.auth.userType;
token.accessToken = user.data.auth.token;
}
return token;
},
session: ({ session, token, user }) => {
if (token) {
session.user.email = token.email;
session.user.username = token.userName;
session.user.accessToken = token.accessToken;
}
return session;
},
},
with the help of session callback, now we can use the useSession hook provided by auth.js in the client-side to get information that we pass to session object.
Before going into the client side, lets check your
[...nextAuth].js
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
const backendURL = process.env.NEXT_PUBLIC_BACKEND_URL;
export const authOptions = {
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},
providers: [
CredentialsProvider({
type: "credentials",
credentials: {
email: {
label: "Email",
type: "email",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const credentialDetails = {
email: credentials.email,
password: credentials.password,
};
const resp = await fetch(backendURL + "/auth/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(credentialDetails),
});
const user = await resp.json();
if (user.is_success) {
console.log("nextauth daki user: " + user.is_success);
return user;
} else {
console.log("check your credentials");
return null;
}
},
}),
],
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.email = user.data.auth.email;
token.username = user.data.auth.userName;
token.user_type = user.data.auth.userType;
token.accessToken = user.data.auth.token;
}
return token;
},
session: ({ session, token, user }) => {
if (token) {
session.user.email = token.email;
session.user.username = token.userName;
session.user.accessToken = token.accessToken;
}
return session;
},
},
};
export default NextAuth(authOptions);
Use the session in client side !
Lets start wrapping our application with SessionProvider;
Move to your _app.js file in the route;
/pages
_app.js
and import SessionProvider from next-auth
import { SessionProvider } from "next-auth/react";
As well as you import the session provider, do not forget to pass session in to the page props and wrap your application with SessionProvider like this in your
_app.js
import { SessionProvider } from "next-auth/react";
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
);
}
After that everything is simple, just import
useSession()
[...nextauth].js
import { useSession} from "next-auth/react";
const { data, status } = useSession();
Now you can use any data that you passed in session callback function by using
data
In this example we can access the user access token which we got from server as :
session.user.accessToken
Also, you can check if the user is authenticated in the serverside, so you may want to protect your pages.
In your component, you may use session in getServerSideProps function as:
import getSession to use session in serverside from :
import {getSession } from "next-auth/react";
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
//if not exists, return a temporary 302 and replace the url with the given in Location.
context.res.writeHead(302, { Location: "/signin" });
context.res.end();
//do not return any session.
return { props: {} };
}
}
And this is a simple use of credentials provider from Auth.js