Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 3 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# Project Auth API

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Front-end: [![Netlify Status](https://api.netlify.com/api/v1/badges/93c0dec7-42c3-4b60-833d-fc9f955c4a81/deploy-status)](https://app.netlify.com/sites/yifan-project-authentication/deploys)

Back-end: https://project-authentication-6r12.onrender.com/
3 changes: 3 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.0",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1"
}
Expand Down
89 changes: 84 additions & 5 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import crypto from "crypto";
import bcrypt from "bcryptjs";
import expressListEndpoints from "express-list-endpoints";

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

// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
const User = mongoose.model("User", {
name: { type: String, unique: true, required: true },
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString("hex"), //user gets a string of random numbers as the accessToken
},
});

//a middleware function for looking for the user based on the accessToken saved in "Authorization" in the header
const authenticateUser = async (req, res, next) => {
const user = await User.findOne({ accessToken: req.header("Authorization") });
if (user) {
req.user = user;
next();
} else {
res.status(401).json({ loggedOut: true });
}
};

const port = process.env.PORT || 8080;
const app = express();

Expand All @@ -18,7 +39,65 @@ app.use(express.json());

// Start defining your routes here
app.get("/", (req, res) => {
res.send("Hello Technigo!");
const endpoints = expressListEndpoints(app);
const documentation = endpoints.map((endpoint) => ({
method: endpoint.methods.join(", "),
path: endpoint.path,
}));
res.json(documentation);
});

app.post("/registration", async (req, res) => {
try {
const { name, email, password } = req.body;

//error handling
if (!name || name.trim() === "") {
return res.status(400).json({ message: "Please enter username" });
}
if (!email || email.trim() === "") {
return res.status(400).json({ message: "Please enter email" });
}
if (!password || password.trim() === "") {
return res.status(400).json({ message: "Please enter password" });
}
if (password.length < 6) {
return res
.status(400)
.json({ message: "Password must be at least 6 characters long" });
}

const salt = bcrypt.genSaltSync();
const user = new User({
name,
email,
password: bcrypt.hashSync(password, salt),
});
user.save();
res.status(201).json({ id: user._id, accessToken: user.accessToken });
} catch (err) {
res
.status(400)
.json({ message: "Could not create the user", errors: err.errors });
}
});

app.get("/dashboard", authenticateUser);
app.get("/dashboard", (req, res) => {
res.json({ message: "You're logged in!" });
});

app.post("/login", async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (user && bcrypt.compareSync(req.body.password, user.password)) {
res.json({
userId: user._id,
name: user.name,
accessToken: user.accessToken,
});
} else {
return res.status(400).json({ notFound: true });
}
});

// Start the server
Expand Down
25 changes: 14 additions & 11 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Project Auth</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>

</html>
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { DashboardPage } from "./pages/DashboardPage";
import { SignLogInPage } from "./pages/SignLogInPage";

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<SignLogInPage />} />
<Route path="/dashboard" element={<DashboardPage />} />
</Routes>
</BrowserRouter>
);
};
81 changes: 81 additions & 0 deletions frontend/src/components/LogInForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";

export const LogInForm = () => {
const [logInData, setlogInData] = useState({
email: "",
password: "",
});
const [message, setMessage] = useState("");

const navigate = useNavigate();

const handleChange = (event) => {
setlogInData({
...logInData,
[event.target.name]: event.target.value,
});
};

const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await fetch(
"https://project-authentication-6r12.onrender.com/login",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(logInData),
}
);

if (!response.ok) {
throw new Error("Failed at logging in...");
}

const data = await response.json();
console.log("User logged in:", data);

//save the accessToken to localStorage
localStorage.setItem("accessToken", data.accessToken);

setlogInData({
email: "",
password: "",
});

setMessage("Login successful!");
navigate("/dashboard");
} catch (error) {
console.error("Login failed:", error);
setMessage("Login failed. Please try again.");
}
};

return (
<section>
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
placeholder="Email"
value={logInData.email}
onChange={handleChange}
required
/>
<input
type="password"
name="password"
placeholder="Password"
value={logInData.password}
onChange={handleChange}
required
/>
<button type="submit">Log In!</button>
</form>
{message && <p>{message}</p>}
</section>
);
};
84 changes: 84 additions & 0 deletions frontend/src/components/RegistrationForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState } from "react";

export const RegistrationForm = () => {
const [registrationData, setRegistrationData] = useState({
name: "",
email: "",
password: "",
});
const [message, setMessage] = useState("");

const handleChange = (event) => {
setRegistrationData({
...registrationData,
[event.target.name]: event.target.value,
});
};

const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await fetch(
"https://project-authentication-6r12.onrender.com/registration",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(registrationData),
}
);

if (!response.ok) {
throw new Error("Failed at creating the account...");
}

const data = await response.json();
console.log("User registered:", data);

setRegistrationData({
name: "",
email: "",
password: "",
});

setMessage("Registration successful!");
} catch (error) {
console.error("Registration failed:", error);
setMessage("Registration failed. Please try again.");
}
};

return (
<section>
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
placeholder="Name"
value={registrationData.name}
onChange={handleChange}
required
/>
<input
type="email"
name="email"
placeholder="Email"
value={registrationData.email}
onChange={handleChange}
required
/>
<input
type="password"
name="password"
placeholder="Password"
value={registrationData.password}
onChange={handleChange}
required
/>
<button type="submit">Sign Up!</button>
</form>
{message && <p>{message}</p>}
</section>
);
};
Loading