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
-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
Write a Response