7.Authentication in NextJS with server actions

3 min read
Installations neccessary dependecies
// create next app
npx create-next-app@latest next-auth
//install shadcn
npx shadcn@latest init
//other
npm i bcryptjs jsonwebtoken
npx shadcn@latest add input // optional for styling
npx shadcn@latest add label
npm i mongoose // for database connection
SignUp
//src/actions/auth.js
'use server'
import dbConnect from "@/database"
import User from "@/models";
import bcryptjs from "bcryptjs"
export async function SignUp(formData){
await dbConnect();
try{
const {name , email , password} = formData;
const existingUser = await User.findOne({email:email});
if(existingUser){
return {
success:false,
message:"User already exist ! Please Login"
}
}
// hash the password
const salt =await bcryptjs.genSalt(10);
const hasedPassword= await bcryptjs.hash(password,salt);
const newUser = await User.create({ //model:extracted data
name:name,
email:email,
password:hasedPassword
})
return {
success:true,
message:"Account created successfully",
data: JSON.parse(JSON.stringify(newUser))
}
}
catch(err){
console.log(err)
return {
success:false,
message:"Internal server error"
}
}
}
Now Call this action from frontend form action just like previous article
Login
export async function Login(formData){ await dbConnect(); try{ const{email , password}= formData; const existingUser = await User.findOne({email:email}); if(!existingUser){ return { success:false, message:"User not exist, Please Signup" } } //check password const checkPassword = await bcryptjs.compare(password, existingUser.password); if(!checkPassword){ return { success:false, message:"Invalid Credetials" } } // create token if password is correct const payload = { id:existingUser._id, userName:existingUser.name, email:existingUser.email } const token = jwt.sign(payload , 'JWT_SECRET_KEY' , {expiresIn:'1d'}); //store this token const getCookies = cookies() getCookies.set('token',token) return { success:true, message:"Login successful" } } catch(err){ console.log(err) return { success:false, message:"Internal Server Error" } } }
4. If we wany user data- name and email so we look for the token as our user data present in token payload , decode the token from cookies(or pass it to the login response and store it in localStorage but here we store in cookies)
// action -> now ftech the user info from any page in any server component too (we cant use localstorage in server component)
export async function fetchAuthUserData(){
await dbConnect()
try{
const getCookies = cookies();
const token = getCookies.get("token")?.value || "";
if(token==""){
return {
success:false,
message:"Token missing"
}
}
const decodeToken = jwt.verify(token , "JWT_SECRET_KEY") // it give us payload that is stored in payload of token
const getUserInfo = await User.findOne({_id:decodeToken.id});
if(getUserInfo){
return {
success:true,
data:JSON.parse(JSON.stringify(getUserInfo)) // contain user name and email and id
}
}
else{
return{
success:false,
message:"Token is invalid"
}
}
}
catch(err){
return {
success:false,
message:"Internal Server error , Please try again later"
}
}
}
// frontend
import { fetchAuthUserData } from "@/actions/auth";
import Image from "next/image";
export default async function Home() {
const currentUser = await fetchAuthUserData() // using current user we decdie what to show on home page login and signup buttons
if(!currentUser?.success) redirect('/login') // we cant access home page if token is not availablle
return (
<div className="w-11/12 mx-auto mt-5 font-bold text-3xl">
Authentication in NextJS
<p>{currentUser?.data?.name}</p>
<p>{currentUser?.data?.email}</p>
<LogOut/>
</div>
);
}
If token is present then we cant allowed to access the openRoutes - sign-up , login page ; To make open Route we use concept of middleware in nextJS
//src/middleware.js import { cookies } from "next/headers"; import { NextResponse } from "next/server"; export function middleware(request){ const path = request.nextUrl.pathname; const checkOpenRoute = path ==="/sign-up" || path==="/login"; const getCookies = cookies(); const token = getCookies.get("token")?.value || ""; if(checkOpenRoute && token !== ""){ // if user trying to access open/pulbic route when he is authenticated return NextResponse.redirect(new URL('/', request.nextUrl)); } if(!checkOpenRoute && token ===""){ // if user is not authneicated but he trying to access private route return NextResponse.redirect(new URL('/login',request.nextUrl)) } } export const config={ matcher:['/sign-up' , '/login'] }
Implement LogOut Functionality
// logout in action export async function LogOutAction(){ const getCookies = cookies() getCookies.set("token","") // make token as empty } // frontend 'use client' import { Button } from "../ui/button"; import { LogOutAction } from "@/actions/auth"; export default function LogOut(){ const handleLogOut = async()=>{ await LogOutAction() } return ( <Button onClick={handleLogOut}>LogOut</Button> ) }
0
Subscribe to my newsletter
Read articles from Ayush Rajput directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
