From 37d566d2f987b9fbf8ea8cb319ef8d28932d3da6 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Tue, 21 May 2024 10:19:18 +0200 Subject: [PATCH 01/21] instal dependecies --- backend/server.js | 4 ++-- package.json | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/server.js b/backend/server.js index dfe86fb8e..579df2be5 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2,14 +2,14 @@ import cors from "cors"; import express from "express"; import mongoose from "mongoose"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/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 port = process.env.PORT || 8080; +const port = process.env.PORT || 9000; const app = express(); // Add middlewares to enable cors and json body parsing diff --git a/package.json b/package.json index d774b8cc3..6d01d9860 100644 --- a/package.json +++ b/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "scripts": { "postinstall": "npm install --prefix backend" + }, + "dependencies": { + "bcrypt": "^5.1.1" } } From 61e901ca7ef2377c7862a7fbc9ee5e5c226447b7 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Tue, 21 May 2024 22:53:02 +0200 Subject: [PATCH 02/21] in progress --- backend/package.json | 2 +- backend/server.js | 57 ++++++++++++++++++++++++++++++++++++++++++++ frontend/index.html | 3 +-- netlify.toml | 7 +++++- package.json | 5 +++- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..4701f6136 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,4 +17,4 @@ "mongoose": "^8.0.0", "nodemon": "^3.0.1" } -} +} \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 579df2be5..4e2a8c4f8 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,11 +1,56 @@ +import bcrypt from " bcrypt"; import cors from "cors"; +import dotenv from "dotenv"; import express from "express"; +import jwt from "jsonwebtoken"; import mongoose from "mongoose"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-auth"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; +dotenv.config(); + +const userSchema = mongoose.model("User", { + username: { + type: String, + unique: true, + }, + email: { + type: String, + unique: true, + }, + password: { + type: String, + required: true, + }, + token: { + type: String, + }, +}); + +const User = model("User", userSchema); + +const generateAccessToken = (userId) => { + return jwt.sign({ userId }, process.env.ACCESS_TOKEN_SECRET, { + expriresIn: "1h", + }); +}; + +const authethicateToken = async (req, res, next) => { + const authHeader = req.headers["authorization"]; + const token = authHeader && authHeader.split(" ")[1]; + if (token == null) return res.sendStatus(401); +}; + +try { + const decoded = jwt.verify(toke, process.env.ACCESS_TOKEN_SECRET); + req.user = await User.findById(decoded.userId); + next(); +} catch (err) { + return res.sendStatus(403); +} + // 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 @@ -21,6 +66,18 @@ app.get("/", (req, res) => { res.send("Hello Technigo!"); }); +//Registration Endpoint +app.post("/register", async (req, res) => { + try { + const { username, email, password } = req.body; + const hashedPassword = await bcrypt.hash(password, 10); + const user = new User({ username, email, password: hashedPassword }); + const savedUser = await user.save(); + res.status(201).json({ id: savedUser._id }); + } catch (error) { + res.status(400), json({ error: "Registartion failed" }); + } +}); // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/frontend/index.html b/frontend/index.html index 0c589eccd..b9bb266ae 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,8 +1,7 @@ - + - Vite + React diff --git a/netlify.toml b/netlify.toml index 95443a1f3..1bb70e3a3 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,5 +2,10 @@ # how it should build the JavaScript assets to deploy from. [build] base = "frontend/" - publish = "build/" + publish = "dist" command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 \ No newline at end of file diff --git a/package.json b/package.json index 6d01d9860..b42cb7490 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "postinstall": "npm install --prefix backend" }, "dependencies": { - "bcrypt": "^5.1.1" + "bcrypt": "^5.1.1", + "dotenv": "^16.4.5", + "js": "^0.1.0", + "jsonwebtoken": "^9.0.2" } } From a45eedae2d46afdf9eb270abceb8adb322b25fbf Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Wed, 22 May 2024 14:44:59 +0200 Subject: [PATCH 03/21] endpoints builded --- backend/package.json | 5 +++- backend/server.js | 64 +++++++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4701f6136..e8ddc2b7f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,10 +1,11 @@ { + "type": "module", "name": "project-auth-backend", "version": "1.0.0", "description": "Starter project to get up and running with express quickly", "scripts": { "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "dev": "nodemon server.js" }, "author": "", "license": "ISC", @@ -12,6 +13,8 @@ "@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", "mongoose": "^8.0.0", diff --git a/backend/server.js b/backend/server.js index 4e2a8c4f8..8c04b7c92 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,55 +1,66 @@ -import bcrypt from " bcrypt"; +import bcrypt from "bcrypt"; +import crypto from "crypto"; import cors from "cors"; import dotenv from "dotenv"; import express from "express"; import jwt from "jsonwebtoken"; import mongoose from "mongoose"; +dotenv.config(); + const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-auth"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; -dotenv.config(); +const { Schema, model } = mongoose; -const userSchema = mongoose.model("User", { +const userSchema = new Schema({ username: { type: String, unique: true, + required: true, + minlength: 3, }, email: { type: String, unique: true, + required: true, }, password: { type: String, required: true, + required: true, + minlength: 6, }, - token: { + accessToken: { type: String, + default: () => crypto.randomBytes(16).toString("hex"), }, }); const User = model("User", userSchema); +// Function to generate JWT const generateAccessToken = (userId) => { return jwt.sign({ userId }, process.env.ACCESS_TOKEN_SECRET, { - expriresIn: "1h", + expiresIn: "24h", }); }; -const authethicateToken = async (req, res, next) => { +// Middleware to athenticate the token +const authenticateToken = async (req, res, next) => { const authHeader = req.headers["authorization"]; const token = authHeader && authHeader.split(" ")[1]; if (token == null) return res.sendStatus(401); -}; -try { - const decoded = jwt.verify(toke, process.env.ACCESS_TOKEN_SECRET); - req.user = await User.findById(decoded.userId); - next(); -} catch (err) { - return res.sendStatus(403); -} + try { + const decoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET); + req.user = await User.findById(decoded.userId); + next(); + } catch (err) { + return res.sendStatus(403); + } +}; // 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: @@ -67,17 +78,38 @@ app.get("/", (req, res) => { }); //Registration Endpoint +//http://localhost:9000/register app.post("/register", async (req, res) => { try { const { username, email, password } = req.body; - const hashedPassword = await bcrypt.hash(password, 10); + const hashedPassword = bcrypt.hashSync(password, 10); const user = new User({ username, email, password: hashedPassword }); const savedUser = await user.save(); res.status(201).json({ id: savedUser._id }); } catch (error) { - res.status(400), json({ error: "Registartion failed" }); + res.status(400).json({ error: "Registration failed" }); } }); + +// Sign-in Endpoint +app.post("/login", async (req, res) => { + const { email, password } = req.body; + const user = await User.findOne({ email }); + + if (!user || !(await bcrypt.compare(password, user.password))) { + return res.status(401).json({ error: "Invalid credentials" }); + } + + const accessToken = generateAccessToken(user._id); + await User.findByIdAndUpdate(user._id, { accessToken }); + res.json({ accessToken }); +}); + +// Authenticated endpoint +app.get("/secrets", authenticateToken, (req, res) => { + res.json({ secret: "This is secret content" }); +}); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From 39bf8e451be1e024cde435e14682d76343576dcd Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 19:55:47 +0200 Subject: [PATCH 04/21] backend resgistration and login-done --- backend/package.json | 4 +++- backend/server.js | 46 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index e8ddc2b7f..f58d1f8b2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,9 @@ "bcryptjs": "^2.4.3", "cors": "^2.8.5", "express": "^4.17.3", + "express-async-handler": "^1.2.0", + "express-list-endpoints": "^7.1.0", "mongoose": "^8.0.0", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/backend/server.js b/backend/server.js index 8c04b7c92..c6775cd78 100644 --- a/backend/server.js +++ b/backend/server.js @@ -5,6 +5,8 @@ import dotenv from "dotenv"; import express from "express"; import jwt from "jsonwebtoken"; import mongoose from "mongoose"; +import expressListEndpoints from "express-list-endpoints"; +import asyncHandler from "express-async-handler"; dotenv.config(); @@ -74,7 +76,8 @@ app.use(express.json()); // Start defining your routes here app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const endpoints = expressListEndpoints(app); + res.json(endpoints); }); //Registration Endpoint @@ -85,9 +88,28 @@ app.post("/register", async (req, res) => { const hashedPassword = bcrypt.hashSync(password, 10); const user = new User({ username, email, password: hashedPassword }); const savedUser = await user.save(); - res.status(201).json({ id: savedUser._id }); - } catch (error) { - res.status(400).json({ error: "Registration failed" }); + + // Generate access token for the registered user + const accessToken = generateAccessToken(savedUser._id); + res.status(201).json({ id: savedUser._id, accessToken }); + } catch (err) { + console.error("Error creating user:", err); // Log the actual error for debugging + let errorMessage = "Could not create user"; + if (err.code === 11000) { + // Duplicate key error + if (err.keyPattern && err.keyPattern.username) { + errorMessage = "Username already exists"; + } else if (err.keyPattern && err.keyPattern.email) { + errorMessage = "Email already exists"; + } + } else if (err.errors) { + // Validation errors + const errorFields = Object.keys(err.errors); + if (errorFields.length > 0) { + errorMessage = err.errors[errorFields[0]].message; + } + } + res.status(400).json({ message: errorMessage, errors: err.errors }); } }); @@ -110,6 +132,22 @@ app.get("/secrets", authenticateToken, (req, res) => { res.json({ secret: "This is secret content" }); }); +app.get( + "/logged-in", + authenticateToken, + asyncHandler(async (req, res) => { + res.status(200).json({ + success: true, + response: { + message: "User is logged in", + }, + }); + }) +); + +/* const secret = crypto.randomBytes(64).toString("hex"); +console.log(secret); */ + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From 100ecae7f51cc0e480af60ee8f368f78860ea1f0 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 20:40:22 +0200 Subject: [PATCH 05/21] FE --- backend/.gitignore | 1 + frontend/src/components/AuthenticatedContent.jsx | 0 frontend/src/components/RegistrationForm.jsx | 0 frontend/src/components/SignInForm.jsx | 0 frontend/src/components/SignOutButton.jsx | 6 ++++++ 5 files changed, 7 insertions(+) create mode 100644 frontend/src/components/AuthenticatedContent.jsx create mode 100644 frontend/src/components/RegistrationForm.jsx create mode 100644 frontend/src/components/SignInForm.jsx create mode 100644 frontend/src/components/SignOutButton.jsx diff --git a/backend/.gitignore b/backend/.gitignore index 25c8fdbab..6fe20f0f3 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,3 @@ node_modules +.env package-lock.json \ No newline at end of file diff --git a/frontend/src/components/AuthenticatedContent.jsx b/frontend/src/components/AuthenticatedContent.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/components/RegistrationForm.jsx b/frontend/src/components/RegistrationForm.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/components/SignInForm.jsx b/frontend/src/components/SignInForm.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/components/SignOutButton.jsx b/frontend/src/components/SignOutButton.jsx new file mode 100644 index 000000000..0a781b4d4 --- /dev/null +++ b/frontend/src/components/SignOutButton.jsx @@ -0,0 +1,6 @@ + +export const SignOutButton = () => { + return ( +
SignOutButton
+ ) +} From 58893e8e20be672b3844fb516fa6f1a1be7232a1 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 20:47:17 +0200 Subject: [PATCH 06/21] update package.json --- backend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index f58d1f8b2..beffdcf5c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "description": "Starter project to get up and running with express quickly", "scripts": { "start": "babel-node server.js", - "dev": "nodemon server.js" + "dev": "nodemon server.js --exec babel-node" }, "author": "", "license": "ISC", @@ -22,4 +22,4 @@ "mongoose": "^8.0.0", "nodemon": "^3.0.1" } -} +} \ No newline at end of file From d5f63b1c0e376d48bca941278006bc0fc3e4e72f Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 21:07:32 +0200 Subject: [PATCH 07/21] troubleshoot --- backend/package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index beffdcf5c..69e6d95f2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,7 +11,6 @@ "license": "ISC", "dependencies": { "@babel/core": "^7.17.9", - "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", @@ -21,5 +20,8 @@ "express-list-endpoints": "^7.1.0", "mongoose": "^8.0.0", "nodemon": "^3.0.1" + }, + "devDependencies": { + "@babel/node": "^7.24.6" } -} \ No newline at end of file +} From fa5013e54bf9576197748235e8409a28161f7b98 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 21:19:59 +0200 Subject: [PATCH 08/21] fixing --- backend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/package.json b/backend/package.json index 69e6d95f2..50e31028a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,6 +15,7 @@ "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.17.3", "express-async-handler": "^1.2.0", "express-list-endpoints": "^7.1.0", From 105f75a1ddd409803ec112e31d522ea5ca09926e Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 21:33:41 +0200 Subject: [PATCH 09/21] fix --- backend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/package.json b/backend/package.json index 50e31028a..acd86c304 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,6 +19,7 @@ "express": "^4.17.3", "express-async-handler": "^1.2.0", "express-list-endpoints": "^7.1.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.0", "nodemon": "^3.0.1" }, From da935fdf762c8c72a6f661ecbf899f875cbbf15e Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 23:07:09 +0200 Subject: [PATCH 10/21] register ? sign-in --- backend/server.js | 1 + frontend/package.json | 6 +- frontend/public/vite.svg | 1 - frontend/src/App.jsx | 38 ++++++- frontend/src/components/RegistrationForm.jsx | 101 +++++++++++++++++++ frontend/src/components/SignInForm.jsx | 79 +++++++++++++++ frontend/src/index.css | 58 +++++++++++ 7 files changed, 280 insertions(+), 4 deletions(-) delete mode 100644 frontend/public/vite.svg diff --git a/backend/server.js b/backend/server.js index c6775cd78..b7e4f2ce8 100644 --- a/backend/server.js +++ b/backend/server.js @@ -152,3 +152,4 @@ console.log(secret); */ app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); +console.log("Contents of process.env:", process.env); diff --git a/frontend/package.json b/frontend/package.json index e9c95b79f..211bb0262 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,10 @@ "preview": "vite preview" }, "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "axios": "^1.7.2", + "react": "^18.3.1", + "react-dom": "^18.2.0", + "styled-component": "^2.8.0" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg deleted file mode 100644 index e7b8dfb1b..000000000 --- a/frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1091d4310..e2deac8a0 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,39 @@ +import AuthenticatedContent from "./components/AuthenticatedContent"; +import RegistrationForm from "./components/RegistrationForm"; +import SignInForm from "./components/SignInForm"; + + export const App = () => { - return
Find me in src/app.jsx!
; + return ( +
+

Authentication System

+ +

Sign in

+ + +
+ ); }; + + +/* import { BrowserRouter, Router, Switch, Route } from "react-router-dom"; +import RegistrationForm from "./components/RegistrationForm"; + +export const App = () => { + return ( +
+ + +
+

Project Auth

+ + + + + +
+
+
+
+ ); +}; */ diff --git a/frontend/src/components/RegistrationForm.jsx b/frontend/src/components/RegistrationForm.jsx index e69de29bb..619999988 100644 --- a/frontend/src/components/RegistrationForm.jsx +++ b/frontend/src/components/RegistrationForm.jsx @@ -0,0 +1,101 @@ +import { useState } from 'react'; +import axios from 'axios'; + +const RegistrationForm = () => { + // State to store form data + const [formData, setFormData] = useState({ + username: '', + email: '', + password: '' + }); + + // State to store error message + const [error, setError] = useState(''); + + const [registrationSuccess, setRegistrationSuccess] = useState(false); + + // Handle form input changes + const handleInputChange = (event) => { + const { name, value } = event.target; + setFormData({ ...formData, [name]: value }); + }; + + // Handle form submission + const handleSubmit = async (event) => { + event.preventDefault(); + setError(''); + + try { + const response = await axios.post('https://project-auth-2zcr.onrender.com/register', formData); + console.log(response.data); + if (response.status === 201) { + setRegistrationSuccess(true); + setFormData({ username: '', email: '', password: '' }); + console.log('Registration successful'); + } + } catch (error) { + console.error('Error:', error); + if (error.response && error.response.data && error.response.data.message) { + setError(error.response.data.message); + } else { + setError('Something went wrong'); + } + } + }; + + return ( +
+ {registrationSuccess && ( +
+

You have successfully registered!

+

Now you can sign in using your credentials.

+
+ )} + {error &&
{error}
} +

Register

+
+
+ + +
+
+ + +
+
+ + +
+ +
+ {error &&
{error}
} +
+ ); +}; + +export default RegistrationForm; diff --git a/frontend/src/components/SignInForm.jsx b/frontend/src/components/SignInForm.jsx index e69de29bb..8fa074469 100644 --- a/frontend/src/components/SignInForm.jsx +++ b/frontend/src/components/SignInForm.jsx @@ -0,0 +1,79 @@ +import { useState } from 'react'; +import axios from 'axios'; + +const SignInForm = () => { + // State to store form data + const [formData, setFormData] = useState({ + email: '', + password: '' + }); + + // State to store error message + const [error, setError] = useState(''); + + // Handle form input changes + const handleInputChange = (event) => { + const { name, value } = event.target; + setFormData({ ...formData, [name]: value }); + }; + + // Handle form submission + const handleSubmit = async (event) => { + event.preventDefault(); + setError(''); + + try { + // Send form data to backend API for sign-in + const response = await axios.post('/login', formData); // Assuming proxy is set up for backend + console.log(response.data); + // Sign-in successful + const accessToken = response.data.accessToken; + console.log('Sign-in successful. Access token:', accessToken); + // Reset form fields + setFormData({ email: '', password: '' }); + // Store access token in localStorage or sessionStorage + sessionStorage.setItem('accessToken', accessToken); + } catch (error) { + console.error('Error:', error); + // Sign-in failed + setError(error.response.data.error || 'Something went wrong'); + } + }; + + return ( +
+

Welcome back! Please sign in to continue.

+
+
+ + +
+
+ + +
+ +
+ {error &&
{error}
} +
+ ); +}; + +export default SignInForm; diff --git a/frontend/src/index.css b/frontend/src/index.css index 3e560a674..145584177 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -10,4 +10,62 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; +} + +.form-container { + max-width: 400px; + margin: auto; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + background-color: #fff; +} + +.heading { + margin-bottom: 20px; + text-align: center; +} + +.form { + display: flex; + flex-direction: column; +} + +.input-group { + margin-bottom: 15px; +} + +.label { + margin-bottom: 5px; + font-weight: bold; +} + +.input { + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 16px; + width: 100%; +} + +.button { + padding: 10px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + width: 100%; +} + +.button:hover { + background-color: #0056b3; +} + +.error { + margin-top: 10px; + color: red; + text-align: center; } \ No newline at end of file From 7701d9397fdf5f3f0db243d9b75442ca98498ceb Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Fri, 24 May 2024 23:10:14 +0200 Subject: [PATCH 11/21] registration msg --- frontend/src/components/RegistrationForm.jsx | 90 ++++++++++---------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/frontend/src/components/RegistrationForm.jsx b/frontend/src/components/RegistrationForm.jsx index 619999988..d9a799c05 100644 --- a/frontend/src/components/RegistrationForm.jsx +++ b/frontend/src/components/RegistrationForm.jsx @@ -45,55 +45,59 @@ const RegistrationForm = () => { return (
+ {!registrationSuccess && ( +
+ {error &&
{error}
} +

Register

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ )} {registrationSuccess && (
+

Hello, {formData.username}!

You have successfully registered!

Now you can sign in using your credentials.

)} - {error &&
{error}
} -

Register

-
-
- - -
-
- - -
-
- - -
- -
- {error &&
{error}
}
); }; From 29c4e13f0a101c9cfbbddceb02430b59dce5a11e Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 12:30:49 +0200 Subject: [PATCH 12/21] username acces --- backend/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index b7e4f2ce8..c70d47af1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -124,7 +124,7 @@ app.post("/login", async (req, res) => { const accessToken = generateAccessToken(user._id); await User.findByIdAndUpdate(user._id, { accessToken }); - res.json({ accessToken }); + res.json({ accessToken, username: user.username }); }); // Authenticated endpoint From 8da55f27877c8549124892515c88a58c771e7378 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 13:16:39 +0200 Subject: [PATCH 13/21] frontend included succesfully --- frontend/.eslintrc.cjs | 23 +-- frontend/README.md | 8 - frontend/package.json | 5 +- frontend/src/App.jsx | 51 +++---- .../src/components/AuthenticatedContent.jsx | 13 ++ frontend/src/components/RegistrationForm.jsx | 58 ++++++-- frontend/src/components/SignInForm.jsx | 140 +++++++++--------- frontend/src/index.css | 31 +++- 8 files changed, 200 insertions(+), 129 deletions(-) delete mode 100644 frontend/README.md diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 4dcb43901..bb4f312b1 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -2,19 +2,20 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], + "react/prop-types": "off", }, -} +}; diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index f768e33fc..000000000 --- a/frontend/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# React + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/frontend/package.json b/frontend/package.json index 211bb0262..18d92f4c9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,8 +12,7 @@ "dependencies": { "axios": "^1.7.2", "react": "^18.3.1", - "react-dom": "^18.2.0", - "styled-component": "^2.8.0" + "react-dom": "^18.2.0" }, "devDependencies": { "@types/react": "^18.2.15", @@ -25,4 +24,4 @@ "eslint-plugin-react-refresh": "^0.4.3", "vite": "^4.4.5" } -} +} \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index e2deac8a0..373b523a8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,39 +1,36 @@ import AuthenticatedContent from "./components/AuthenticatedContent"; import RegistrationForm from "./components/RegistrationForm"; import SignInForm from "./components/SignInForm"; +import { useState } from 'react' export const App = () => { - return ( -
-

Authentication System

- -

Sign in

- - -
- ); -}; + const [isAuthenticated, setIsAuthenticated] = useState(false) + const [username, setUsername] = useState('') + const handleSigninSuccess = (username) => { + setIsAuthenticated(true) + setUsername(username) + } -/* import { BrowserRouter, Router, Switch, Route } from "react-router-dom"; -import RegistrationForm from "./components/RegistrationForm"; + const handleSignOut = () => { + setIsAuthenticated(false) + setUsername('') + sessionStorage.removeItem('accessToken') + } -export const App = () => { return ( -
- - -
-

Project Auth

- - - - - -
-
-
+
+ {!isAuthenticated ? ( +
+

Authentication System

+ +

Sign in

+ +
+ ) : ( + + )}
); -}; */ +}; diff --git a/frontend/src/components/AuthenticatedContent.jsx b/frontend/src/components/AuthenticatedContent.jsx index e69de29bb..28bdd1a4c 100644 --- a/frontend/src/components/AuthenticatedContent.jsx +++ b/frontend/src/components/AuthenticatedContent.jsx @@ -0,0 +1,13 @@ +const AuthenticatedContent = ({ username, onSignOut }) => { + return ( +
+

πŸŽ‰ Congratulations, {username}! πŸŽ‰

+

You've successfully signed in!

+

Your profile is currently under construction, but stay tuned for more exciting features coming your way!

+

Meanwhile, sit back and relaxπŸ˜‰.

+ +
+ ); +}; + +export default AuthenticatedContent; diff --git a/frontend/src/components/RegistrationForm.jsx b/frontend/src/components/RegistrationForm.jsx index d9a799c05..c69b8d842 100644 --- a/frontend/src/components/RegistrationForm.jsx +++ b/frontend/src/components/RegistrationForm.jsx @@ -9,26 +9,47 @@ const RegistrationForm = () => { password: '' }); - // State to store error message - const [error, setError] = useState(''); - const [registrationSuccess, setRegistrationSuccess] = useState(false); + const [registeredUsername, setRegisteredUsername] = useState(''); + const [loading, setLoading] = useState(false); + + // State to store error messages + const [error, setError] = useState({ + username: '', + email: '', + password: '', + form: '' + }); // Handle form input changes const handleInputChange = (event) => { const { name, value } = event.target; setFormData({ ...formData, [name]: value }); + + // Inline validation logic + if (name === 'username' && value.length < 3) { + setError((prev) => ({ ...prev, username: 'Username must be at least 3 characters long' })); + } else if (name === 'email' && !/\S+@\S+\.\S+/.test(value)) { + setError((prev) => ({ ...prev, email: 'Email must be valid' })); + } else if (name === 'password' && value.length < 6) { + setError((prev) => ({ ...prev, password: 'Password must be at least 6 characters long' })); + } else { + setError((prev) => ({ ...prev, [name]: '' })); + } }; // Handle form submission const handleSubmit = async (event) => { event.preventDefault(); - setError(''); + setLoading(true); + + // Clear previous error messages + setError({ form: '', username: '', email: '', password: '' }); try { const response = await axios.post('https://project-auth-2zcr.onrender.com/register', formData); - console.log(response.data); if (response.status === 201) { + setRegisteredUsername(formData.username); setRegistrationSuccess(true); setFormData({ username: '', email: '', password: '' }); console.log('Registration successful'); @@ -36,10 +57,21 @@ const RegistrationForm = () => { } catch (error) { console.error('Error:', error); if (error.response && error.response.data && error.response.data.message) { - setError(error.response.data.message); + const errorMsg = error.response.data.message; + if (errorMsg.includes('Username')) { + setError((prev) => ({ ...prev, username: errorMsg })); + } else if (errorMsg.includes('Email')) { + setError((prev) => ({ ...prev, email: errorMsg })); + } else if (errorMsg.includes('Password')) { + setError((prev) => ({ ...prev, password: errorMsg })); + } else { + setError((prev) => ({ ...prev, form: 'Something went wrong' })); + } } else { - setError('Something went wrong'); + setError((prev) => ({ ...prev, form: 'Something went wrong' })); } + } finally { + setLoading(false); } }; @@ -47,7 +79,6 @@ const RegistrationForm = () => {
{!registrationSuccess && (
- {error &&
{error}
}

Register

@@ -61,24 +92,26 @@ const RegistrationForm = () => { className="input" required /> + {error.username &&
{error.username}
}
+ {error.email &&
{error.email}
}
{ minLength={6} required /> + {error.password &&
{error.password}
}
+ {loading &&
Registering...
} + {error.form &&
{error.form}
}
)} {registrationSuccess && (
-

Hello, {formData.username}!

+

Hello, {registeredUsername}!

You have successfully registered!

Now you can sign in using your credentials.

diff --git a/frontend/src/components/SignInForm.jsx b/frontend/src/components/SignInForm.jsx index 8fa074469..aabf6fdca 100644 --- a/frontend/src/components/SignInForm.jsx +++ b/frontend/src/components/SignInForm.jsx @@ -1,79 +1,83 @@ import { useState } from 'react'; import axios from 'axios'; -const SignInForm = () => { - // State to store form data - const [formData, setFormData] = useState({ - email: '', - password: '' - }); +const SignInForm = ({ onSignInSuccess }) => { + // State to store form data + const [formData, setFormData] = useState({ + email: '', + password: '' + }); - // State to store error message - const [error, setError] = useState(''); + // State to store error message + const [error, setError] = useState(''); - // Handle form input changes - const handleInputChange = (event) => { - const { name, value } = event.target; - setFormData({ ...formData, [name]: value }); - }; + // Handle form input changes + const handleInputChange = (event) => { + const { name, value } = event.target; + setFormData({ ...formData, [name]: value }); + }; - // Handle form submission - const handleSubmit = async (event) => { - event.preventDefault(); - setError(''); + // Handle form submission + const handleSubmit = async (event) => { + event.preventDefault(); + setError(''); - try { - // Send form data to backend API for sign-in - const response = await axios.post('/login', formData); // Assuming proxy is set up for backend - console.log(response.data); - // Sign-in successful - const accessToken = response.data.accessToken; - console.log('Sign-in successful. Access token:', accessToken); - // Reset form fields - setFormData({ email: '', password: '' }); - // Store access token in localStorage or sessionStorage - sessionStorage.setItem('accessToken', accessToken); - } catch (error) { - console.error('Error:', error); - // Sign-in failed - setError(error.response.data.error || 'Something went wrong'); - } - }; - return ( -
-

Welcome back! Please sign in to continue.

-
-
- - -
-
- - -
- -
- {error &&
{error}
} -
- ); + try { + // Send form data to backend API for sign-in + const response = await axios.post('https://project-auth-2zcr.onrender.com/login', formData); + if (response.status === 200) { + const accessToken = response.data.accessToken; + const username = response.data.username; + onSignInSuccess(username); // Pass the username to the parent component + /* console.log('Sign-in successful. Access token:', accessToken); */ + // Reset form fields + setFormData({ email: '', password: '' }); + // Store access token in sessionStorage + sessionStorage.setItem('accessToken', accessToken); + } + } catch (error) { + console.error('Error:', error); + // Sign-in failed + setError(error.response?.data?.error || 'Something went wrong'); + } + }; + + + return ( +
+

Please sign in to continue.

+
+
+ + +
+
+ + +
+ +
+ {error &&
{error}
} +
+ ); }; export default SignInForm; diff --git a/frontend/src/index.css b/frontend/src/index.css index 145584177..d3abec6a2 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -46,7 +46,7 @@ code { border: 1px solid #ccc; border-radius: 5px; font-size: 16px; - width: 100%; + width: calc(100% - 22px); } .button { @@ -68,4 +68,33 @@ code { margin-top: 10px; color: red; text-align: center; +} + +.loading { + margin-top: 10px; + color: blue; + text-align: center; +} + +/* Styling after signing in */ +.authenticated-content { + max-width: 600px; + margin: 20px auto; + padding: 20px; + border: 2px solid #ccc; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: #f9f9f9; + text-align: center; + font-family: "Arial", sans-serif; +} + +.authenticated-content h2 { + color: #4caf50; + font-size: 24px; +} + +.authenticated-content p { + margin: 10px 0; + font-size: 18px; } \ No newline at end of file From 47e819a0a4b459d59804ba6d679318bb6173f321 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 13:18:47 +0200 Subject: [PATCH 14/21] deploy commands adjusted --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 1bb70e3a3..66a5356d0 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,7 +2,7 @@ # how it should build the JavaScript assets to deploy from. [build] base = "frontend/" - publish = "dist" + publish = "frontend/dist" command = "npm run build" [[redirects]] From 3130ddc69aa852f2e083e72f5c01c30f00d310a2 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 13:39:13 +0200 Subject: [PATCH 15/21] deploy setting --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 66a5356d0..c867ddfde 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ # 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/" + base = "project-auth/frontend" publish = "frontend/dist" command = "npm run build" From 2d16aab065abba0927b9fd2966ab7369d27a4a04 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 13:51:12 +0200 Subject: [PATCH 16/21] build set --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index c867ddfde..844e672e2 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ # This file tells netlify where the code for this project is and # how it should build the JavaScript assets to deploy from. [build] - base = "project-auth/frontend" + base = "frontend" publish = "frontend/dist" command = "npm run build" From b998a37cd8720bc6b18b1c811eff79b92ab7c7ac Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 13:53:49 +0200 Subject: [PATCH 17/21] build --- netlify.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netlify.toml b/netlify.toml index 844e672e2..774460a96 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,8 +1,8 @@ # 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 = "frontend/dist" + base = "frontend/" + publish = "build/" command = "npm run build" [[redirects]] From c1e1f84743495fd17374021eef8ec35bdb242cba Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 14:00:15 +0200 Subject: [PATCH 18/21] build... --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 774460a96..cb4d41209 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,7 +2,7 @@ # how it should build the JavaScript assets to deploy from. [build] base = "frontend/" - publish = "build/" + publish = "dist/" command = "npm run build" [[redirects]] From 4e18635bf4a7020c5d66a28c2b3be95a71fab750 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 14:08:35 +0200 Subject: [PATCH 19/21] buil... --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b42cb7490..728936250 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "project-auth-parent", "version": "1.0.0", "scripts": { - "postinstall": "npm install --prefix backend" + "postinstall": "npm install --prefix backend", + "build": "npm run build --prefix frontend" }, "dependencies": { "bcrypt": "^5.1.1", @@ -10,4 +11,4 @@ "js": "^0.1.0", "jsonwebtoken": "^9.0.2" } -} +} \ No newline at end of file From 8819b39c451d4c69e3c7b708d00ac87707d92719 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 14:32:40 +0200 Subject: [PATCH 20/21] last try --- frontend/netlify.toml | 9 +++++++++ netlify.toml | 11 ----------- 2 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 frontend/netlify.toml delete mode 100644 netlify.toml diff --git a/frontend/netlify.toml b/frontend/netlify.toml new file mode 100644 index 000000000..bdad2d6c8 --- /dev/null +++ b/frontend/netlify.toml @@ -0,0 +1,9 @@ +[build] + base = "frontend/" + publish = "dist" + command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 \ No newline at end of file diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index cb4d41209..000000000 --- a/netlify.toml +++ /dev/null @@ -1,11 +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 = "dist/" - command = "npm run build" - -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file From e8bc6a721d0371fab342dd570d16499dade86665 Mon Sep 17 00:00:00 2001 From: Maria-source-cell Date: Sat, 25 May 2024 14:42:56 +0200 Subject: [PATCH 21/21] readme --- README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dfa05e177..88631f030 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,26 @@ # 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. +Project Auth is a web application designed to provide user authentication functionalities. The application features a frontend built using React.js and a backend developed with Node.js and Express.js. User data is stored securely in a MongoDB database, and password hashing is implemented using bcrypt for enhanced security. ## 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? +Challenges Encountered: + +Backend Setup: Setting up the backend with Node.js and Express.js required careful configuration of routes and middleware for authentication. + +Frontend Integration: Integrating the frontend with the backend API endpoints and managing authentication state in React was challenging. + +User Authentication and Security: Implementing secure authentication and password hashing with bcrypt and JWT authentication required careful implementation. + +Cross-Origin Resource Sharing (CORS): Handling CORS issues during development and configuring CORS middleware in the backend were challenges. + +Technologies Used: + +Frontend: React.js, Axios +Backend: Node.js, Express.js, MongoDB +Authentication: JSON Web Tokens (JWT), bcrypt ## 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. +BE: https://project-auth-2zcr.onrender.com +FE: https://authentication-bmm.netlify.app/