From 403394ad70ef1dcf395227541ef4fc4f7a583de2 Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Sun, 26 May 2024 19:20:23 +0200 Subject: [PATCH 1/7] first commit --- backend/package.json | 3 ++ backend/server.js | 89 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..3bda8e5d3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,8 +12,11 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", "cors": "^2.8.5", "express": "^4.17.3", + "express-list-endpoints": "^7.1.0", "mongoose": "^8.0.0", "nodemon": "^3.0.1" } diff --git a/backend/server.js b/backend/server.js index dfe86fb8e..ae4d457b6 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,14 +1,35 @@ import cors from "cors"; import express from "express"; import mongoose from "mongoose"; +import crypto from "crypto"; +import bcrypt from "bcryptjs"; +import expressListEndpoints from "express-list-endpoints"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; +const mongoUrl = process.env.MONGO_URL || "mongodb://127.0.0.1/project-auth"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; -// Defines the port the app will run on. Defaults to 8080, but can be overridden -// when starting the server. Example command to overwrite PORT env variable value: -// PORT=9000 npm start +const User = mongoose.model("User", { + name: { type: String, unique: true, required: true }, + email: { type: String, unique: true, required: true }, + password: { type: String, required: true }, + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex"), //user gets a string of random numbers as the accessToken + }, +}); + +//a middleware function for looking for the user based on the accessToken saved in "Authorization" in the header +const authenticateUser = async (req, res, next) => { + const user = await User.findOne({ accessToken: req.header("Authorization") }); + if (user) { + req.user = user; + next(); + } else { + res.status(401).json({ loggedOut: true }); + } +}; + const port = process.env.PORT || 8080; const app = express(); @@ -18,7 +39,65 @@ app.use(express.json()); // Start defining your routes here app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const endpoints = expressListEndpoints(app); + const documentation = endpoints.map((endpoint) => ({ + method: endpoint.methods.join(", "), + path: endpoint.path, + })); + res.json(documentation); +}); + +app.post("/registration", async (req, res) => { + try { + const { name, email, password } = req.body; + + //error handling + if (!name || name.trim() === "") { + return res.status(400).json({ message: "Please enter username" }); + } + if (!email || email.trim() === "") { + return res.status(400).json({ message: "Please enter email" }); + } + if (!password || password.trim() === "") { + return res.status(400).json({ message: "Please enter password" }); + } + if (password.length < 6) { + return res + .status(400) + .json({ message: "Password must be at least 6 characters long" }); + } + + const salt = bcrypt.genSaltSync(); + const user = new User({ + name, + email, + password: bcrypt.hashSync(password, salt), + }); + user.save(); + res.status(201).json({ id: user._id, accessToken: user.accessToken }); + } catch (err) { + res + .status(400) + .json({ message: "Could not create the user", errors: err.errors }); + } +}); + +app.get("/dashboard", authenticateUser); +app.get("/dashboard", (req, res) => { + res.json({ message: "Your're logged in!" }); +}); + +app.post("/login", async (req, res) => { + const user = await User.findOne({ email: req.body.email }); + if (user && bcrypt.compareSync(req.body.password, user.password)) { + res.json({ + userId: user._id, + name: user.name, + accrssToken: user.accessToken, + }); + } else { + res.json({ notFound: true }); + } }); // Start the server From d255fb3beb9fb561f5ec28e48be23f52aa782e28 Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Sun, 26 May 2024 22:39:20 +0200 Subject: [PATCH 2/7] build structure --- backend/server.js | 7 +++---- frontend/index.html | 25 ++++++++++++++----------- frontend/package.json | 3 ++- frontend/src/App.jsx | 13 ++++++++++++- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/backend/server.js b/backend/server.js index ae4d457b6..727f29761 100644 --- a/backend/server.js +++ b/backend/server.js @@ -82,9 +82,8 @@ app.post("/registration", async (req, res) => { } }); -app.get("/dashboard", authenticateUser); -app.get("/dashboard", (req, res) => { - res.json({ message: "Your're logged in!" }); +app.get("/dashboard", authenticateUser, (req, res) => { + res.json({ message: "You're logged in!" }); }); app.post("/login", async (req, res) => { @@ -96,7 +95,7 @@ app.post("/login", async (req, res) => { accrssToken: user.accessToken, }); } else { - res.json({ notFound: true }); + return res.status(400).json({ notFound: true }); } }); diff --git a/frontend/index.html b/frontend/index.html index 0c589eccd..69a744d15 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,13 +1,16 @@ - - - - - Vite + React - - -
- - - + + + + + + Project Auth + + + +
+ + + + \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index e9c95b79f..db1a62a6e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.23.1" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1091d4310..2eadac160 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,14 @@ +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { DashboardPage } from "./pages/DashboardPage"; +import { SignLogInPage } from "./pages/SignLogInPage"; + export const App = () => { - return
Find me in src/app.jsx!
; + return ( + + + } /> + } /> + + + ); }; From 02907ff008261e45ffd5fce144e2e02438975e1b Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Mon, 27 May 2024 07:02:18 +0200 Subject: [PATCH 3/7] add pages --- frontend/src/components/LogInForm.jsx | 78 ++++++++++++++++++ frontend/src/components/RegistrationForm.jsx | 84 ++++++++++++++++++++ frontend/src/pages/DashboardPage.jsx | 3 + frontend/src/pages/SignLogInPage.jsx | 14 ++++ 4 files changed, 179 insertions(+) create mode 100644 frontend/src/components/LogInForm.jsx create mode 100644 frontend/src/components/RegistrationForm.jsx create mode 100644 frontend/src/pages/DashboardPage.jsx create mode 100644 frontend/src/pages/SignLogInPage.jsx diff --git a/frontend/src/components/LogInForm.jsx b/frontend/src/components/LogInForm.jsx new file mode 100644 index 000000000..c4e80d945 --- /dev/null +++ b/frontend/src/components/LogInForm.jsx @@ -0,0 +1,78 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; + +export const LogInForm = () => { + const [logInData, setlogInData] = useState({ + email: "", + password: "", + }); + const [message, setMessage] = useState(""); + + const navigate = useNavigate(); + + const handleChange = (event) => { + setlogInData({ + ...logInData, + [event.target.name]: event.target.value, + }); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + try { + const response = await fetch( + "https://project-authentication-6r12.onrender.com/login", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(logInData), + } + ); + + if (!response.ok) { + throw new Error("Failed at logging in..."); + } + + const data = await response.json(); + console.log("User logged in:", data); + + setlogInData({ + email: "", + password: "", + }); + + setMessage("Login successful!"); + navigate("/dashboard"); + } catch (error) { + console.error("Login failed:", error); + setMessage("Login failed. Please try again."); + } + }; + + return ( +
+
+ + + +
+ {message &&

{message}

} +
+ ); +}; diff --git a/frontend/src/components/RegistrationForm.jsx b/frontend/src/components/RegistrationForm.jsx new file mode 100644 index 000000000..707e06046 --- /dev/null +++ b/frontend/src/components/RegistrationForm.jsx @@ -0,0 +1,84 @@ +import { useState } from "react"; + +export const RegistrationForm = () => { + const [registrationData, setRegistrationData] = useState({ + name: "", + email: "", + password: "", + }); + const [message, setMessage] = useState(""); + + const handleChange = (event) => { + setRegistrationData({ + ...registrationData, + [event.target.name]: event.target.value, + }); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + try { + const response = await fetch( + "https://project-authentication-6r12.onrender.com/registration", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(registrationData), + } + ); + + if (!response.ok) { + throw new Error("Failed at creating the account..."); + } + + const data = await response.json(); + console.log("User registered:", data); + + setRegistrationData({ + name: "", + email: "", + password: "", + }); + + setMessage("Registration successful!"); + } catch (error) { + console.error("Registration failed:", error); + setMessage("Registration failed. Please try again."); + } + }; + + return ( +
+
+ + + + +
+ {message &&

{message}

} +
+ ); +}; diff --git a/frontend/src/pages/DashboardPage.jsx b/frontend/src/pages/DashboardPage.jsx new file mode 100644 index 000000000..d5fc177bc --- /dev/null +++ b/frontend/src/pages/DashboardPage.jsx @@ -0,0 +1,3 @@ +export const DashboardPage = () => { + return

Dashboard

; +}; diff --git a/frontend/src/pages/SignLogInPage.jsx b/frontend/src/pages/SignLogInPage.jsx new file mode 100644 index 000000000..63b79c14a --- /dev/null +++ b/frontend/src/pages/SignLogInPage.jsx @@ -0,0 +1,14 @@ +import { RegistrationForm } from "../components/RegistrationForm"; +import { LogInForm } from "../components/LogInForm"; + +export const SignLogInPage = () => { + return ( +
+

Welcome!

+
+ + +
+
+ ); +}; From f3589d216fe48de1a0e5dc0959400ffc653c2219 Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Mon, 27 May 2024 07:03:40 +0200 Subject: [PATCH 4/7] debug server.js --- backend/server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index 727f29761..544d5df62 100644 --- a/backend/server.js +++ b/backend/server.js @@ -82,7 +82,8 @@ app.post("/registration", async (req, res) => { } }); -app.get("/dashboard", authenticateUser, (req, res) => { +app.get("/dashboard", authenticateUser); +app.get("/dashboard", (req, res) => { res.json({ message: "You're logged in!" }); }); From b3880925ec1b87b8d9d1cc8215a1fd0846b7eee0 Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Mon, 27 May 2024 18:55:56 +0200 Subject: [PATCH 5/7] debug backend - fix typo accessToken --- backend/server.js | 2 +- frontend/src/components/LogInForm.jsx | 3 ++ frontend/src/pages/DashboardPage.jsx | 55 ++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/backend/server.js b/backend/server.js index 544d5df62..59b5f90b9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -93,7 +93,7 @@ app.post("/login", async (req, res) => { res.json({ userId: user._id, name: user.name, - accrssToken: user.accessToken, + accessToken: user.accessToken, }); } else { return res.status(400).json({ notFound: true }); diff --git a/frontend/src/components/LogInForm.jsx b/frontend/src/components/LogInForm.jsx index c4e80d945..47f458bc9 100644 --- a/frontend/src/components/LogInForm.jsx +++ b/frontend/src/components/LogInForm.jsx @@ -38,6 +38,9 @@ export const LogInForm = () => { const data = await response.json(); console.log("User logged in:", data); + //save the accessToken to localStorage + localStorage.setItem("accessToken", data.accessToken); + setlogInData({ email: "", password: "", diff --git a/frontend/src/pages/DashboardPage.jsx b/frontend/src/pages/DashboardPage.jsx index d5fc177bc..988195dbe 100644 --- a/frontend/src/pages/DashboardPage.jsx +++ b/frontend/src/pages/DashboardPage.jsx @@ -1,3 +1,56 @@ +import { useState, useEffect } from "react"; + export const DashboardPage = () => { - return

Dashboard

; + const [message, setMessage] = useState(""); + + useEffect(() => { + const fetchAccessToken = async () => { + const accessToken = localStorage.getItem("accessToken"); + + if (!accessToken) { + setMessage("You don't have access to this page. Please log in first."); + return; + } + + try { + const response = await fetch( + "https://project-authentication-6r12.onrender.com/dashboard", + { + headers: { + Authorization: accessToken, + }, + } + ); + + if (!response.ok) { + throw new Error( + "Failed to log into your dashboard. Please try again." + ); + } + + const data = await response.json(); + setMessage(data.message); + } catch (err) { + console.error("Failed to fetch dashboard info:", err); + setMessage("Failed to log into your dashboard. Please try again."); + } + }; + + fetchAccessToken(); + }, []); + + return ( +
+

Dashboard

+

{message}

+ +
+ ); }; From 1982c1e7baeb5ab762e78909abe3c0510a06c88d Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Mon, 27 May 2024 19:05:32 +0200 Subject: [PATCH 6/7] delete netlify.tml --- netlify.toml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 netlify.toml diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index 95443a1f3..000000000 --- a/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -# This file tells netlify where the code for this project is and -# how it should build the JavaScript assets to deploy from. -[build] - base = "frontend/" - publish = "build/" - command = "npm run build" From 653af29cf0edfbcdb3078847efd01a6a633f54d1 Mon Sep 17 00:00:00 2001 From: Yifan-858 <133569248+Yifan-858@users.noreply.github.com> Date: Mon, 27 May 2024 20:38:19 +0200 Subject: [PATCH 7/7] update README --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index dfa05e177..ce8626bb7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,7 @@ # Project Auth API -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. - -## The problem - -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? - ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +Front-end: [![Netlify Status](https://api.netlify.com/api/v1/badges/93c0dec7-42c3-4b60-833d-fc9f955c4a81/deploy-status)](https://app.netlify.com/sites/yifan-project-authentication/deploys) + +Back-end: https://project-authentication-6r12.onrender.com/