diff --git a/README.md b/README.md index 0dc9ea2..7a16ffe 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,47 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# Custom Authentication and Authorization Website -## Getting Started +Welcome to our website! This README file will guide you through the features and functionality of our custom authentication and authorization system. -First, run the development server: +## Overview -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +Our website provides a custom authentication and authorization system to manage user access to different pages. It consists of two main pages: -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +1. **Public Page:** Accessible to all users, located at the root path (`/`). +2. **Private Page:** Accessible only to logged-in users, located at the `/private` path. -You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. +Additionally, we have dedicated pages for signing up and logging in: -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. +- **Signup Page:** Allows users to create a new account, located at `/sign-up`. +- **Login/Sign-in Page:** Enables existing users to log in to their accounts, located at `/sign-in`. -## Learn More +## Features -To learn more about Next.js, take a look at the following resources: +- **Custom Authentication:** We've implemented a custom authentication system to manage user logins securely. +- **Authorization:** Users can only access the private page (`/private`) if they are authenticated. +- **Error Handling:** If users provide invalid credentials during login, the server sends an error message to notify them. -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +## Usage -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +1. **Signup:** Visit the `/sign-up` page to create a new account. Provide necessary details such as username, email, and password. +2. **Login:** After signing up, visit the `/sign-in` page to log in with your credentials. If you're an existing user, enter your email and password. +3. **Access Private Page:** Once logged in, you can access the private page (`/private`) to view its content. -## Deploy on Vercel +## Technologies Used -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +- **Frontend:** Next JS +- **Backend:** Next JS server Action +- **Database:** MongoDB -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +## Setup + +1. Clone the repository to your local machine. +2. Install dependencies using [package manager]. For example, if using Node.js, run `npm install`. +3. Configure the backend server to connect to your database. +4. Run the server locally. Ensure that the necessary routes (public, private, signup, signin) are properly configured. +5. Access the website through your browser. + +## Feedback + +If you have any feedback or suggestions, please feel free to [contact us](mailto:khandakerbayazidahmed@email.com). We'd love to hear from you! + +Thank you for using our custom authentication and authorization system. We hope you find it useful! diff --git a/actions/index.js b/actions/index.js index 50777f1..72bc068 100644 --- a/actions/index.js +++ b/actions/index.js @@ -1,7 +1,7 @@ "use server"; import connectDB from "@/database/connectDb"; import User from "@/database/models/userModels"; -import { generateJWTToken } from "@/utils"; +import { encrypt, generateJWTToken } from "@/utils"; import bcrypt from "bcrypt"; import { cookies } from "next/headers"; @@ -69,7 +69,7 @@ export async function signIn(formData) { }), }; } - let token = await generateJWTToken(foundUser._id.toString()); + let token = await encrypt({ user: foundUser._id.toString() }); cookies().set("token", token, { secure: true }); userOK = true; return { diff --git a/app/access-decline/page.jsx b/app/access-decline/page.jsx index 1eb7293..98db0c2 100644 --- a/app/access-decline/page.jsx +++ b/app/access-decline/page.jsx @@ -1,23 +1,26 @@ +import Navbar from "@/components/Navbar"; import SignInForm from "@/components/SignInForm"; import Link from "next/link"; import React from "react"; export default function page() { return ( -
-
-

- Log in for Access this page -

- - -
-

Don’t have an account?

- - Sign Up - + <> + +
+
+

+ Log in for Access this page +

+ +
+

Don’t have an account?

+ + Sign Up + +
-
+ ); } diff --git a/app/layout.jsx b/app/layout.jsx index 384c77e..8f484e0 100644 --- a/app/layout.jsx +++ b/app/layout.jsx @@ -12,10 +12,7 @@ export const metadata = { export default function RootLayout({ children }) { return ( - - - {children} - + {children} ); } diff --git a/app/loading.jsx b/app/loading.jsx index 99eff78..fa78587 100644 --- a/app/loading.jsx +++ b/app/loading.jsx @@ -1,5 +1,3 @@ -import React from "react"; - export default function loading() { return (
+
Public Icon diff --git a/app/private/page.jsx b/app/private/page.jsx index c449c8c..7a0da9c 100644 --- a/app/private/page.jsx +++ b/app/private/page.jsx @@ -5,6 +5,7 @@ import privateImage from "@/public/private.gif"; export default function Home() { return (
+
private Icon diff --git a/components/LogoutButton.jsx b/components/LogoutButton.jsx index 939b30e..36295a0 100644 --- a/components/LogoutButton.jsx +++ b/components/LogoutButton.jsx @@ -1,22 +1,23 @@ "use client"; import { signOut } from "@/actions"; -import { useEffect } from "react"; +import { useState } from "react"; export default function LogoutButton() { - useEffect(() => { - console.log("run once"); - }); + const [loading, setLoading] = useState(false); + if (loading) return

Loading...

; return ( diff --git a/components/NavLogoutButtonArea.jsx b/components/NavLogoutButtonArea.jsx index 553ee4e..b87fc90 100644 --- a/components/NavLogoutButtonArea.jsx +++ b/components/NavLogoutButtonArea.jsx @@ -1,10 +1,8 @@ import { cookies } from "next/headers"; import Link from "next/link"; import LogoutButton from "./LogoutButton"; -import { redirect } from "next/navigation"; export default function NavLogoutButtonArea() { const login = cookies().get("token"); - return (
  • {login ? ( diff --git a/components/Navbar.jsx b/components/Navbar.jsx index 2a505a0..a997ef4 100644 --- a/components/Navbar.jsx +++ b/components/Navbar.jsx @@ -1,8 +1,5 @@ import { navItems } from "@/constant"; -import { cookies } from "next/headers"; import Link from "next/link"; -import React from "react"; -import LogoutButton from "./NavLogoutButtonArea"; import NavLogoutButtonArea from "./NavLogoutButtonArea"; const Navbar = () => { diff --git a/middleware.js b/middleware.js index 377dce0..e135336 100644 --- a/middleware.js +++ b/middleware.js @@ -1,20 +1,19 @@ import { NextResponse } from "next/server"; -import { verifyJWT } from "./utils"; +import { decrypt } from "./utils"; export async function middleware(req) { - console.log("middleware running"); + const url = req.nextUrl.clone(); const token = req.cookies.get("token"); - // if (!token) { - // const url = req.nextUrl.clone(); - // url.pathname = "/access-decline"; - // return NextResponse.rewrite(url); - // } - // let decoded = await verifyJWT(token.value); - // console.log(decoded); - // const url = req.nextUrl.clone(); - // url.pathname = "/private"; - // return NextResponse.rewrite(url); + if (!token) { + url.pathname = "/access-decline"; + return NextResponse.rewrite(url); + } + let decoded = await decrypt(token.value); + if (!decoded) { + url.pathname = "/access-decline"; + return NextResponse.rewrite(url); + } } export const config = { - matcher: ["/private", "/sign-in", "/sign-up"], + matcher: ["/private"], }; diff --git a/utils/index.js b/utils/index.js index c90a349..768b0c4 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,13 +1,25 @@ -import jose from "jose"; -export const generateJWTToken = async (payload) => { - const alg = "HS256"; - const jwt = await new jose.SignJWT(payload) - .setProtectedHeader({ alg }) +import { SignJWT, jwtVerify } from "jose"; + +const secretKey = "secret"; +const key = new TextEncoder().encode(secretKey); + +export async function encrypt(payload) { + const expirationTime = new Date(); + expirationTime.setHours(expirationTime.getHours() + 1); + return await new SignJWT(payload) + .setProtectedHeader({ alg: "HS256" }) .setIssuedAt() - .setIssuer("urn:example:issuer") - .setAudience("urn:example:audience") - .setExpirationTime("2h") - .sign(process.env.JWT_SECRET); - console.log(jwt); - return jwt; -}; + .setExpirationTime(expirationTime) + .sign(key); +} + +export async function decrypt(input) { + try { + const { payload } = await jwtVerify(input, key, { + algorithms: ["HS256"], + }); + return payload; + } catch (error) { + return false; + } +}