Easily Manage Authentication In Your Next.js Apps
first, we import the AuthProvider Component and wrap the main layout with the component
return (
<AuthProivder >
<html lang='en'>
<body className={inter.className}>{children}</body>
</html>
<AuthProivder/>
)
on your login or register page import AuthorizeClient Function and pass fromData to validate and pass an API route you define to handle login or register the response should contain only the session object
<form
onSubmit={async (e) => {
e.preventDefault();
let form: FormData = new FormData(e.currentTarget);
await AuthroizeCLiet( 'login' ,form )
}}
>
To consume the User Data in your client import the GetUser Function anywhere in your app if the client isn't authorized it will return undefined otherwise, it will return the User Data it can also use to protect client routes
export default function ProtectedRoute (){
let data = GetUser();
if(!data) return router.replace('/login')
return <div>{data.username}</data>
}
first, in .env file, we create AUTH_SECRET key with any value that can be used as a secret
AUTH_SECRET=as2?<>KJ34@#odp349582@#1qwe
first create in your API folder a folder named easyauth, inside this folder we will add routes to handle auth logic, for login you create a folder named login and for register the same thing for providers create a folder with the same name as the provider ex gitHub folder for github provider all these routes should be POST routes expect the "me" route should be GET
/api
/easyauth
/login
/register
/me
/github
In your login or register, provider route after validating the form or saving the user in the database it's time to generate a token call the Authenticate function and pass a date (the date format should be :"10d","10h","10m" ends with d for days, h for hours, and m for minutes) and a payload the payload should be an object, the response should only return the object generated by the Authenticate function
Example
import { NextResponse } from "next/server";
import Joi, { Schema } from "joi";
import hashing from "bcrypt";
import { db } from "@/db/db";
import { Artists } from "@/db/schema";
import { eq } from "drizzle-orm";
import Authenticate from "easynextauth
export async function POST(req: Request) {
let form = await req.formData();
let email = form.get("email")
let password = form.get("password")
const schema: Schema = Joi.object({
email: Joi.string()
.email({ tlds: { allow: false } })
.required()
.min(8)
.max(120)
.label("Email"),
password: Joi.string().required().min(8).max(120).label("Password"),
});
const { error } = schema.validate({email,password});
if (error) return new Response(error.message, { status: 400 });
try {
let artist = await db
select({
d: Artists.id,
email: Artists.email,
password: Artists.password,
})
.from(Artists)
.where(eq(Artists.email, data.email));
if (artist.length === 0) {
return new Response("Email Doesn't Exist", { status: 400 });
}
let hashed_pass: boolean = await hashing.compare(
data.password,
artist[0].password!
);
if (!hashed_pass) {
return new Response("Invalid Password", { status: 400 });
}
let session = await Authenticate({id:artist[0].id!},"10d");
// the response should be in json and return the session object
return NextResponse.json(session, { status: 200 });
} catch (error) {
return new Response("Something Wrong Happen", { status: 400 });
}
}
To protect routes in your middleware file import the GetPayload Function the function returns the payload if the token you provide is a valid token otherwise it returns an error
Example
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import GetPayload from "easynextauth";
export async function middleware(request: NextRequest) {
try {
await GetPayload();
return NextResponse.next();
} catch (error) {
return new Response("Invalid Token", { status: 400 });
}
}
export const config = {
matcher: [
"/api/me",
"/api/follow/:path*",
"/api/unfollow/:path*",
"/api/like/:path*",
"/api/dislike/:path*",
"/api/comment/:path*",
"/api/upload",
],
};
to retrieve personal data define a route called 'me' The route should return the data you want the client to consume you can protect this route from the middleware or call the GetPayload if it throws an error response to the client with the error else it will return the payload and that's what we want I added the user id to the payload so we can use this id to retrieve user data or do other operation make sure its GET method
Example
import { NextResponse } from "next/server";
import { db } from "@/db/db";
import { Artists } from "@/db/schema";
import { eq } from "drizzle-orm";
import GetPayload from "easynextauth"
export async function GET(req: Request) {
try {
let payload = await GetPayload();
let result = await db
.select({
id: Artists.id,
name: Artists.name,
followers: Artists.followers,
following: Artists.following,
songs: Artists.songs,
cover: Artists.cover,
})
.from(Artists)
.where(eq(Artists.id, payload.id));
if (result.length === 0) return new Response("Something Wrong Happen", {
status:400}
return NextResponse.json(result[0]);
} catch (error) {
return new Response("Something Wrong Happen", { status: 400 });
}
}
in you easyauth api folder define a route caled github in this route call the GithubServer function and pass the client_id and the client_secret, this function will return the user personal information from github you can do whatever you want with this date
Example
import { NextResponse } from "next/server";
import { db } from "@/db/db";
import { Users } from "@/db/schema";
import GithubServer from "easynextauth"
export async function POST(req: Request) {
let userData = await GithubServer(client_id, client_secret)
try {
let user = await db.insert(Users).values(userData);
// and dont forget to call the Authenticate function
let session = await Authenticate({id:user[0].id!},"10d");
return NextResponse.json(session, { status: 200 });
} catch (error) {
return new Response("Something Wrong Happen", { status: 400 });
}
}
in the client side call the GithubClient function and pass the client_id