Authentication with credentials using Next-Auth & MongoDB
Tags: nextjs, javascript, react, nextauth
Published: 2022-10-09 14:26:26

For create Login Authentication we're going to use Next-Auth package from Vercel Here is the link [Next-Auth](https://next-auth.js.org/) ,

Let's install it open your terminal and run


```
npm i next-auth
``` 

next step is creating a folder inside Api and set folder name to auth and inside auth create new file set filename to [...nextauth].js every sign in, sign out & checking the authentication will be redirected to this file and we need to handle it


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7sq3b7ycvk0drhke2maz.PNG)

let's start by importing Next-Auth


```
import NextAuth from "next-auth";
``` 

then export default NextAuth it's a function that accept Object as a parameter for Object we need to set some properties the fist one is session and inside session we're going to use jwt as a strategy


```
import NextAuth from "next-auth";

export default NextAuth({
  session: {
    strategy: "jwt",
  }
})

``` 
next setting for the NextAuth function is callbacks is an object and inside that we need to define function it is jwt it's an async function and jwt accept two parameter the first one is session and the second one is user, inside jwt function we check user id if does it exist and fill token id with user id and return the token.

user id is coming from database and token in NextAuth life cycle

```
import NextAuth from "next-auth";

export default NextAuth({
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user?._id) token._id = user._id;
      if (user?.isAdmin) token.isAdmin = user.isAdmin;
      return token;
    },
  },
})

```
in the next callbacks we're going to define session function it accept two parameter session and token,

in the session function we check token if does it exist and fill session with token and at the end return session.

```
import NextAuth from "next-auth";

export default NextAuth({
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user?._id) token._id = user._id;
      if (user?.isAdmin) token.isAdmin = user.isAdmin;
      return token;
    },
    async session({ session, token }) {
      if (token?._id) session.user._id = token._id;
      if (token?.isAdmin) session.user.isAdmin = token.isAdmin;
      return session;
    },
  },
})

```

let's going for the next setting is define a providers is an Array and the provider that we're going to use is CredentialsProvider we're going to authenticate user based in MongoDB database not using Github Authentication or Google Login.

we're using CredentialsProvider in parameter define an object and inside define authorize function is an async function accept credentials as a parameter inside we need to connect to database and find the user in database based in email in the credentials parameter,
we need to import ConnectDB from config folder, and User from models folder.

next step is checking user and password together if user exist it means that we have user with that email and checking the password if it is correct then we need to return an object, the object is coming from database.


```
import NextAuth from "next-auth";
import connectDB from "../../../config/db";
import CredentialsProvider from "next-auth/providers/credentials";
import User from "../../../models/User";
import bcrypt from "bcryptjs";

export default NextAuth({
  session: {
    strategy: "jwt",
  },

  callbacks: {
    async jwt({ token, user }) {
      if (user?._id) token._id = user._id;
      if (user?.isAdmin) token.isAdmin = user.isAdmin;
      return token;
    },
    async session({ session, token }) {
      if (token?._id) session.user._id = token._id;
      if (token?.isAdmin) session.user.isAdmin = token.isAdmin;
      return session;
    },
  },
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        connectDB();
        const user = await User.findOne({ email: credentials.email });
        if (user && bcrypt.compareSync(credentials.password, user.password)) {
          console.log(user);
          return {
            _id: user._id,
            name: user.name,
            email: user.email,
            isAdmin: user.isAdmin,
          };
        }
        throw new Error("Invalid Email or Password");
      },
    }),
  ],
});

``` 

AMAZIING, we implement the sign in function and we configured NextAuth function next step is go to login page inside the page we need to handle submission 


```
import Link from "next/link";
import React, { useEffect, useState } from "react";
import Layout from "../components/Layout";
import { useForm } from "react-hook-form";
import { signIn, useSession } from "next-auth/react";
import { toast } from "react-toastify";
import { getError } from "../utils/error";
import { useRouter } from "next/router";

const login = () => {
  const { data: session } = useSession();
  const router = useRouter();

  useEffect(() => {
    if (session?.user) router.push("/");
  }, [session, router]);

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();
  const handleLogin = async ({ email, password }) => {
    try {
      const result = await signIn("credentials", {
        redirect: false,
        email,
        password,
      });
      if (result.error) toast.error(result.error);
    } catch (err) {
      toast.error(getError(err));
    }
  };
  const handleError = (errors) => {};

  const loginOptions = {
    email: {
      required: "Email is required",
      pattern: {
        value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$/i,
        message: "Email not valid",
      },
    },
    password: {
      required: "Password is required",
    },
  };

  return (
    <Layout title="Login">
      <form
        className="mx-auto max-w-screen-sm"
        onSubmit={handleSubmit(handleLogin, handleError)}
      >
        <div className="flex flex-col mt-4 mb-5">
          <h1 className="text-[30px] text-[#06283D] font-medium">Login</h1>
        </div>

        <div className="flex flex-col mb-3">
          <label
            htmlFor="email"
            className="text-[17px] text-[#06283D] font-medium mb-2"
          >
            Email
          </label>
          <input
            type="text"
            id="email"
            {...register("email", loginOptions.email)}
            autoFocus
          />
          {errors?.email && (
            <p className="text-[16px] text-red-500">{errors.email.message}</p>
          )}
        </div>
        <div className="flex flex-col mb-3">
          <label
            htmlFor="password"
            className="text-[17px] text-[#06283D] font-medium mb-2"
          >
            Password
          </label>
          <input
            type="password"
            id="password"
            {...register("password", loginOptions.password)}
            autoFocus
          />
          {errors?.password && (
            <p className="text-[16px] text-red-500">
              {errors.password.message}
            </p>
          )}
        </div>
        <button className="bg-[#06283D] px-5 py-3 text-white font-medium text-center rounded-sm">
          Login
        </button>
        <div className="flex flex-col mt-4 text-[#06283D] text-[16px] font-medium">
          Don't have an account, <br />
          <Link href="/register">Register</Link>
        </div>
      </form>
    </Layout>
  );
};

export default login;

```