Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Week 16: Authentication - Etna and Alma #332

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5cfae3f
starting up week project; manage to create a new user
Caracal23 May 22, 2024
1892dca
completed backend part
Caracal23 May 22, 2024
e0d7677
components creation in frontend
Caracal23 May 22, 2024
6166f44
started with registration form in frontend
almaherris May 22, 2024
ec48849
login form up and running
Caracal23 May 22, 2024
c139420
logout button created
Caracal23 May 22, 2024
392e98e
set up user page
almaherris May 22, 2024
cacccdd
Merge pull request #1 from almaherris/alma-branch
almaherris May 22, 2024
cc10038
added landing page with path /
almaherris May 22, 2024
4e428be
buttons styling
almaherris May 22, 2024
1dc4d35
Merge pull request #2 from almaherris/alma-branch
almaherris May 22, 2024
5238045
Merge branch 'master' into project-auth-etna
Caracal23 May 22, 2024
a90be78
Merge pull request #3 from almaherris/project-auth-etna
Caracal23 May 22, 2024
3c7bb48
css on login form and button
Caracal23 May 22, 2024
bfa9f61
added links that disappeared in merge
almaherris May 22, 2024
1fc154f
added 403 error to backend
almaherris May 22, 2024
51f691b
added global context for authorization
almaherris May 22, 2024
4d733c2
fixed errors in authorization and made a to do list
almaherris May 22, 2024
e9a003e
error handling on the backend; registration form adjustments
Caracal23 May 23, 2024
1b7a759
Adding alert message and style it
Caracal23 May 23, 2024
ad512de
added logout functionality and unauthorized nimation
almaherris May 23, 2024
c3df09a
updated animation in unauthorized page
almaherris May 23, 2024
a117242
unauthorized message styling
almaherris May 23, 2024
7e88535
fixed bug that caused 409 error for every new user
almaherris May 23, 2024
63552f0
replaced localhost urls with deployed
almaherris May 23, 2024
13726f7
deleted toml
almaherris May 23, 2024
72947c4
added netlify.toml
almaherris May 23, 2024
e356e51
removed toml file again
almaherris May 23, 2024
9fa3782
update readme
almaherris May 23, 2024
a7db3ec
added netlify toml for redirects
almaherris May 23, 2024
46f5ea8
update todo list in readme
almaherris May 23, 2024
10388c7
Adding case insensitive in the backend; completing the README
Caracal23 May 24, 2024
4a062b9
adding a loading state and style it
Caracal23 May 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# 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.
In this week project we needed to create our backend with the specificatios of authenticate and user creation as well as handle passwords.
Once the backend was set up, we needed to link it to an user friendly frontend.

## 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?
On this project the technologies that we used were Mongoose and MongoDB for the backend and for the fronted we used React Hooks, Context and Navigate.
As a problem, we faced a bit of confussion on how our end-points would look in the frontend.

## 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.
Backend: https://bubblegum-auth.onrender.com/
Frontend: https://pink-authorization.netlify.app/
2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
104 changes: 102 additions & 2 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import bcrypt from "bcrypt";
import expressListEndpoints from "express-list-endpoints";

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
mongoose.connect(mongoUrl);
mongoose.Promise = Promise;

const User = mongoose.model("User", {
name: {
type: String,
unique: true,
},
email: {
type: String,
unique: true,
},
password: {
type: String,
required: true,
},
accessToken: {
type: String,
default: () => bcrypt.genSaltSync(),
},
});

// 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
Expand All @@ -16,9 +37,88 @@ const app = express();
app.use(cors());
app.use(express.json());

// Start defining your routes here
const authenticateUser = async (req, res, next) => {
const token = req.header("Authorization");

if (!token) {
return res.status(401).json({ message: "Access token is missing" });
}

const user = await User.findOne({ accessToken: token });

if (!user) {
res.status(403).json({ message: "Invalid access token" });
}

req.user = user;
next();
};

// List routes here
app.get("/", (req, res) => {
res.send("Hello Technigo!");
const endpoints = expressListEndpoints(app);
res.json(endpoints);
});

// Create user endpoint
app.post("/users", async (req, res) => {
const salt = bcrypt.genSaltSync(10);

try {
const { name, email, password } = req.body;

if (name === "" || email === "" || password === "") {
res.status(400).json({ message: "All fields are required" });
}
Comment on lines +70 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it DRY, I like it 😏


// Check if the user already exists
const existingUser = await User.findOne({ email: email.toLowerCase() });

if (existingUser) {
return res.status(409).json({ message: "User already exists" });
}

// Create new user
const user = new User({
name,
email,
password: bcrypt.hashSync(password, salt),
});

await user.save();

return res
.status(201)
.json({ id: user._id, accessToken: user.accessToken });
} catch (error) {
res.status(400).json({
response: error.message,
success: false,
message: "Could not create user",
errors: error.errors,
});
}
});

// Endpoint once user is signed in
app.get("/user-page", authenticateUser, (req, res) => {
res.json({ message: "You are logged in!", user: req.user });
});

// Sign-in endpoint
app.post("/sessions", async (req, res) => {
const { email, password } = req.body;

const user = await User.findOne({ email: email.toLowerCase() });
if (user && bcrypt.compareSync(password, user.password)) {
res.json({ userId: user._id, accessToken: user.accessToken });
} else if (user && !bcrypt.compareSync(password, user.password)) {
// Wrong password
res.status(400).json({});
} else {
// User does not exist
res.status(404).json({});
}
});

// Start the server
Expand Down
8 changes: 0 additions & 8 deletions frontend/README.md

This file was deleted.

13 changes: 10 additions & 3 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Encode+Sans+Expanded:wght@100;200;300;400;500;600;700;800;900&display=swap"
rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Gamja+Flower&display=swap" rel="stylesheet">

<title>Authentication Project</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 4 additions & 0 deletions frontend/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
6 changes: 5 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
"preview": "vite preview"
},
"dependencies": {
"lottie-react": "^2.4.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-icons": "^5.2.1",
"react-router-dom": "^6.23.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
Expand Down
Binary file added frontend/public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion frontend/public/vite.svg

This file was deleted.

51 changes: 51 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
* {
box-sizing: border-box;
margin: 0;
}

a {
text-decoration: none;
color: inherit;
}

body {
background: rgb(252, 156, 210);
background: linear-gradient(
157deg,
rgba(252, 156, 210, 1) 16%,
rgba(254, 195, 228, 1) 49%,
rgba(241, 209, 227, 1) 78%
);
font-family: "Encode Sans Expanded", sans-serif;
}

button {
font-family: "Encode Sans Expanded", sans-serif;
margin-top: 20px;
height: 30px;
border-radius: 40px;
border: none;
background-color: #d56e90;
color: white;
font-weight: 500;
font-size: 14px;
cursor: pointer;
width: 100px;
}

.full-width {
width: 100%;
}

/*Common styling for back-links*/
.back-link {
color: #d56e90;
cursor: pointer;
display: flex;
align-items: center;
font-size: 14px;
}

.back-link:hover {
text-decoration: underline;
}
22 changes: 20 additions & 2 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
import { BrowserRouter, Routes, Route } from "react-router-dom"
import { RegistrationForm } from "./components/RegistrationForm"
import { LoginForm } from "./components/LoginForm"
import { UserPage } from "./components/UserPage"
import { WelcomePage } from "./components/WelcomePage"
import "./App.css"

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
};
return (
<div className="app-container">
<BrowserRouter>
<Routes>
<Route path="/" element={<WelcomePage />} />
<Route path="/registration" element={<RegistrationForm />} />
<Route path="/login" element={<LoginForm />} />
<Route path="/user-page" element={<UserPage />} />
</Routes>
</BrowserRouter>
</div>
)
}
1 change: 1 addition & 0 deletions frontend/src/assets/lottie-unauth.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"v":"5.8.0","fr":60,"ip":0,"op":120,"w":1000,"h":1000,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[500,490,0],"to":[0,1.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[500,510,0],"to":[0,0,0],"ti":[0,1.667,0]},{"t":120,"s":[500,490,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[200,200,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"face","parent":1,"sr":1,"ks":{"o":{"a":0,"k":50,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.748,"y":0},"t":30,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.352,"y":1},"o":{"x":0.178,"y":0},"t":45,"s":[8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.491,"y":1},"o":{"x":0.077,"y":0},"t":60,"s":[-8,0,0],"to":[0,0,0],"ti":[-1.333,0,0]},{"i":{"x":0.491,"y":1},"o":{"x":0.333,"y":0},"t":73,"s":[3,0,0],"to":[1.333,0,0],"ti":[0.5,0,0]},{"t":84,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.964,0],[0.926,1.269],[-2.109,1.537],[-12.421,0],[-9.638,-6.507],[1.46,-2.163],[2.164,1.463],[9.78,0],[8.382,-6.108]],"o":[[-1.46,0],[-1.537,-2.109],[10.009,-7.294],[11.67,0],[2.163,1.46],[-1.458,2.163],[-8.071,-5.448],[-10.408,0],[-0.84,0.612]],"v":[[-30.712,9.847],[-34.536,7.904],[-33.499,1.302],[0.788,-9.847],[33.361,0.099],[34.633,6.66],[28.073,7.932],[0.788,-0.396],[-27.934,8.941]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8706,0.2078,0.4314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[250,273.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-5.22],[5.22,0],[0,5.22],[-5.22,0]],"o":[[0,5.22],[-5.22,0],[0,-5.22],[5.22,0]],"v":[[9.451,0],[0,9.451],[-9.451,0],[0,-9.451]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8706,0.2078,0.4314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[212.197,225.702],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-5.22],[5.22,0],[0,5.22],[-5.22,0]],"o":[[0,5.22],[-5.22,0],[0,-5.22],[5.22,0]],"v":[[9.451,0],[0,9.451],[-9.451,0],[0,-9.451]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8706,0.2078,0.4314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[287.803,225.702],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[287.803,225.702],"ix":2},"a":{"a":0,"k":[287.803,225.702],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"magnification glass","parent":1,"sr":1,"ks":{"o":{"a":0,"k":50,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27,24,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[14.17,-15.84],[0.87,-0.89],[25.14,0],[0,49.35],[-49.35,0],[0,-49.35]],"o":[[-0.82,0.93],[-16.27,16.75],[-49.35,0],[0,-49.35],[49.35,0],[0,22.88]],"v":[[39.949,35.36],[37.419,38.08],[-26.751,65.25],[-116.251,-24.25],[-26.751,-113.75],[62.749,-24.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[4.11,4.1],[0,0],[0,25.41],[60.93,0],[0,-60.93],[-60.93,0],[-19.48,17.2],[0,0],[-2.69,0],[-2.05,2.05]],"o":[[0,0],[14.48,-18.69],[0,-60.93],[-60.93,0],[0,60.93],[27.96,0],[0,0],[2.05,2.05],[2.69,0],[4.11,-4.1]],"v":[[134.169,116.83],[60.639,43.3],[83.749,-24.25],[-26.751,-134.75],[-137.251,-24.25],[-26.751,86.25],[46.269,58.61],[119.329,131.67],[126.749,134.75],[134.169,131.67]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8706,0.2078,0.4314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0}],"markers":[]}
1 change: 1 addition & 0 deletions frontend/src/assets/lottie-user.json

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions frontend/src/components/AlertMessage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.alert-message {
padding: 8px 12px;
border-radius: 4px;
font-size: 13px;
margin: 6px auto;
}

.background-error {
background-color: rgba(234, 134, 133, 1);
color: rgb(111, 3, 3);
}

.background-success {
background-color: rgba(29, 209, 161, 1);
color: rgb(6, 87, 67);
}
18 changes: 18 additions & 0 deletions frontend/src/components/AlertMessage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import PropTypes from "prop-types"
import "./AlertMessage.css"

export const AlertMessage = ({ type, message }) => {
return (
<div
className={`alert-message ${
type === "success" ? "background-success" : "background-error"
}`}>
<p>{message}</p>
</div>
)
}

AlertMessage.propTypes = {
type: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
}
Loading