From 799bce2d0246ab28dce6a2989beca333e568ddec Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Tue, 21 May 2024 14:12:24 +0200 Subject: [PATCH 01/21] removed pull-request template and instructions-file --- instructions.md | 37 ------------------------------------- pull_request_template.md | 7 ------- 2 files changed, 44 deletions(-) delete mode 100644 instructions.md delete mode 100644 pull_request_template.md diff --git a/instructions.md b/instructions.md deleted file mode 100644 index eccc02575..000000000 --- a/instructions.md +++ /dev/null @@ -1,37 +0,0 @@ -# Instructions -Your project needs two parts; a backend API, and a React frontend. You'll need to create a `User` model using mongoose, with properties for your registered user, and to store a user's access token. - -Then, on the frontend side of things, you'll need to build up a registration form that POSTs to your API. You'll need to store the access token you get back in the browser using local storage, and then use that token when making other requests to your API. - -Once a user is logged in, you will need to have one last endpoint which returns some content which only logged-in users should be able to access. You can choose what you want this endpoint to return, and if you want to make multiple endpoints, that's fine too. It could return hard-coded data or something from the database - either is fine. Whatever you choose, it should be displayed in the frontend after you've logged in. - -To summarise, your API needs: -- Registration endpoint, to create a new user. -- Sign-in endpoint, to authenticate a returning user. -- An authenticated endpoint which only returns content if the `Authorization` header with the user's token was correct. - -Your frontend needs: -- A registration form. -- A sign-in form. -- A page to show the authenticated content from the API. -- A 'sign out' button that removes the saved access token and redirects the user to the login form. - -## Requirements -- Your API should have routes to register and login, and finally an authenticated endpoint. -- The authenticated endpoint should return a 401 or 403 (see [401 vs. 403 on SO](https://stackoverflow.com/questions/3297048/403-forbidden-vs-401-unauthorized-http-responses)) with an error message if you try to access it without an `Authentication` access token or with an invalid token. -- Your frontend should have a registration form which POSTs to the API to create a new user. -- Your passwords in the database should be encrypted with bcrypt. -- Your API should validate the user input when creating a new user, and return error messages which could be shown by the frontend (displaying the errors in a nice way in the frontend is a stretch goal - it’s fine to just show 'Something went wrong' on the frontend if you run out of time). - -## Stretch goals -So you’ve completed the requirements? Great job! Make sure you've committed and pushed a version of your project before starting on the stretch goals. Remember that the stretch goals are optional. - -### Intermediate Stretch Goals -- Store data in the database for your authenticated data routes. -- When registering, display error messages from the API next to the field which has the error. For example, if the email address is invalid, show an error message next to the email input. -- To challenge yourself, try to implement Google authentication with Firebase. [Here](https://www.freecodecamp.org/news/react-firebase-authentication-and-crud-operations/) you will find detailed tutorial which will guide you through implementation (some of the steps connected to [Material UI](https://mui.com/) components can be replaced with your custom components). - - -### Advanced Stretch Goals -- Add more routes, perhaps even a `POST` route to create new objects in your database as a logged-in user. -- Improve validations in the backend to ensure unique email addresses, or validate the email address format using a regular expression. diff --git a/pull_request_template.md b/pull_request_template.md deleted file mode 100644 index d92c89b51..000000000 --- a/pull_request_template.md +++ /dev/null @@ -1,7 +0,0 @@ -## Netlify link -Add your Netlify link here. -PS. Don't forget to add it in your readme as well. - -## Collaborators -Add your collaborators here. Write their GitHub usernames in square brackets. If there's more than one, separate them with a comma, like this: -[github-username-1, github-username-2] From b503ac28abe7a23a0c13afe08bb25cabec314457 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Tue, 21 May 2024 15:35:49 +0200 Subject: [PATCH 02/21] added registrations-endpoint, sign-in-endpoint & my-pages-endpointas an authenticated endpoint --- backend/package.json | 2 ++ backend/server.js | 78 ++++++++++++++++++++++++++++++++++++++++++-- netlify.toml | 9 +++-- 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..53bbf3933 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,8 +12,10 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.1.1", "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..d39c63dfe 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,15 +1,56 @@ +import bcrypt from "bcrypt"; import cors from "cors"; import express from "express"; +import expressListEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; +// Create user object that has access-token. Mongoose-model +// Destructure schema & model +const { Schema, model } = mongoose; + +const userSchema = new Schema({ + name: { + type: String, + unique: true, + }, + email: { + type: String, + unique: true, + }, + password: { + type: String, + required: true, + }, + accessToken: { + type: String, + default: () => bcrypt.genSaltSync(), + /* default: ()=> crypto.randomBytes(128).toString("hex") */ + }, +}); + +const User = model("User", userSchema); + +// create middleware- if everything is ok, then tell express to continue execution +const authenticateUser = async (req, res, next) => { + const user = await User.findOne({ accessToken: req.header("Authorization") }); + // if user is found based on accestoken they sent in, if found- attach the user-object to the request. Modifying the request inside the middleware + if (user) { + req.user = user; + next(); + } else { + //if not found, return an unauthorized statuscode with loggedOut-body to true + res.status(401).json({ loggedOut: true }); + } +}; + // 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 || 8030; const app = express(); // Add middlewares to enable cors and json body parsing @@ -21,6 +62,39 @@ app.get("/", (req, res) => { res.send("Hello Technigo!"); }); +app.post("/register", async (req, res) => { + try { + const { name, email, password } = req.body; + // Encrypt the password + const user = await new User({ + name, + email, + password: bcrypt.hashSync(password, 10), + }).save(); + res.status(201).json({ id: user._id, accessToken: user.accessToken }); + } catch (err) { + res + .status(400) + .json({ message: "Could not create user", errors: err.errors }); + } +}); + +// protect my-pages endpoint +app.get("/my-pages", authenticateUser); +app.get("/my-pages", (req, res) => { + res.json({ message: "This is a super secret message" }); +}); + +// Allow the user to log in, not only register +app.post("/sign-in", 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, accessToken: user.accessToken }); + } else { + res.json({ notFound: true }); + } +}); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/netlify.toml b/netlify.toml index 95443a1f3..6abaa9a64 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,6 +1,11 @@ # 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/" + base = "frontend" + publish = "dist" command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 From a70e35f2582b4bdcf043b5ba47add4d33bcf6bb5 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Tue, 21 May 2024 15:40:28 +0200 Subject: [PATCH 03/21] added expresslist-endpoints --- backend/server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index d39c63dfe..5e2e11b91 100644 --- a/backend/server.js +++ b/backend/server.js @@ -59,7 +59,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); }); app.post("/register", async (req, res) => { From dadc9bb36142eeb0fa1024a2e5f99e17e6a3014f Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Wed, 22 May 2024 10:24:01 +0200 Subject: [PATCH 04/21] added components-structure --- frontend/components/MyPages.jsx | 5 +++++ frontend/components/Registration.jsx | 7 +++++++ frontend/components/SignIn.jsx | 7 +++++++ frontend/components/SignOut.jsx | 7 +++++++ frontend/src/assets/react.svg | 1 - 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 frontend/components/MyPages.jsx create mode 100644 frontend/components/Registration.jsx create mode 100644 frontend/components/SignIn.jsx create mode 100644 frontend/components/SignOut.jsx delete mode 100644 frontend/src/assets/react.svg diff --git a/frontend/components/MyPages.jsx b/frontend/components/MyPages.jsx new file mode 100644 index 000000000..4d5ce5c4d --- /dev/null +++ b/frontend/components/MyPages.jsx @@ -0,0 +1,5 @@ +import React from "react"; + +export const MyPages = () => { + return
MyPages
; +}; diff --git a/frontend/components/Registration.jsx b/frontend/components/Registration.jsx new file mode 100644 index 000000000..fe48ef6c5 --- /dev/null +++ b/frontend/components/Registration.jsx @@ -0,0 +1,7 @@ +import React from 'react' + +export const Registration = () => { + return ( +
Registration
+ ) +} diff --git a/frontend/components/SignIn.jsx b/frontend/components/SignIn.jsx new file mode 100644 index 000000000..688a552a0 --- /dev/null +++ b/frontend/components/SignIn.jsx @@ -0,0 +1,7 @@ +import React from 'react' + +export const SignIn = () => { + return ( +
SignIn
+ ) +} diff --git a/frontend/components/SignOut.jsx b/frontend/components/SignOut.jsx new file mode 100644 index 000000000..b0eba383a --- /dev/null +++ b/frontend/components/SignOut.jsx @@ -0,0 +1,7 @@ +import React from 'react' + +export const SignOut = () => { + return ( +
SignOut
+ ) +} diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9bb..000000000 --- a/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 3e3e47e06fbdf5319cc27a3dca0caefbe5995873 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Wed, 22 May 2024 11:08:57 +0200 Subject: [PATCH 05/21] added functioning registration-form --- backend/server.js | 2 +- frontend/components/Registration.jsx | 7 --- frontend/src/App.jsx | 8 ++- frontend/{ => src}/components/MyPages.jsx | 2 - frontend/src/components/Registration.css | 44 ++++++++++++++ frontend/src/components/Registration.jsx | 73 +++++++++++++++++++++++ frontend/{ => src}/components/SignIn.jsx | 2 - frontend/{ => src}/components/SignOut.jsx | 2 - 8 files changed, 125 insertions(+), 15 deletions(-) delete mode 100644 frontend/components/Registration.jsx rename frontend/{ => src}/components/MyPages.jsx (69%) create mode 100644 frontend/src/components/Registration.css create mode 100644 frontend/src/components/Registration.jsx rename frontend/{ => src}/components/SignIn.jsx (71%) rename frontend/{ => src}/components/SignOut.jsx (72%) diff --git a/backend/server.js b/backend/server.js index 5e2e11b91..0c2d4bd15 100644 --- a/backend/server.js +++ b/backend/server.js @@ -47,7 +47,7 @@ const authenticateUser = async (req, res, next) => { } }; -// Defines the port the app will run on. Defaults to 8080, but can be overridden +// Defines the port the app will run on. Defaults to 8030, 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 || 8030; diff --git a/frontend/components/Registration.jsx b/frontend/components/Registration.jsx deleted file mode 100644 index fe48ef6c5..000000000 --- a/frontend/components/Registration.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react' - -export const Registration = () => { - return ( -
Registration
- ) -} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1091d4310..1bd3a0b62 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,9 @@ +import { Registration } from "./components/Registration"; + export const App = () => { - return
Find me in src/app.jsx!
; + return ( +
+ +
+ ); }; diff --git a/frontend/components/MyPages.jsx b/frontend/src/components/MyPages.jsx similarity index 69% rename from frontend/components/MyPages.jsx rename to frontend/src/components/MyPages.jsx index 4d5ce5c4d..908c68d95 100644 --- a/frontend/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -1,5 +1,3 @@ -import React from "react"; - export const MyPages = () => { return
MyPages
; }; diff --git a/frontend/src/components/Registration.css b/frontend/src/components/Registration.css new file mode 100644 index 000000000..cffb710ce --- /dev/null +++ b/frontend/src/components/Registration.css @@ -0,0 +1,44 @@ +.container { + background-color: white; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + max-width: 400px; + margin: 50px auto; + font-family: Arial, sans-serif; +} + +h1 { + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 5px; +} + +input { + width: 100%; + padding: 8px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 3px; +} + +button { + width: 100%; + padding: 10px; + background-color: #28a745; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; +} + +button:hover { + background-color: #218838; +} + +p { + margin-top: 20px; +} diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx new file mode 100644 index 000000000..d4e64109e --- /dev/null +++ b/frontend/src/components/Registration.jsx @@ -0,0 +1,73 @@ +import { useState } from "react"; +import "./Registration.css"; + +export const Registration = () => { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleSubmit = async (event) => { + event.preventDefault(); + + try { + const response = await fetch("https://auth-s0og.onrender.com/register", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ name, email, password }), + }); + + const result = await response.json(); + + if (response.ok) { + setMessage("Registration successful!"); + setName(""); + setEmail(""); + setPassword(""); + } else { + setMessage(result.message || "Registration failed!"); + } + } catch (error) { + setMessage("An error occurred. Please try again later."); + } + }; + + return ( +
+

Register

+
+ + setName(e.target.value)} + required + /> + + setEmail(e.target.value)} + required + /> + + setPassword(e.target.value)} + required + /> + +
+

{message}

+
+ ); +}; diff --git a/frontend/components/SignIn.jsx b/frontend/src/components/SignIn.jsx similarity index 71% rename from frontend/components/SignIn.jsx rename to frontend/src/components/SignIn.jsx index 688a552a0..c8684fe39 100644 --- a/frontend/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,5 +1,3 @@ -import React from 'react' - export const SignIn = () => { return (
SignIn
diff --git a/frontend/components/SignOut.jsx b/frontend/src/components/SignOut.jsx similarity index 72% rename from frontend/components/SignOut.jsx rename to frontend/src/components/SignOut.jsx index b0eba383a..309047209 100644 --- a/frontend/components/SignOut.jsx +++ b/frontend/src/components/SignOut.jsx @@ -1,5 +1,3 @@ -import React from 'react' - export const SignOut = () => { return (
SignOut
From 0a7502b8687463c99d0ebcae281f39d148c0d664 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Wed, 22 May 2024 12:00:13 +0200 Subject: [PATCH 06/21] added signin-form & loading-state --- frontend/src/components/Registration.jsx | 128 +++++++++++++++++------ frontend/src/components/SignIn.css | 0 frontend/src/components/SignIn.jsx | 61 ++++++++++- 3 files changed, 154 insertions(+), 35 deletions(-) create mode 100644 frontend/src/components/SignIn.css diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index d4e64109e..a40abba44 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -1,14 +1,18 @@ import { useState } from "react"; import "./Registration.css"; +import { SignIn } from "./SignIn"; export const Registration = () => { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); + const [isRegistered, setIsRegistered] = useState(false); + const [isLoading, setIsLoading] = useState(false); const handleSubmit = async (event) => { event.preventDefault(); + setIsLoading(true); try { const response = await fetch("https://auth-s0og.onrender.com/register", { @@ -26,48 +30,108 @@ export const Registration = () => { setName(""); setEmail(""); setPassword(""); + setIsRegistered(true); + setIsLoading(false); } else { setMessage(result.message || "Registration failed!"); } } catch (error) { setMessage("An error occurred. Please try again later."); + } finally { + setIsLoading(false); // Reset isLoading after form submission is completed } }; return (
-

Register

-
- - setName(e.target.value)} - required - /> - - setEmail(e.target.value)} - required - /> - - setPassword(e.target.value)} - required - /> - -
-

{message}

+ {isLoading && ( +
+

Loading..

+
+ )} + {isRegistered ? ( + + ) : ( + <> +

Register

+
+ + setName(e.target.value)} + required + /> + + setEmail(e.target.value)} + required + /> + + setPassword(e.target.value)} + required + /> + +
+

{message}

+ + )}
); }; + +//{isLoading ? ( +//
+//

Loading thoughts...

+//
+// {loading &&

Loading...

} + +// return ( +//
+//

Register

+//
+// +// setName(e.target.value)} +// required +// /> +// +// setEmail(e.target.value)} +// required +// /> +// +// setPassword(e.target.value)} +// required +// /> +// +//
+//

{message}

+// +//
+// ); +// }; diff --git a/frontend/src/components/SignIn.css b/frontend/src/components/SignIn.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index c8684fe39..b48e94f10 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,5 +1,60 @@ +import { useState } from "react"; +import "./SignIn.css"; + export const SignIn = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + + const handleSubmit = async (event) => { + event.preventDefault(); + + try { + const response = await fetch("https://auth-s0og.onrender.com/sign-in", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + + const result = await response.json(); + + if (response.ok) { + setMessage("Sign in successful!"); + // You can also save the token to localStorage or context for further authenticated requests + localStorage.setItem("accessToken", result.accessToken); + } else { + setMessage(result.message || "Sign in failed!"); + } + } catch (error) { + setMessage("An error occurred. Please try again later."); + } + }; + return ( -
SignIn
- ) -} +
+

Sign In

+
+ + setEmail(e.target.value)} + required + /> + + setPassword(e.target.value)} + required + /> + +
+

{message}

+
+ ); +}; From a9241dd228840bb3ebcdf535e4d815ad3a7ba01f Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Wed, 22 May 2024 13:55:04 +0200 Subject: [PATCH 07/21] added isRegistering-state to show register button --- frontend/src/App.jsx | 10 ++- frontend/src/components/MyPages.jsx | 3 +- frontend/src/components/Registration.jsx | 82 ++++++++++++------------ frontend/src/components/SignIn.css | 44 +++++++++++++ frontend/src/components/SignIn.jsx | 8 ++- 5 files changed, 103 insertions(+), 44 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1bd3a0b62..cac87e2f5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,9 +1,17 @@ +import { useState } from "react"; import { Registration } from "./components/Registration"; +import { SignIn } from "./components/SignIn"; export const App = () => { + // Default to show login-page + const [isRegistering, setIsRegistering] = useState(false); return (
- + {isRegistering ? ( + + ) : ( + + )}
); }; diff --git a/frontend/src/components/MyPages.jsx b/frontend/src/components/MyPages.jsx index 908c68d95..855f97adc 100644 --- a/frontend/src/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -1,3 +1,4 @@ export const MyPages = () => { - return
MyPages
; + + return (
MyPages
) }; diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index a40abba44..a95f24503 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -2,12 +2,12 @@ import { useState } from "react"; import "./Registration.css"; import { SignIn } from "./SignIn"; -export const Registration = () => { +export const Registration = ({ setIsRegistering }) => { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); - const [isRegistered, setIsRegistered] = useState(false); + // const [isRegistered, setIsRegistered] = useState(false); const [isLoading, setIsLoading] = useState(false); const handleSubmit = async (event) => { @@ -30,7 +30,7 @@ export const Registration = () => { setName(""); setEmail(""); setPassword(""); - setIsRegistered(true); + /* setIsRegistered(true); */ setIsLoading(false); } else { setMessage(result.message || "Registration failed!"); @@ -49,44 +49,44 @@ export const Registration = () => {

Loading..

)} - {isRegistered ? ( - - ) : ( - <> -

Register

-
- - setName(e.target.value)} - required - /> - - setEmail(e.target.value)} - required - /> - - setPassword(e.target.value)} - required - /> - -
-

{message}

- - )} + <> +

Register

+
+ + setName(e.target.value)} + required + /> + + setEmail(e.target.value)} + required + /> + + setPassword(e.target.value)} + required + /> + +
+

{message}

+

+ Already have an account?{" "} + +

+ ); }; diff --git a/frontend/src/components/SignIn.css b/frontend/src/components/SignIn.css index e69de29bb..cffb710ce 100644 --- a/frontend/src/components/SignIn.css +++ b/frontend/src/components/SignIn.css @@ -0,0 +1,44 @@ +.container { + background-color: white; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + max-width: 400px; + margin: 50px auto; + font-family: Arial, sans-serif; +} + +h1 { + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 5px; +} + +input { + width: 100%; + padding: 8px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 3px; +} + +button { + width: 100%; + padding: 10px; + background-color: #28a745; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; +} + +button:hover { + background-color: #218838; +} + +p { + margin-top: 20px; +} diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index b48e94f10..b6de65a05 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,7 +1,8 @@ import { useState } from "react"; + import "./SignIn.css"; -export const SignIn = () => { +export const SignIn = ({ setIsRegistering }) => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); @@ -55,6 +56,11 @@ export const SignIn = () => {

{message}

+

+ {" "} + Not a registered user?{" "} + +

); }; From b0d733fccbf56f038c66f358db6b2dffc71a5a7c Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Wed, 22 May 2024 14:18:46 +0200 Subject: [PATCH 08/21] Replaced button with a tag text and added styling. --- .anima/.gitignore | 1 + frontend/src/components/Registration.css | 1 + frontend/src/components/Registration.jsx | 12 +++++++++++- frontend/src/components/SignIn.css | 15 +-------------- frontend/src/components/SignIn.jsx | 14 ++++++++++++-- 5 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 .anima/.gitignore diff --git a/.anima/.gitignore b/.anima/.gitignore new file mode 100644 index 000000000..5e4659675 --- /dev/null +++ b/.anima/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/frontend/src/components/Registration.css b/frontend/src/components/Registration.css index cffb710ce..fba6f0caf 100644 --- a/frontend/src/components/Registration.css +++ b/frontend/src/components/Registration.css @@ -41,4 +41,5 @@ button:hover { p { margin-top: 20px; + text-align: center; } diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index a95f24503..1b843e444 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -84,8 +84,18 @@ export const Registration = ({ setIsRegistering }) => {

{message}

Already have an account?{" "} - + setIsRegistering(false)} + > + Sign in +

+ {/*

+ Already have an account?{" "} + +

*/} ); diff --git a/frontend/src/components/SignIn.css b/frontend/src/components/SignIn.css index cffb710ce..d5d00c59c 100644 --- a/frontend/src/components/SignIn.css +++ b/frontend/src/components/SignIn.css @@ -25,20 +25,7 @@ input { border-radius: 3px; } -button { - width: 100%; - padding: 10px; - background-color: #28a745; - color: white; - border: none; - border-radius: 3px; - cursor: pointer; -} - -button:hover { - background-color: #218838; -} - p { margin-top: 20px; + text-align: center; } diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index b6de65a05..37f8f8fe6 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -57,10 +57,20 @@ export const SignIn = ({ setIsRegistering }) => {

{message}

- {" "} Not a registered user?{" "} - + setIsRegistering(true)} + > + Register +

+ {/*

+ {" "} + Not a registered user?{" "} + +

*/} ); }; From 4578185aae51ed09ac848b3fbf714b351622c2ef Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Wed, 22 May 2024 14:36:44 +0200 Subject: [PATCH 09/21] Updated backend endpoint /sign-in to also include the users name in the response. --- backend/server.js | 6 +++++- frontend/src/components/MyPages.jsx | 12 +++++++++--- frontend/src/components/Registration.css | 10 ++++++++++ frontend/src/components/SignIn.css | 10 ++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/server.js b/backend/server.js index 0c2d4bd15..84f468544 100644 --- a/backend/server.js +++ b/backend/server.js @@ -90,7 +90,11 @@ app.get("/my-pages", (req, res) => { app.post("/sign-in", 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, accessToken: user.accessToken }); + res.json({ + userId: user._id, + name: user.name, + accessToken: user.accessToken, + }); } else { res.json({ notFound: true }); } diff --git a/frontend/src/components/MyPages.jsx b/frontend/src/components/MyPages.jsx index 855f97adc..b3d1e0651 100644 --- a/frontend/src/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -1,4 +1,10 @@ -export const MyPages = () => { - - return (
MyPages
) +import React from "react"; + +export const MyPages = ({ user }) => { + return ( +
+

Welcome, {user.name}

+

This is your personal page.

+
+ ); }; diff --git a/frontend/src/components/Registration.css b/frontend/src/components/Registration.css index fba6f0caf..dfbfced73 100644 --- a/frontend/src/components/Registration.css +++ b/frontend/src/components/Registration.css @@ -43,3 +43,13 @@ p { margin-top: 20px; text-align: center; } + +.login-link { + color: blue; + cursor: pointer; + text-decoration: underline; +} + +.register-link:hover { + text-decoration: none; +} diff --git a/frontend/src/components/SignIn.css b/frontend/src/components/SignIn.css index d5d00c59c..27381bdea 100644 --- a/frontend/src/components/SignIn.css +++ b/frontend/src/components/SignIn.css @@ -29,3 +29,13 @@ p { margin-top: 20px; text-align: center; } + +.register-link { + color: blue; + cursor: pointer; + text-decoration: underline; +} + +.register-link:hover { + text-decoration: none; +} From b7f52821b787619d2780aa556d47296485f829af Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Wed, 22 May 2024 14:48:38 +0200 Subject: [PATCH 10/21] Added a user state to handle user information and showing it on MyPage on sign in. --- frontend/src/App.jsx | 12 ++++++++---- frontend/src/components/SignIn.jsx | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index cac87e2f5..988abd352 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,16 +1,20 @@ import { useState } from "react"; import { Registration } from "./components/Registration"; import { SignIn } from "./components/SignIn"; +import { MyPages } from "./components/MyPages"; export const App = () => { - // Default to show login-page const [isRegistering, setIsRegistering] = useState(false); + const [user, setUser] = useState(null); + return ( -
- {isRegistering ? ( +
+ {user ? ( + + ) : isRegistering ? ( ) : ( - + )}
); diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index 37f8f8fe6..e2096c4bf 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,8 +1,7 @@ import { useState } from "react"; - import "./SignIn.css"; -export const SignIn = ({ setIsRegistering }) => { +export const SignIn = ({ setIsRegistering, setUser }) => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); @@ -25,6 +24,7 @@ export const SignIn = ({ setIsRegistering }) => { setMessage("Sign in successful!"); // You can also save the token to localStorage or context for further authenticated requests localStorage.setItem("accessToken", result.accessToken); + setUser({ id: result.iserID, name: result.name }); } else { setMessage(result.message || "Sign in failed!"); } From ad6ecc2b11c1d5e89e0699863d7dfd408743ef02 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Wed, 22 May 2024 16:18:03 +0200 Subject: [PATCH 11/21] implemented the authenticated content page --- frontend/src/components/MyPages.jsx | 40 +++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/MyPages.jsx b/frontend/src/components/MyPages.jsx index b3d1e0651..271bc551d 100644 --- a/frontend/src/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -1,10 +1,46 @@ -import React from "react"; +import { useEffect, useState } from "react"; export const MyPages = ({ user }) => { + const [message, setMessage] = useState(""); + const [error, setError] = useState(""); + + useEffect(() => { + const fetchMyPages = async () => { + const accessToken = localStorage.getItem("accessToken"); + try { + const response = await fetch( + "https://auth-s0og.onrender.com/my-pages", + { + headers: { + Authorization: accessToken, + }, + } + ); + const data = await response.json(); + if (response.ok) { + setMessage(data.message); + } else { + setError("Failed to fetch data. Please log in again"); + } + } catch (error) { + setError("An error occurred. Please try again later"); + } + }; + fetchMyPages(); + }, []); + + { + error && ( +
+

{error}

+
+ ); + } + return (

Welcome, {user.name}

-

This is your personal page.

+

{message}

); }; From 0a44d1528b28d0a06a2a5f3bb4e04e6e308bfd1e Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Wed, 22 May 2024 18:04:27 +0200 Subject: [PATCH 12/21] Added loading state to sign in and register form. --- frontend/src/components/Registration.jsx | 10 +++++----- frontend/src/components/SignIn.jsx | 13 ++++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index 1b843e444..e1d13ab76 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -44,11 +44,6 @@ export const Registration = ({ setIsRegistering }) => { return (
- {isLoading && ( -
-

Loading..

-
- )} <>

Register

@@ -81,6 +76,11 @@ export const Registration = ({ setIsRegistering }) => { />
+ {isLoading && ( +
+

Registering..

+
+ )}

{message}

Already have an account?{" "} diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index e2096c4bf..402977d21 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -6,8 +6,11 @@ export const SignIn = ({ setIsRegistering, setUser }) => { const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const handleSubmit = async (event) => { event.preventDefault(); + setIsLoading(true); try { const response = await fetch("https://auth-s0og.onrender.com/sign-in", { @@ -24,12 +27,15 @@ export const SignIn = ({ setIsRegistering, setUser }) => { setMessage("Sign in successful!"); // You can also save the token to localStorage or context for further authenticated requests localStorage.setItem("accessToken", result.accessToken); - setUser({ id: result.iserID, name: result.name }); + setUser({ id: result.userId, name: result.name }); + setIsLoading(false); } else { setMessage(result.message || "Sign in failed!"); } } catch (error) { setMessage("An error occurred. Please try again later."); + } finally { + setIsLoading(false); // Reset isLoading after form submission is completed } }; @@ -55,6 +61,11 @@ export const SignIn = ({ setIsRegistering, setUser }) => { /> + {isLoading && ( +

+

Signing in..

+
+ )}

{message}

Not a registered user?{" "} From 230bd99bb00a9c73ff09fc5ddbfed5b4f74fbcaa Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Wed, 22 May 2024 18:28:14 +0200 Subject: [PATCH 13/21] Added signout button and sign out function. --- frontend/src/App.jsx | 2 +- frontend/src/components/MyPages.jsx | 28 +++++++++++++++++++++++++--- frontend/src/components/SignOut.jsx | 13 ++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 988abd352..142695925 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -10,7 +10,7 @@ export const App = () => { return (

{user ? ( - + ) : isRegistering ? ( ) : ( diff --git a/frontend/src/components/MyPages.jsx b/frontend/src/components/MyPages.jsx index 271bc551d..73ae24dd4 100644 --- a/frontend/src/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -1,12 +1,17 @@ import { useEffect, useState } from "react"; +import { SignOut } from "./SignOut"; -export const MyPages = ({ user }) => { +export const MyPages = ({ user, setUser }) => { const [message, setMessage] = useState(""); const [error, setError] = useState(""); useEffect(() => { const fetchMyPages = async () => { const accessToken = localStorage.getItem("accessToken"); + if (!accessToken) { + setError("No access token found. Please log in again."); + return; + } try { const response = await fetch( "https://auth-s0og.onrender.com/my-pages", @@ -29,18 +34,35 @@ export const MyPages = ({ user }) => { fetchMyPages(); }, []); - { - error && ( + if (error) { + return (

{error}

); } + if (!message) { + return ( +
+

Loading...

+
+ ); + } + + // { + // error && ( + //
+ //

{error}

+ //
+ // ); + // } + return (

Welcome, {user.name}

{message}

+
); }; diff --git a/frontend/src/components/SignOut.jsx b/frontend/src/components/SignOut.jsx index 309047209..e39796ce3 100644 --- a/frontend/src/components/SignOut.jsx +++ b/frontend/src/components/SignOut.jsx @@ -1,5 +1,8 @@ -export const SignOut = () => { - return ( -
SignOut
- ) -} +export const SignOut = ({ setUser }) => { + const handleSignOut = () => { + localStorage.removeItem("accessToken"); + setUser(null); // Reset the user state to null + }; + + return ; +}; From b19ab74212df73734f0ee634c63fb519eb2d9ccc Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Thu, 23 May 2024 14:37:00 +0200 Subject: [PATCH 14/21] fixed some things with localstorage, can now refresh without being logged out --- frontend/src/App.jsx | 16 ++++++++++++++-- frontend/src/components/SignIn.jsx | 3 +++ frontend/src/components/SignOut.jsx | 2 ++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 142695925..79796e2af 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,12 +1,24 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; + +import { MyPages } from "./components/MyPages"; import { Registration } from "./components/Registration"; import { SignIn } from "./components/SignIn"; -import { MyPages } from "./components/MyPages"; export const App = () => { const [isRegistering, setIsRegistering] = useState(false); const [user, setUser] = useState(null); + useEffect(() => { + // Check if user data is available in localStorage on component mount + const accessToken = localStorage.getItem("accessToken"); + const userName = localStorage.getItem("userName"); + const userId = localStorage.getItem("userId"); + + if (accessToken && userName && userId) { + setUser({ id: userId, name: userName }); + } + }, []); + return (
{user ? ( diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index 402977d21..6e18c42d7 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,4 +1,5 @@ import { useState } from "react"; + import "./SignIn.css"; export const SignIn = ({ setIsRegistering, setUser }) => { @@ -27,6 +28,8 @@ export const SignIn = ({ setIsRegistering, setUser }) => { setMessage("Sign in successful!"); // You can also save the token to localStorage or context for further authenticated requests localStorage.setItem("accessToken", result.accessToken); + localStorage.setItem("userId", result.userId); + localStorage.setItem("userName", result.name); setUser({ id: result.userId, name: result.name }); setIsLoading(false); } else { diff --git a/frontend/src/components/SignOut.jsx b/frontend/src/components/SignOut.jsx index e39796ce3..f9e6f30b6 100644 --- a/frontend/src/components/SignOut.jsx +++ b/frontend/src/components/SignOut.jsx @@ -1,6 +1,8 @@ export const SignOut = ({ setUser }) => { const handleSignOut = () => { localStorage.removeItem("accessToken"); + localStorage.removeItem("userId"); + localStorage.removeItem("userName"); setUser(null); // Reset the user state to null }; From c51514d3ff58df0fcc4b646331ce07cb1e48228e Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Thu, 23 May 2024 16:25:36 +0200 Subject: [PATCH 15/21] added validation rules in the model and handled logic for password-requirements, also som error-messages --- backend/server.js | 162 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 23 deletions(-) diff --git a/backend/server.js b/backend/server.js index 84f468544..c26dee0a1 100644 --- a/backend/server.js +++ b/backend/server.js @@ -16,37 +16,84 @@ const userSchema = new Schema({ name: { type: String, unique: true, + required: [true, "Name is required"], }, email: { type: String, unique: true, + required: [true, "Email is required"], + match: [/.+\@.+\..+/, "Please enter a valid email address"], }, password: { type: String, - required: true, + required: [true, "Password is required"], + minlength: [8, "Password must be at least 8 characters long"], + // validate: { + // validator: function (value) { + // return ( + // value && + // value.length >= 12 && + // /[A-Z]/.test(value) && // At least one uppercase letter + // /[a-z]/.test(value) && // At least one lowercase letter + // /[0-9]/.test(value) && // At least one number + // /[!@#$%^&*(),.?":{}|<>]/.test(value) // At least one special character + // ); + // }, + // message: + // "Password must be at least 12 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character", + // }, + // minlength: [3, "Password cannot be empty"], + // validate: { + // validator: function (value) { + // return value && value.trim().length > 0; + // }, + // message: "Password cannot be empty", + // }, }, accessToken: { type: String, default: () => bcrypt.genSaltSync(), - /* default: ()=> crypto.randomBytes(128).toString("hex") */ }, }); const User = model("User", userSchema); -// create middleware- if everything is ok, then tell express to continue execution const authenticateUser = async (req, res, next) => { - const user = await User.findOne({ accessToken: req.header("Authorization") }); - // if user is found based on accestoken they sent in, if found- attach the user-object to the request. Modifying the request inside the middleware - if (user) { - req.user = user; - next(); - } else { - //if not found, return an unauthorized statuscode with loggedOut-body to true - res.status(401).json({ loggedOut: true }); + try { + const user = await User.findOne({ + accessToken: req.header("Authorization"), + }); + if (user) { + req.user = user; + next(); + } else { + res.status(401).json({ + message: "Authentication missing or invalid.", + loggedOut: true, + }); + } + } catch (err) { + res + .status(500) + .json({ message: "Internal server error", error: err.message }); } }; +// // create middleware- if everything is ok, then tell express to continue execution +// const authenticateUser = async (req, res, next) => { +// const user = await User.findOne({ accessToken: req.header("Authorization") }); +// // if user is found based on accestoken they sent in, if found- attach the user-object to the request. Modifying the request inside the middleware +// if (user) { +// req.user = user; +// next(); +// } else { +// //if not found, return an unauthorized statuscode with loggedOut-body to true +// res.status(401).json({ +// message: ""Authentication missing och invalid." +// loggedOut: true }); +// } +// }; + // Defines the port the app will run on. Defaults to 8030, but can be overridden // when starting the server. Example command to overwrite PORT env variable value: // PORT=9000 npm start @@ -63,15 +110,55 @@ app.get("/", (req, res) => { res.json(endpoints); }); +// app.post("/register", async (req, res) => { +// try { +// const { name, email, password } = req.body; +// if (!name || !email || !password) { +// res +// .status(400) +// .json({ message: "Name, email, and password are required" }); +// return; +// } + +// const user = await new User({ +// name, +// email, +// password: bcrypt.hashSync(password, 10), +// }).save(); +// res.status(201).json({ id: user._id, accessToken: user.accessToken }); +// } catch (err) { +// if (err.code === 11000) { +// res.status(400).json({ message: "Email or name already exists" }); +// } else { +// res +// .status(400) +// .json({ message: "Could not create user", errors: err.errors }); +// } +// } +// }); + app.post("/register", async (req, res) => { try { const { name, email, password } = req.body; + + // check if password is empty + if (!password) { + return res.status(400).json({ message: "Password is required" }); + } + + // Check if password meets minimum length req + if (password.length < 8) { + return res.status(400).json({ + message: "Password has to be at least 8 characters long", + }); + } // Encrypt the password const user = await new User({ name, email, password: bcrypt.hashSync(password, 10), }).save(); + res.status(201).json({ id: user._id, accessToken: user.accessToken }); } catch (err) { res @@ -81,25 +168,54 @@ app.post("/register", async (req, res) => { }); // protect my-pages endpoint -app.get("/my-pages", authenticateUser); -app.get("/my-pages", (req, res) => { - res.json({ message: "This is a super secret message" }); +app.get("/my-pages", authenticateUser, (req, res) => { + res.json({ message: "This is your personal page" }); }); +// app.get("/my-pages", authenticateUser); +// app.get("/my-pages", (req, res) => { +// res.json({ message: "This is a super secret message" }); +// }); // Allow the user to log in, not only register + app.post("/sign-in", 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, - accessToken: user.accessToken, - }); - } else { - res.json({ notFound: true }); + try { + const { email, password } = req.body; + if (!email || !password) { + res.status(400).json({ message: "Email and password are required" }); + return; + } + + const user = await User.findOne({ email }); + if (user && bcrypt.compareSync(password, user.password)) { + res.json({ + userId: user._id, + name: user.name, + accessToken: user.accessToken, + }); + } else { + res.status(401).json({ message: "Invalid email or password" }); + } + } catch (err) { + res + .status(500) + .json({ message: "Internal server error", error: err.message }); } }); +// app.post("/sign-in", 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, +// accessToken: user.accessToken, +// }); +// } else { +// res.json({ notFound: true }); +// } +// }); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); From f2b85faf6fce41dec4e515229ea877d54dab456e Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Fri, 24 May 2024 10:47:00 +0200 Subject: [PATCH 16/21] added netlify & render links to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dfa05e177..69b1af7eb 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,5 @@ Describe how you approached to problem, and what tools and techniques you used t ## 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. +[Frontend](https://authentication-service.netlify.app/) +[Backend](https://auth-s0og.onrender.com) From 16a0415b7abcdb831c09afe3b62c01cccf83ed9e Mon Sep 17 00:00:00 2001 From: Frida Svensson Date: Fri, 24 May 2024 11:22:56 +0200 Subject: [PATCH 17/21] Show loader animation when signing in and registering --- frontend/src/components/Loader.css | 17 +++++++++++++++++ frontend/src/components/Loader.jsx | 5 +++++ frontend/src/components/Registration.css | 6 ++++++ frontend/src/components/Registration.jsx | 3 ++- frontend/src/components/SignIn.jsx | 3 ++- 5 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/Loader.css create mode 100644 frontend/src/components/Loader.jsx diff --git a/frontend/src/components/Loader.css b/frontend/src/components/Loader.css new file mode 100644 index 000000000..e729789fe --- /dev/null +++ b/frontend/src/components/Loader.css @@ -0,0 +1,17 @@ +.loader { + border: 2px solid #f3f3f3; + border-top: 2px solid #000000; + border-radius: 50%; + width: 16px; + height: 16px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/frontend/src/components/Loader.jsx b/frontend/src/components/Loader.jsx new file mode 100644 index 000000000..c2d7f5b66 --- /dev/null +++ b/frontend/src/components/Loader.jsx @@ -0,0 +1,5 @@ +import "./Loader.css"; + +export const Loader = () => { + return
; +}; diff --git a/frontend/src/components/Registration.css b/frontend/src/components/Registration.css index dfbfced73..f6c554eb2 100644 --- a/frontend/src/components/Registration.css +++ b/frontend/src/components/Registration.css @@ -53,3 +53,9 @@ p { .register-link:hover { text-decoration: none; } + +.loading-container { + display: flex; + justify-content: center; + padding: 20px; +} diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index e1d13ab76..54cd8a952 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -1,6 +1,7 @@ import { useState } from "react"; import "./Registration.css"; import { SignIn } from "./SignIn"; +import { Loader } from "./Loader"; export const Registration = ({ setIsRegistering }) => { const [name, setName] = useState(""); @@ -78,7 +79,7 @@ export const Registration = ({ setIsRegistering }) => { {isLoading && (
-

Registering..

+
)}

{message}

diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index 6e18c42d7..6f1821aaf 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,6 +1,7 @@ import { useState } from "react"; import "./SignIn.css"; +import { Loader } from "./Loader"; export const SignIn = ({ setIsRegistering, setUser }) => { const [email, setEmail] = useState(""); @@ -66,7 +67,7 @@ export const SignIn = ({ setIsRegistering, setUser }) => { {isLoading && (
-

Signing in..

+
)}

{message}

From c22a34a9065a2f10d6936027b887478bfbfbd299 Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Fri, 24 May 2024 11:30:40 +0200 Subject: [PATCH 18/21] fixed so that after a user registers, it comes to the signin-page automatically after 2 seconds --- frontend/src/components/Registration.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index e1d13ab76..5a53a5021 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -32,6 +32,11 @@ export const Registration = ({ setIsRegistering }) => { setPassword(""); /* setIsRegistered(true); */ setIsLoading(false); + + // Redirect to the sign-in form after successful registration + setTimeout(() => { + setIsRegistering(false); + }, 2000); // Redirect after 2 seconds } else { setMessage(result.message || "Registration failed!"); } From 9836c786090ab5aeb7ee5f4989443bd87582c364 Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Fri, 24 May 2024 11:46:19 +0200 Subject: [PATCH 19/21] Updated Readme. --- frontend/README.md | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 frontend/README.md 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 From e80db818e6c881fe33c5727a83447563e9060fb2 Mon Sep 17 00:00:00 2001 From: Johanna Billingskog Nyberg Date: Fri, 24 May 2024 12:08:18 +0200 Subject: [PATCH 20/21] Added readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 69b1af7eb..f83173911 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # 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. +This project, completed as part of the Technigo bootcamp, involves developing a full-stack authentication system with a backend API and a React frontend. The project includes user registration and login functionalities, token-based authentication, and protected routes that require valid authentication tokens for access. ## 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? +The goal of this project was to create a secure user authentication system. The system allows users to register and log in, storing their credentials securely. Once logged in, users can access protected content that is only available to authenticated users. The primary challenge was ensuring the security of user data and tokens while providing a seamless user experience. + +To create a secure user authentication system with a backend API and a React frontend, we planned the project by outlining the essential components and their interactions. The approach involved building a Node.js and Express backend with MongoDB for data storage, and bcrypt for password hashing. The frontend was developed using React, incorporating forms for user registration and login, with authenticated routes to manage secure content access. If given more time, we would enhance the validation and security features, add more user functionalities, and improve the user experience with better error handling. ## View it live From 5bc32f8223410e2a49c199d889ee61837a4bd85b Mon Sep 17 00:00:00 2001 From: Erica Mechler Date: Mon, 27 May 2024 09:49:39 +0200 Subject: [PATCH 21/21] removed comments etc to clean up --- backend/server.js | 80 ------------------------ frontend/src/components/MyPages.jsx | 8 --- frontend/src/components/Registration.jsx | 52 --------------- frontend/src/components/SignIn.jsx | 5 -- 4 files changed, 145 deletions(-) diff --git a/backend/server.js b/backend/server.js index c26dee0a1..ea9cf14e9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -28,27 +28,6 @@ const userSchema = new Schema({ type: String, required: [true, "Password is required"], minlength: [8, "Password must be at least 8 characters long"], - // validate: { - // validator: function (value) { - // return ( - // value && - // value.length >= 12 && - // /[A-Z]/.test(value) && // At least one uppercase letter - // /[a-z]/.test(value) && // At least one lowercase letter - // /[0-9]/.test(value) && // At least one number - // /[!@#$%^&*(),.?":{}|<>]/.test(value) // At least one special character - // ); - // }, - // message: - // "Password must be at least 12 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character", - // }, - // minlength: [3, "Password cannot be empty"], - // validate: { - // validator: function (value) { - // return value && value.trim().length > 0; - // }, - // message: "Password cannot be empty", - // }, }, accessToken: { type: String, @@ -79,21 +58,6 @@ const authenticateUser = async (req, res, next) => { } }; -// // create middleware- if everything is ok, then tell express to continue execution -// const authenticateUser = async (req, res, next) => { -// const user = await User.findOne({ accessToken: req.header("Authorization") }); -// // if user is found based on accestoken they sent in, if found- attach the user-object to the request. Modifying the request inside the middleware -// if (user) { -// req.user = user; -// next(); -// } else { -// //if not found, return an unauthorized statuscode with loggedOut-body to true -// res.status(401).json({ -// message: ""Authentication missing och invalid." -// loggedOut: true }); -// } -// }; - // Defines the port the app will run on. Defaults to 8030, but can be overridden // when starting the server. Example command to overwrite PORT env variable value: // PORT=9000 npm start @@ -110,33 +74,6 @@ app.get("/", (req, res) => { res.json(endpoints); }); -// app.post("/register", async (req, res) => { -// try { -// const { name, email, password } = req.body; -// if (!name || !email || !password) { -// res -// .status(400) -// .json({ message: "Name, email, and password are required" }); -// return; -// } - -// const user = await new User({ -// name, -// email, -// password: bcrypt.hashSync(password, 10), -// }).save(); -// res.status(201).json({ id: user._id, accessToken: user.accessToken }); -// } catch (err) { -// if (err.code === 11000) { -// res.status(400).json({ message: "Email or name already exists" }); -// } else { -// res -// .status(400) -// .json({ message: "Could not create user", errors: err.errors }); -// } -// } -// }); - app.post("/register", async (req, res) => { try { const { name, email, password } = req.body; @@ -171,10 +108,6 @@ app.post("/register", async (req, res) => { app.get("/my-pages", authenticateUser, (req, res) => { res.json({ message: "This is your personal page" }); }); -// app.get("/my-pages", authenticateUser); -// app.get("/my-pages", (req, res) => { -// res.json({ message: "This is a super secret message" }); -// }); // Allow the user to log in, not only register @@ -203,19 +136,6 @@ app.post("/sign-in", async (req, res) => { } }); -// app.post("/sign-in", 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, -// accessToken: user.accessToken, -// }); -// } else { -// res.json({ notFound: true }); -// } -// }); - // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/frontend/src/components/MyPages.jsx b/frontend/src/components/MyPages.jsx index 73ae24dd4..a94687ace 100644 --- a/frontend/src/components/MyPages.jsx +++ b/frontend/src/components/MyPages.jsx @@ -50,14 +50,6 @@ export const MyPages = ({ user, setUser }) => { ); } - // { - // error && ( - //
- //

{error}

- //
- // ); - // } - return (

Welcome, {user.name}

diff --git a/frontend/src/components/Registration.jsx b/frontend/src/components/Registration.jsx index 5aa8a4330..89c7de6e3 100644 --- a/frontend/src/components/Registration.jsx +++ b/frontend/src/components/Registration.jsx @@ -1,6 +1,5 @@ import { useState } from "react"; import "./Registration.css"; -import { SignIn } from "./SignIn"; import { Loader } from "./Loader"; export const Registration = ({ setIsRegistering }) => { @@ -8,7 +7,6 @@ export const Registration = ({ setIsRegistering }) => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); - // const [isRegistered, setIsRegistered] = useState(false); const [isLoading, setIsLoading] = useState(false); const handleSubmit = async (event) => { @@ -31,7 +29,6 @@ export const Registration = ({ setIsRegistering }) => { setName(""); setEmail(""); setPassword(""); - /* setIsRegistered(true); */ setIsLoading(false); // Redirect to the sign-in form after successful registration @@ -98,56 +95,7 @@ export const Registration = ({ setIsRegistering }) => { Sign in

- {/*

- Already have an account?{" "} - -

*/}
); }; - -//{isLoading ? ( -//
-//

Loading thoughts...

-//
-// {loading &&

Loading...

} - -// return ( -//
-//

Register

-//
-// -// setName(e.target.value)} -// required -// /> -// -// setEmail(e.target.value)} -// required -// /> -// -// setPassword(e.target.value)} -// required -// /> -// -//
-//

{message}

-// -//
-// ); -// }; diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/SignIn.jsx index 6f1821aaf..64d48d60c 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -81,11 +81,6 @@ export const SignIn = ({ setIsRegistering, setUser }) => { Register

- {/*

- {" "} - Not a registered user?{" "} - -

*/}
); };