From c48c9597490d0497f42cda176deafc0efbe4dab6 Mon Sep 17 00:00:00 2001 From: Chief Bube Date: Sat, 21 Oct 2023 03:06:58 -0700 Subject: [PATCH] added authentication --- .env | 1 + components/Authentication/SignInForm.js | 139 +++++++++++++++++++----- components/_App/Layout.js | 2 +- middleware.js | 53 +++++++-- middlewares/AuthRoute.js | 34 ++++-- pages/index.js | 92 +++++++++++++++- services/Fetcher.js | 61 +++++++++++ 7 files changed, 327 insertions(+), 55 deletions(-) create mode 100644 .env create mode 100644 services/Fetcher.js diff --git a/.env b/.env new file mode 100644 index 0000000..1c3188b --- /dev/null +++ b/.env @@ -0,0 +1 @@ +AUX_ENDPOINT="http://localhost:50016/api" \ No newline at end of file diff --git a/components/Authentication/SignInForm.js b/components/Authentication/SignInForm.js index 241e1bc..e0e437e 100644 --- a/components/Authentication/SignInForm.js +++ b/components/Authentication/SignInForm.js @@ -1,6 +1,9 @@ import React, { useState } from "react"; +import { useRouter } from "next/router"; +// import { cookies } from "next/headers"; import Link from "next/link"; import Grid from "@mui/material/Grid"; +import LoadingButton from "@mui/lab/LoadingButton"; import IconButton from "@mui/material/IconButton"; import { Typography } from "@mui/material"; import { Box } from "@mui/system"; @@ -10,16 +13,77 @@ import Button from "@mui/material/Button"; import Visibility from "@mui/icons-material/Visibility"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; import styles from "./signinform.module.css"; -import WrenchBoardLogo from "@/public/images/logos/wrenchboard-logo.png"; +import Fetcher from "services/Fetcher"; const SignInForm = () => { - const handleSubmit = (event) => { + const [formValues, setFormValues] = useState({ + email: "", + password: "", + }); + const [errorHandlers, setErrorHandlers] = useState({ + email: false, + password: false, + }); + const [loading, setLoading] = useState(false); + const api = new Fetcher(); + const router = useRouter(); + + const handleChange = (event) => { + setFormValues({ ...formValues, [event.target.name]: event.target.value }); + }; + + // "use server" + const handleSubmit = async (event) => { event.preventDefault(); - const data = new FormData(event.currentTarget); - console.log({ - email: data.get("email"), - password: data.get("password"), - }); + + const { email, password } = formValues; + + if (email === "" || password === "") { + setErrorHandlers({ ...errorHandlers, email: true, password: true }); + setTimeout(() => { + setErrorHandlers({ ...errorHandlers, email: false, password: false }); + }, 1500); + return; + } else if (email === "") { + setErrorHandlers({ ...errorHandlers, email: true }); + setTimeout(() => { + setErrorHandlers({ ...errorHandlers, email: false }); + }, 1500); + return; + } else if (password === "") { + setErrorHandlers({ ...errorHandlers, password: true }); + setTimeout(() => { + setErrorHandlers({ ...errorHandlers, password: false }); + }, 1500); + return; + } + + setLoading(true); + try { + const data = { + username: email, + password, + }; + const res = await api.login(data); + + if (res.status === 204 || res.length === 0) { + setErrorHandlers({ ...errorHandlers, email: true, password: true }); + } + + // Store the token in cookies + // const cookieStore = cookies(); + // cookieStore.set("cmc-profile", { ...res.profile }, { secure: true }); + // cookieStore.set("cmc-token", res.token, { secure: true }); + document.cookie = "cmc-token=" + res.token; + + // setLoading(false); + router.push("/"); + + console.log(res); + } catch (error) { + setLoading(false); + console.log(error); + } }; const [showPassword, setShowPassword] = useState(false); @@ -95,13 +159,20 @@ const SignInForm = () => { required fullWidth id="email" - label="Email Address" + label={ + errorHandlers.email + ? "Incomplete/wrong email" + : "Email Address" + } + value={formValues.email} + onChange={handleChange} name="email" autoComplete="email" InputProps={{ style: { borderRadius: 8 }, }} sx={textFieldStyles} + error={errorHandlers.email} /> @@ -122,10 +193,17 @@ const SignInForm = () => { required fullWidth name="password" - label="Password" + label={ + errorHandlers.password + ? "Incomplete/wrong password" + : "Password" + } + value={formValues.password} type={showPassword ? "text" : "password"} id="password" autoComplete="new-password" + onChange={handleChange} + error={errorHandlers.password} InputProps={{ style: { borderRadius: 8 }, endAdornment: ( @@ -154,26 +232,29 @@ const SignInForm = () => { - + + + Sign In + + diff --git a/components/_App/Layout.js b/components/_App/Layout.js index 979e0df..ecda064 100644 --- a/components/_App/Layout.js +++ b/components/_App/Layout.js @@ -20,7 +20,7 @@ const Layout = ({ children }) => { useEffect(() => { const authenticationPages = [ - "/", + // "/", "/auth", "/auth/login", "/auth/sign-up", diff --git a/middleware.js b/middleware.js index ae34892..15cb37d 100644 --- a/middleware.js +++ b/middleware.js @@ -1,12 +1,32 @@ import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; const checkAuthentication = async () => { - // Replace this logic with your actual authentication check. - const isAuthenticated = false; // Check if the user is authenticated. + const token = req.cookies["cmc-token"]; // Access the token from cookies + console.log("checking token", token); + const isAuthenticated = token ? true : false; // Check if the user is authenticated. return isAuthenticated; }; +const isTokenValid = () => { + const cookies = document.cookie.split("; "); // Get all cookies and split them into an array + + for (const cookie of cookies) { + const [name, value] = cookie.split("="); // Split the cookie into its name and value + + if (name === "cmc-token" && value) { + return true; // The cmc-token cookie exists + } + } + + return false; // The cmc-token cookie does not exist +}; + export async function middleware(req) { + const token = isTokenValid() + // req.cookies["cmc-token"]; // Access the token from cookies + const cookieList = cookies(); + const headers = new Headers(req.headers); headers.set("X-XSS-Protection", "1; mode=block"); headers.set("X-Frame-Options", "SAMEORIGIN"); @@ -15,16 +35,31 @@ export async function middleware(req) { const { origin, pathname } = req.nextUrl; try { - const authenticated = await checkAuthentication(); - - if (pathname === "/auth/login" && authenticated) { - return NextResponse.redirect(new URL("/ecommerce")); + if (pathname === "/auth/login" && token) { + // Redirect to the home page if already authenticated + return NextResponse.redirect(new URL("/"), { status: 307 }); } - if (authenticationPages.includes(pathname) && !authenticated) { - return NextResponse.redirect(new URL("/auth/login", origin)); + if (!authenticationPages.includes(pathname) && !token) { + // Redirect to the login page if not authenticated + return NextResponse.redirect(new URL("/auth/login", origin), { + status: 307, + }); } + // Add authentication logic here (verify the token, etc.) + // const isAuthenticated = verifyToken(token); + const isAuthenticated = cookieList.has("cmc-token"); + console.log(token) + + if (!isAuthenticated) { + // Handle unauthenticated users + return NextResponse.error(new Error("Authentication failed"), { + status: 401, + }); + } + + // Continue with the request if authenticated return NextResponse.next(); } catch (error) { console.error("Error during authentication check:", error); @@ -37,7 +72,7 @@ export const config = { }; const authenticationPages = [ - "/", + // "/", "/auth", "/auth/login", "/auth/sign-up", diff --git a/middlewares/AuthRoute.js b/middlewares/AuthRoute.js index 29beaf6..cfc8a64 100644 --- a/middlewares/AuthRoute.js +++ b/middlewares/AuthRoute.js @@ -1,24 +1,36 @@ -"use client"; -import { useEffect, useLayoutEffect } from "react"; +"use client" +import { useEffect } from "react"; import { useRouter } from "next/router"; -/** - * This function is used to protect routes in a web application. - * It checks if the user is authenticated and redirects them to the sign-in page if they are not. - - */ const AuthRoute = ({ children }) => { const router = useRouter(); - + + const token = req.cookies["cmc-token"]; // Access the token from cookies useEffect(() => { - const isAuthenticated = false; // In a real application, this would be determined based on the user's authentication status. - if (!isAuthenticated) { + const isAuthenticated = token ? true : false; + + if (router.pathname === "/auth/login" && isAuthenticated) { router.push("/"); - } + } + + if (!authenticationPages.includes(router.pathname) && !isAuthenticated) { + router.push("/auth/login"); + } }, []); return <>{children}; }; export default AuthRoute; + +const authenticationPages = [ + // "/", + "/auth", + "/auth/login", + "/auth/sign-up", + "/auth/forgot-password", + "/auth/lock-screen", + "/auth/confirm-mail", + "/auth/logout", +]; \ No newline at end of file diff --git a/pages/index.js b/pages/index.js index a3c2273..2883faf 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,6 +1,88 @@ -import SignInForm from "@/components/Authentication/SignInForm"; -import axios from "axios" +import React from "react"; +import Grid from "@mui/material/Grid"; +import Link from "next/link"; +import styles from "@/styles/PageTitle.module.css"; +import Features from "@/components/Dashboard/eCommerce/Features"; +import Ratings from "@/components/Dashboard/eCommerce/Ratings"; +import AudienceOverview from "@/components/Dashboard/eCommerce/AudienceOverview"; +import VisitsByDay from "@/components/Dashboard/eCommerce/VisitsByDay"; +import Impressions from "@/components/Dashboard/eCommerce/Impressions"; +import ActivityTimeline from "@/components/Dashboard/eCommerce/ActivityTimeline"; +import RevenuStatus from "@/components/Dashboard/eCommerce/RevenuStatus"; +import SalesByCountries from "@/components/Dashboard/eCommerce/SalesByCountries"; +import NewCustomers from "@/components/Dashboard/eCommerce/NewCustomers"; +import RecentOrders from "@/components/Dashboard/eCommerce/RecentOrders"; +import TeamMembersList from "@/components/Dashboard/eCommerce/TeamMembersList"; +import BestSellingProducts from "@/components/Dashboard/eCommerce/BestSellingProducts"; +import LiveVisitsOnOurSite from "@/components/Dashboard/eCommerce/LiveVisitsOnOurSite"; +import AuthRoute from "middlewares/AuthRoute"; -export default function SignIn() { - return ; -} \ No newline at end of file +function MainPage() { + return ( + <> + {/* Page title */} +
+

eCommerce

+
    +
  • + Dashboard +
  • +
  • eCommerce
  • +
+
+ + + {/* Features */} + + {/* AudienceOverview */} + + + + {/* VisitsByDay */} + + + + {/* Impressions */} + + + {/* ActivityTimeline */} + + + + {/* RevenuStatus */} + + + + + + {/* Ratings */} + + {/* LiveVisitsOnOurSite */} + + {/* SalesByLocations */} + + {/* NewCustomers */} + + + + {/* Recent Orders */} + + + + {/* TeamMembersList */} + + + + {/* BestSellingProducts */} + + + + + ); +} + +export default MainPage \ No newline at end of file diff --git a/services/Fetcher.js b/services/Fetcher.js new file mode 100644 index 0000000..6e667cc --- /dev/null +++ b/services/Fetcher.js @@ -0,0 +1,61 @@ +import Axios from "axios"; + +class Fetcher { + constructor(url) { + // this.url = url; + console.log("first request!!!"); + } + + // Endpoints Here + // GET /api/ + + // POST /api/ + login(values) { + return this.postAuxEnd("/auth/login", values); + } + + //---------------------------------------- ----- + // Unified call below + //---------------------------------------- ----- + + async getAuxEnd(uri, reqData) { + const endPoint = + (process.env.AUX_ENDPOINT || "http://localhost:50016/api") + uri; + console.log("Checking endpoint get request", endPoint); + try { + const response = await Axios.get(endPoint); + console.log(response.data); // Log the response data if needed. + return response.data; + } catch (error) { + this.handleAxiosError(error); + } + } + + async postAuxEnd(uri, reqData) { + const endPoint = + (process.env.AUX_ENDPOINT || "http://localhost:50016/api") + uri; + console.log("Checking endpoint post request", endPoint); + try { + const response = await Axios.post(endPoint, reqData); + console.log(response.data); // Log the response data if needed. + return response.data; + } catch (error) { + this.handleAxiosError(error); + } + } + + handleAxiosError(error) { + if (error.response) { + // Response status is an error code. + console.log(error.response.status); + } else if (error.request) { + // Response not received though the request was sent. + console.log(error.request); + } else { + // An error occurred when setting up the request. + console.log(error.message); + } + } +} + +export default Fetcher;