DEV Community

Said Mounaim
Said Mounaim

Posted on

Authentication with credentials using Next-Auth & MongoDB

For create Login Authentication we're going to use Next-Auth package from Vercel Here is the link Next-Auth ,

Let's install it open your terminal and run

npm i next-auth
Enter fullscreen mode Exit fullscreen mode

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

let's start by importing Next-Auth

import NextAuth from "next-auth";
Enter fullscreen mode Exit fullscreen mode

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",
  }
})

Enter fullscreen mode Exit fullscreen mode

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;
    },
  },
})

Enter fullscreen mode Exit fullscreen mode

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;
    },
  },
})

Enter fullscreen mode Exit fullscreen mode

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");
      },
    }),
  ],
});

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
ktisakib profile image
Kazi Tanvirul Isam Sakib

Does the session is stored in Mongodb?? Cause I am trying it creates the user signs up but signing in Doesn't create any session in my case