In today's digital landscape, securing user data is paramount. With cyber threats on the rise, it's crucial to implement robust authentication mechanisms in your web applications. One such mechanism is server-side authentication, which ensures that all authentication processes are handled securely on the server, reducing the risk of client-side attacks.
This article delves into server-side authentication using Next Auth, a popular authentication library for Next.js, and MongoDB, a leading NoSQL database. We'll explore how these tools work together to provide a secure and scalable authentication solution for modern web applications.
Server-side authentication refers to the process of handling user authentication on the server rather than on the client. This approach offers several advantages, including enhanced security, as sensitive operations like password verification and token generation occur on a secure server.
While client-side authentication involves handling credentials and tokens directly on the user's device, server-side authentication processes these elements on the server. This distinction is crucial because server-side authentication minimizes exposure to potential vulnerabilities such as cross-site scripting (XSS) and token theft.
Next Auth is a flexible and easy-to-implement authentication solution designed specifically for Next.js applications. It provides a range of authentication methods, including email/password, OAuth, and social logins, making it a versatile choice for developers.
Next Auth is a preferred choice for many developers due to its simplicity, flexibility, and strong community support. It allows for quick setup while offering extensive customization options for more complex authentication scenarios.
MongoDB is a powerful NoSQL database that stores data in a flexible, JSON-like format. Its scalability and performance make it an excellent choice for managing user data in authentication systems.
While relational databases like MySQL are traditionally used for storing user data, MongoDB offers greater flexibility and scalability, making it a better fit for modern, dynamic applications that require rapid development and scaling.
Before diving into the integration of MongoDB, it's essential to set up Next Auth in your Next.js project. The following steps will guide you through the initial setup.
npx create-next-app@latest
npm install next-auth@beta
npm i mongodb // and mongoose// npm i mongoose
Create a Next Auth : In your Next.js project, create a new API route for Next Auth under the app/utils/auth.js directory.
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import GoogleProvider from "next-auth/providers/google";
import { connect } from "./ConnectMongo";
import User from "@/app/module/User";
import { authConfig } from "./auth.config";
export const {
handlers: { POST, GET },
auth,
signIn,
signOut,
} = NextAuth({
...authConfig,
providers: [
GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
callbacks: {
async signIn({ user, account, profile }) {
if (account.provider === "github") {
connect();
try {
const user = await User.findOne({ email: profile.email });
if (!user) {
const newUser = new User({
name: profile.name,
email: profile.email,
imageUrl: profile.avatar_url,
});
await newUser.save();
console.log("user is created");
Response.redirect(new URL("/dashboard"));
}
} catch (err) {
console.log("this is the error auth new user", err.message);
return true;
}
} else if (account.provider === "google") {
connect();
try {
const user = await User.findOne({ email: profile.email });
if (!user) {
const newUser = new User({
name: profile.name,
email: profile.email,
imageUrl: profile.picture,
});
await newUser.save();
console.log("user is created");
}
} catch (err) {
console.log("error when created user", err.message);
}
}
return true;
},
...authConfig.callbacks,
},
});
3.create User.js Model in this directory /app/models/User.js
import mongoose from "mongoose";
const { Schema } = mongoose;
const userSchema = new Schema(
{
name: {
type: String,
unique: true,
required: true,
},
email: {
type: String,
unique: true,
required: true,
},
imageUrl: {
type: String,
},
isAdmin: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
//If the User collection does not exist create a new one.
module.exports = mongoose.models?.users || mongoose.model("users", userSchema);
4.Create mongoDb connection function in this directory /app/utils/mongoDb.js
import mongoose from "mongoose";
const connect = async () => {
try {
await mongoose.connect(process.env.MONGO);
} catch (err) {
throw new Error("connection failed !");
}
};
export default connect;
5.create auth.config file in this directory /app/utils/auth.config.js
export const authConfig = {
page: {
signIn: "/login",
},
providers: [],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
token.isAdmin = user.isAdmin;
token.userSlug = user.userSlug;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id;
session.user.isAdmin = token.isAdmin;
session.user.userSlug = token.userSlug;
}
return session;
},
authorized({ auth, request }) {
const user = auth?.user;
const isOneBlog = request?.nextUrl?.pathname?.startsWith("/dashboard");
const adminUserPage = request?.nextUrl?.pathname === "/dashboard/users";
const adminDraftBlog =request?.nextUrl?.pathname == "dashboard/pending";
const isOnLoginPage = request?.nextUrl?.pathname.startsWith("/login");
if (adminUserPage && user?.email !== EMAIL) {
return Response.redirect(new URL("/dashboard", request.nextUrl));
}
if (adminDraftBlog && user?.name != "MOHAMMED DAKIR") {
return Response.redirect(new URL("/dashboard", request.nextUrl));
}
if (isOneBlog && !user) {
return Response.redirect(new URL("/login", request.nextUrl));
}
if (isOnLoginPage && user) {
return Response.redirect(new URL("/dashboard", request.nextUrl));
}
return true;
},
},
};
6.How to call auth function to get session in server side components
import React from "react";
import HomePage from "./components/HomePage";
import {auth} from "./utils/auth.js"
export default async function Home() {
//this is for call session to get user authentificationconst session =await auth()
return (
<>
<HomePage />
</>
);
}
7.How to call auth function to get session in client side components
to do that you be must pass auth.js as a props from server side to client side this example HomePage.js is a client component:
import React from "react";
import HomePage from "./components/HomePage";
import {auth} from "./utils/auth.js"
export default async function Home() {
//this is for call session to get user authentificationconst session =await auth()
return (
<>
<HomePage session={session} />
</>
);
}
In HomePage.js
"use client";
import React, { useState, useEffect } from "react";
import "../globals.css";
const HomePage = ({ session}) => {
useEffect(() => {
if(session){
router.push("/dashboard")
}else{
router.push("/login")
}
});
return (
<div className="p-10 xl:p-6 sm:p-2 xs:p-2 dark:bg-dark">
<h1>HomePage</h1>
</div>
);
};
export default HomePage;