How To Use Next-Auth In Next.js 14 | Login Credentials | Github Login | Register|With Mongodb .

  • Posted on January 1, 2024
  • By MmantraTech
  • 386 Views

Hello guys, in this article we will dive into Next-auth library where we will be creating Next.js app having Login and Register form with Next-Auth Login credentials and gihub login providers. In this App we will use mongodb as database to store user's information.

Video Tutorial Link :

https://www.youtube.com/watch?v=BpLLNCTRgqA

Orange & Yellow Modern Live Webinar Affiliate Marketing YouTube Thumbnail (3)-WWCpzAG6vY.png

Introduction :

NextAuth.js is a secured and authenticated library which offers flexibility by supporting various databases, including MySQL, MongoDB, PostgreSQL, and MariaDB, making it adaptable to different application needs. Whether used with or without a database, the library seamlessly integrates with OAuth services, facilitating passwordless sign-ins and ensuring a secure authentication process.

 

 

 

Unlike traditional methods, NextAuth.js prioritizes security by avoiding the storage of passwords for user accounts. For those with existing databases containing usernames and passwords, the option to utilize a custom credentials provider enables a smooth transition to NextAuth.js, allowing users to sign in with their credentials securely.

The versatility of NextAuth.js extends to its compatibility with different authentication providers and strategies, such as OAuth 1.0, 1.0A, 2.0, and OpenID Connect. This inclusive approach caters to a wide range of authentication requirements, ensuring a seamless integration with diverse authentication services.

The great thing is Implementing authentication features in Next.js applications becomes straightforward with NextAuth.js. It eliminates the need for in-depth knowledge of identity protocols like OAuth, providing a streamlined solution for building secure applications. Designed to prioritize user data security, NextAuth.js ensures that sensitive information, such as passwords, is not stored, enhancing overall application security.

With minimum line of code, developers can integrate authentication features seamlessly into their Next.js applications, reducing the complexity typically associated with identity protocols. NextAuth.js simplifies the process, allowing developers to focus on creating robust and secure applications without compromising on user authentication.

Furthermore, NextAuth.js offers a client-side API, enabling developers to interact with sessions within their applications effortlessly. The session data retrieved from Providers includes user payloads, providing valuable information that can be displayed to users upon successful login. This streamlined approach enhances the user experience, making NextAuth.js a comprehensive solution for implementing secure and user-friendly authentication in Next.js applications.

Let's create and setup example App

Step 1: Create the Next App Setup

npx create-next-app@latest

The above command will install latest Next JS Version >=14

Step 2: Setup App API Router and Next Auth

 

--- LOGIN | GITHUB LOGIN | PROVIDERS SETUP IN NEXT-AUTH ---

 

2.1 Create a file  api/auth/[...nextauth]/route.js and paste below code 

import NextAuth from "next-auth";
import { Account, User as AuthUser } from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import User from "@/app/models/User";
import dbConnect from "@/app/common/database";

export const authOptions: any = {
  // Configure one or more authentication providers
  providers: [
    CredentialsProvider({
      id: "credentials",  
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "text" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials: any) {
        await dbConnect();
        try {
          const user = await User.findOne({ email: credentials.email });
          if (user) {
            const password = await bcrypt.compare(
              credentials.password,
              user.password
            );
            if (password) {
              return user;
            }
          }
        } catch (err: any) {
          throw new Error(err);
        }
      },
    }),
    GithubProvider({
      clientId: process.env.GITHUB_ID ?? "",
      clientSecret: process.env.GITHUB_SECRET ?? "",
    }),
    //GoogleProvider()
    // ...any other provider hers
  ],
  callbacks: {
    async signIn({ user, account }: { user: AuthUser; account: Account }) {
      if (account?.provider == "credentials") {
        return true;
      }
      if (account?.provider == "github") {
        await dbConnect();
        try {
          const existingUser = await User.findOne({ email: user.email });
          if (!existingUser) {
            const newUser = new User({
              email: user.email,
            });

            await newUser.save();
            return true;
          }
          return true;
        } catch (err) {
          console.log("Error saving user", err);
          return false;
        }
      }
    },
  },
};

export const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

 

-- -REGISTER ROUTE API ---

Step 3: Create a file  app/api/register/route.ts and paste the below code.

import React from "react";
import { NextRequest, NextResponse } from "next/server";
import dbConnect from "@/app/common/database";
import User from "@/app/models/User";
import bcrypt from "bcryptjs";

export const POST = async (request: any) => {
  const { email, password } = await request.json();
  await dbConnect();

  const isUserExist = await User.findOne({ email });
  if (isUserExist) {
    return NextResponse.json({ msg: "User already existed" }, { status: 400 });
  }
  try {
    const password_hash =await bcrypt.hash(password, 5);

    const newUser = new User({
      email,
      password: password_hash,
    });

    await newUser.save();
    return NextResponse.json(
      { msg: "User is ceated Successfully" },
      { status: 200 }
    );
  } catch (err) {
    return NextResponse.json({ msg: err }, { status: 500 });
  }
};

 

       --NEXT AUTH SESSION PROVIDER SETUP--

Step 4: Create a file app/common/NextAuthProvider.tsx and paste below code :

"use client"
import React from "react";
import { SessionProvider } from "next-auth/react";
import { Session } from "inspector";

const NextAuthProvider = ({ children }: any) => {
  return <SessionProvider>{children}</SessionProvider>;
};

export default NextAuthProvider;

 

---MONGODB CONNECTION---

 

Step 5: Create a file app/common/database.js and paste below code :

import mongoose from "mongoose";

//you can more details on mmantratech.com
//youtube : techmalasi
 const MONGODB_URI = process.env.MONGO_URI;

if (!MONGODB_URI) {
  throw new Error(
    'Something went wrong'
  )
}
let cached = global.mongoose

if (!cached) {
  cached = global.mongoose = { conn: null, promise: null }
}

async function dbConnect () {
  if (cached.conn) {
    return cached.conn
  }

  if (!cached.promise) {
    const opts = {
      useNewUrlParser: true,
      useUnifiedTopology: true
    }

    cached.promise = mongoose.connect(MONGODB_URI,opts).then(mongoose => {
      console.log("You successfully Connected")
      return mongoose
    })
  }
  cached.conn = await cached.promise
  return cached.conn
}

export default dbConnect

 

---PROJECT NAVBAR SETUP---

Step 6: Create a file app/components/Navbar.tsx and paste below code:

"use client"
import React from "react";
import Link from "next/link";
import { signOut, useSession } from "next-auth/react";

const NavarBar = () => {
  const { data: userData } = useSession();

  return (
    <div className="w-full max-w-[80%]">
      <ul className="flex gap-5 mt-4 ml-12 justify-center items-center  bg-gray-500 text-white">
        <Link href="/">
          <li className="p-4">Home</li>
        </Link>
        <li className="p-4">Services</li>
        <Link href="/dashboard">
          <li className="p-4">Dashboard</li>
        </Link>
        {!userData ? (
          <>
            <Link href="/login">
              <li className="p-4">Login </li>
            </Link>
            <Link href="/register">
              <li className="p-4">Register</li>
            </Link>
          </>
        ) : (
          <>
            {userData.user?.email}
            <li className="p-4">
              <button
                onClick={() => {
                  signOut();
                }}
              >
                Logout
              </button>
            </li>
          </>
        )}
      </ul>
    </div>
  );
};

export default NavarBar;

 

Step 7: Create a file app/dashboard/page.tsx and paste below code:

import React from 'react'
import { getServerSession } from 'next-auth'
import {redirect} from "next/navigation"

const Dashboard = async () => {
    const session = await getServerSession();
    if(!session) {
        redirect("/");
    }

  return (
    <div className="flex min-h-screen flex-col justify-center items-center p-24">   
        Dashboard</div>
  )
}

export default Dashboard

 

---LOGIN PAGE SETUP---

Step 8: Create a file app/login/page.tsx and paste below code:

"use client";
import Link from "next/Link";
import React, { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { signIn, useSession } from "next-auth/react";

const Login = () => {
  const [customerror, setCustomerror] = useState();
  const [data, setData] = useState({});
  const router = useRouter();

  const { data: userData, status: userSession } = useSession();

  const handleChange = (e: any) => {
    setData({ ...data, [e.target.name]: e.target.value });
  };

  useEffect(()=>{
    if(userSession=="authenticated")
    {
        router.replace("/dashboard");
    }

  },[userSession,router])

  const handleSubmit = async () => {
    const email = data.email;
    const password = data.password;

    const response = await signIn("credentials", {
      redirect: false,
      email,
      password,
    });
    console.log("response>>",response);

    if(response?.error){
        setCustomerror("Invalide user name and password");
    }
    else {

    console.log("else userdata>>",userData)
    console.log("else userSession>>",userSession)

        setCustomerror("");
    }

  };

  

  return (
    <div className="flex min-h-screen flex-col  items-center justify-between p-24">
      <div className="border-2 border-gray-500 p-8 rounded-lg shadow-lg w-96">
        <h1 className="text-3xl text-center font-semibold mb-8">Sign In</h1>

        <input
          name="email"
          className="w-full border border-gray-300 text-black rounded px-3 py-2 mb-4 focus:outline-none focus:border-blue-400 focus:text-black text-[18px]"
          placeholder="Email"
          required
          onChange={handleChange}
        />
        <input
          name="password"
          className="w-full border border-gray-300 text-black rounded px-3 py-2 focus:outline-none focus:border-blue-400 focus:text-black text-[18px]"
          placeholder="password"
          required
          onChange={handleChange}
        />
        <div className="text-center">
          <button
            onClick={() => handleSubmit()}
            className=" w-1/2 bg-blue-500 text-white  py-2 rounded hover:bg-blue-600 mt-6 text-[16px]"
          >
            Login
          </button>
          <div className="text-red-500 text-[16px] mb-4">
            {customerror && customerror}
          </div>
        </div>
        <div className="flex justify-center items-center">
          <button
            onClick={() => {
              signIn("github");
            }}
            className="bg-black text-white rounded-md p-4"
          >
            Github Login
          </button>
          <button className="bg-blue-500 text-white rounded-md p-4 ml-2">
            Google Login
          </button>
        </div>

        <div className="text-center text-gray-500 mt-4 text-sm">- OR - </div>
        <Link
          href="/register"
          className="block text-center text-blue-500 mt-2 text-[16px] hover:underline"
        >
          Register here
        </Link>
      </div>
    </div>
  );
};

export default Login;

 

---REGISTER PAGE SETUP---

 

Step 9: Create a file app/register/page.tsx and paste below code:

"use client";
import { setEngine } from "crypto";
import Link from "next/Link";
import React, { useState } from "react";
import {useRouter} from "next/navigation";
import {useSession } from 'next-auth/react'

const Register = () => {
  const [customerror, setCustomerror] = useState();
  const [data, setData] = useState({});
  const router = useRouter();

  const handleChange = (e: any) => {
    setData({ ...data, [e.target.name]: e.target.value });
  };

  const handleSubmit = async () => {

    const email = data.email;
    const password = data.password;

    try{

       const response =await  fetch("/api/register",{
        method: "POST",
        headers:{"Content-Type": "application/json"},
        body:JSON.stringify({email,password})
       });

       const res = await response.json();
       console.log(res.status);
       if(response.status==200)
       {
        router.push("/login")
       }
       if(response.status==400){
        setCustomerror(res.msg) ;
       }

    }catch(err){
      console.log(err);

    }





  };

  return (
    <div className="flex min-h-screen flex-col  items-center justify-between p-24">
      <div className="border-2 border-gray-500 p-8 rounded-lg shadow-lg w-96">
        <h1 className="text-3xl text-center font-semibold mb-8">Register</h1>

        <input
          name="email"
          className="w-full border border-gray-300 text-black rounded px-3 py-2 mb-4 focus:outline-none focus:border-blue-400 focus:text-black text-[18px]"
          placeholder="Email"
          required
          onChange={handleChange}
        />
        <input
          name="password"
          className="w-full border border-gray-300 text-black rounded px-3 py-2 focus:outline-none focus:border-blue-400 focus:text-black text-[18px]"
          placeholder="password"
          required
          onChange={handleChange}
        />
        <div className="text-center">
          <button
            onClick={() =>  handleSubmit()}
            className=" w-1/2 bg-blue-500 text-white  py-2 rounded hover:bg-blue-600 mt-6 text-[16px]"
          >
            Register
          </button>
          <div className="text-red-500 text-[16px] mb-4">
            {customerror && customerror}
          </div>
        </div>

        <div className="text-center text-gray-500 mt-4 text-sm">- OR - </div>
        <Link
          href="/login"
          className="block text-center text-blue-500 mt-2 text-[16px] hover:underline"
        >
          Login with existing account
        </Link>
      </div>
    </div>
  );
};

export default Register;

Step 10 : Paste below code in app/layout.tsx file

import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import NextAuthProvider from './common/NextAuthProvider'
import { getServerSession } from 'next-auth'
import Navbar from './components/Navbar'
import NavarBar from './components/NavarBar'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session= await getServerSession();
  return (
    <html lang="en">
      <body className={inter.className}>
        <NextAuthProvider session={session}>
          <div className="flex flex-col justify-center items-center">
            <NavarBar/>
          {children}
          </div>

        </NextAuthProvider>
        </body>

    </html>
  )
}

Step 11 : Create a .env.local file in project root:

 MONGO_URI=mongodb+srv://username:password@cluster0.0ka9dp1.mongodb.net/authmongodb?retryWrites=true&w=majority
 GITHUB_ID=<your github id>
 GITHUB_SECRET=<yout github secret>

---DATABASE SCHEMA MONGODB ---

Step 12 : Create a User.js and paste below code:

import mongoose from "mongoose";

const UserSchema = mongoose.Schema(
  {
    email: {
      type: String,
      unique: true,
      required: true,
    },
    password:{
        type:String,
        required:false
    }
  },
  { timestamps: true }
);

export default mongoose.models.User ||mongoose.model("User",UserSchema)

 

Github link :

https://github.com/TechMalasi/next-auth-nextjs14-updated/tree/master

Video tutorial :

https://youtu.be/BpLLNCTRgqA

 

 

1
Author
No Image
Admin
MmantraTech

Mmantra Tech is a online platform that provides knowledge (in the form of blog and articles) into a wide range of subjects .

Write a Response