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

Project-Auth-Evelyn #325

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0a5be10
added the userModel.js
EvelynDelCarmen Dec 6, 2023
2418ebb
started with a new code
EvelynDelCarmen Dec 8, 2023
db523f2
added the userRoutes.js
EvelynDelCarmen Dec 8, 2023
984c8d1
secretRoutes.js
EvelynDelCarmen Dec 8, 2023
18f7fc3
added the Endlist
EvelynDelCarmen Dec 8, 2023
3d453ad
added the .env file
EvelynDelCarmen Dec 9, 2023
899ffd9
trying to get it work
EvelynDelCarmen Dec 9, 2023
8dcee19
debugging
EvelynDelCarmen Dec 10, 2023
7ff9896
added the email and stuff
EvelynDelCarmen Dec 10, 2023
cb433b9
I get a result in Postman
EvelynDelCarmen Dec 10, 2023
cbe00a4
added the new info in .env and changed the structure, something wrong…
EvelynDelCarmen Dec 10, 2023
3026e68
the sign up endpoint works
EvelynDelCarmen Dec 11, 2023
8bd9f45
frontend
EvelynDelCarmen Dec 13, 2023
36dd86e
frontend + dashboard + register
EvelynDelCarmen Dec 13, 2023
1c1ff1f
frontend SignIn
EvelynDelCarmen Dec 13, 2023
580099f
react problem
EvelynDelCarmen Dec 13, 2023
8d723a3
debugged the frontend
EvelynDelCarmen Dec 14, 2023
c210138
test deploying
EvelynDelCarmen Dec 14, 2023
7a260b1
render
EvelynDelCarmen Dec 14, 2023
1e6e07f
api debugging
EvelynDelCarmen Dec 14, 2023
4a18521
added the store
EvelynDelCarmen Dec 14, 2023
fbe53cf
frontend 2.0
EvelynDelCarmen Dec 15, 2023
e58fd64
added the new code
EvelynDelCarmen Dec 15, 2023
ddaf224
changed the API
EvelynDelCarmen Dec 15, 2023
c83d5f1
changed the /user/register
EvelynDelCarmen Apr 2, 2024
a93fdcf
made some changes to backend
EvelynDelCarmen Apr 2, 2024
fec87d3
changed the logic in the backend
EvelynDelCarmen Apr 3, 2024
ec6b753
changed the backend logic
EvelynDelCarmen Apr 3, 2024
8824bc6
changed frontend
EvelynDelCarmen Apr 3, 2024
75be415
changed the logic
EvelynDelCarmen Apr 3, 2024
3797bb7
backend logic
EvelynDelCarmen Apr 4, 2024
ca07904
changed the logic and path to userModel.js
EvelynDelCarmen Apr 4, 2024
efb4627
ready to deploy
EvelynDelCarmen Apr 4, 2024
84e65e4
cleared all the comments, now ready
EvelynDelCarmen Apr 4, 2024
2abe88b
Update README.md
EvelynDelCarmen Apr 4, 2024
86db721
Update README.md
EvelynDelCarmen Apr 4, 2024
528352f
Update README.md
EvelynDelCarmen Apr 4, 2024
a299f1a
added the frontend URL
EvelynDelCarmen Apr 29, 2024
d08be9c
added the frontend url
EvelynDelCarmen Apr 29, 2024
e704c5c
added the new logic
EvelynDelCarmen Apr 29, 2024
d442dc9
git for deploy
EvelynDelCarmen Apr 29, 2024
2d30128
updated usermodel + PORT in server
EvelynDelCarmen Apr 29, 2024
280d155
git for deploy 2
EvelynDelCarmen Apr 29, 2024
68903d4
removed the second PORT
EvelynDelCarmen Apr 29, 2024
f1576f9
added the new log in / sign up logic
EvelynDelCarmen Apr 29, 2024
9d40289
removed the button
EvelynDelCarmen Apr 29, 2024
0584b6b
added the design to the not-found page
EvelynDelCarmen Apr 29, 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
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Project Auth API

Replace this readme with your own information about your project.
A simple authentication API built with Node.js and Express. It allows users to sign up, log in, and access protected routes with a valid token. The frontend is made with Tailwind CSS.

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?
I didnt understand the first codes that we were provided, I did everything a second time and used videos and lectures that I understood more of. If I had more time I would implement some sort of contact form in the /home directory and work more in the design.


## 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://auth-project-fullstack.netlify.app/

Backend: https://project-auth-api-mnx9.onrender.com/
3 changes: 2 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
package-lock.json
package-lock.json
.env
18 changes: 18 additions & 0 deletions backend/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import mongoose from "mongoose";
import dotenv from "dotenv";

dotenv.config();

export const connectDB = async () => {
try {

const conn = await mongoose.connect(process.env.MONGO_URI);

console.log(`Mongo DB Connected: ${conn.connection.host}`);
} catch (error) {
console.log(error);
process.exit(1);
}
};

213 changes: 213 additions & 0 deletions backend/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// import { UserModel } from "../models/userModel";
// //asyncHandler: We use asyncHandler to simplify error handling in asynchronous code. It helps us avoid writing repetitive try-catch blocks by automatically catching errors and passing them to our error handling middleware. This makes our code cleaner and more readable, reducing the risk of unhandled exceptions that could crash the server.
// import asyncHandler from "express-async-handler";
// // bcrypt: We use bcrypt to securely hash and store passwords in our database. Storing plain-text passwords is a security risk, as it exposes user credentials in case of a data breach. bcrypt helps us hash passwords in a way that is computationally expensive and time-consuming for potential attackers, making it difficult to crack passwords even if the database is compromised. It enhances the overall security of user authentication in our application.
// import bcrypt from "bcrypt";
// // jwt (JSON Web Tokens): We use jwt for authentication and authorization. It allows us to create and verify tokens that contain user identity information, such as user IDs or roles. These tokens are often sent with requests to secure routes and verify that a user has the necessary permissions to access certain resources. JWTs are stateless and efficient, making them a popular choice for secure communication between the client and server.
// import jwt from "jsonwebtoken";



// export const registerUserController = asyncHandler(async (req, res) => {
// // Extract email, username and password from the request body
// const { username, password, email } = req.body;
// // In this try section of the try catch we will first do some conditional logic and then generate the newUser with a crypted password within the DB.
// try {
// // 1st Condition
// // Check wether all fields of registration logic are NOT [!email] inputted from the request.body object
// if (!username || !email || !password) {
// // if so, set http status to a 400code
// res.status(400);
// // and throw new error with some info
// throw new Error("Please add all fields");
// }
// // 2nd Condition
// // Check if the current user trying to register is using an usernam or email that matches with the same username or email in the database, so they would have to choose something diferent
// const existingUser = await UserModel.findOne({
// $or: [{ username }, { email }],
// });
// if (existingUser) {
// res.status(400);
// throw new Error(
// `User with ${existingUser.username === username ? "username" : "email"
// } already exists`
// );
// }

// // Generate a salt and hash the user's password
// //In this line below, we're using the bcrypt library to create a random value called "salt." The salt is added to the password before hashing it. It adds an extra layer of security by making it more difficult for attackers to use precomputed tables (rainbow tables) to crack passwords. The 10 in genSaltSync(10) represents the cost factor, which determines how computationally intensive the hashing process will be.
// const salt = bcrypt.genSaltSync(10);

// const hashedPassword = bcrypt.hashSync(password, salt);
// // In this line below, we're using the generated salt to hash the user's password. Hashing transforms the password into a secure and irreversible string of characters. The bcrypt library handles the entire process for us, ensuring that the password is securely hashed. The resulting hashedPassword is what we store in the database to keep the user's password safe.
// // Create a new user instance with the hashed password
// const newUser = new UserModel({
// username,
// email,
// password: hashedPassword,
// });

// // Mongoose Method: newUser.save()
// // Description: Save the new user instance to the database
// await newUser.save();


// // Respond with a success message, user details, and the JWT token
// res.status(201).json({
// success: true,
// response: {
// username: newUser.username,
// email: newUser.email,
// id: newUser._id,
// accessToken: newUser.accessToken,
// },
// });
// } catch (e) {
// // Handle any errors that occur during the registration process
// res.status(500).json({ success: false, response: e.message });
// }
// });

// // -----------------------


// export const loginUserController = asyncHandler(async (req, res) => {
// // Extract username and password from the request body
// const { username, password } = req.body;

// try {

// console.log("Username:", username);
// console.log("Password:", password);
// // Find a user with the provided username in the database
// const user = await UserModel.findOne({ username });
// if (!user) {
// // If no user is found with the provided username, respond with a 401 Unauthorized and a user not found message
// return res
// .status(401)
// .json({ success: false, response: "User not found" });
// }

// // Compare the provided password with the hashed password in the database
// const isMatch = await bcrypt.compare(password, user.password);
// if (!isMatch) {
// // If the provided password doesn't match the stored password, respond with a 401 Unauthorized and an incorrect password message
// return res
// .status(401)
// .json({ success: false, response: "Incorrect password" });
// }
// // Respond with a success message, user details, and the JWT token
// res.status(200).json({
// success: true,
// response: {
// username: user.username,
// id: user._id,
// accessToken: user.accessToken, // token for the user using the acessToken generated from the model, // Use the generated token here
// },
// });
// } catch (e) {
// // Handle any errors that occur during the login process
// res.status(500).json({ success: false, response: e.message });
// }
// });

import { UserModel } from "../models/userModel";
import asyncHandler from "express-async-handler";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";

const generateToken = (user) => {
// Function to generate JWT token
return jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: "24h",
});
};

export const registerUserController = asyncHandler(async (req, res) => {


const { username, password, email } = req.body;

try {

if (!username || !email || !password) {
res.status(400);
throw new Error("Please add all fields");
}

const existingUser = await UserModel.findOne({
$or: [{ username }, { email }],
});
if (existingUser) {
res.status(400);
throw new Error(
`User with ${existingUser.username === username ? "username" : "email"
} already exists`
);
}


const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(password, salt);
const newUser = new UserModel({
username,
email,
password: hashedPassword,
});

await newUser.save();

const token = generateToken(newUser._id);

res.status(201).json({
success: true,
response: {
username: newUser.username,
email: newUser.email,
id: newUser._id,
accessToken: token,
},
});
} catch (e) {
res.status(500).json({ success: false, response: e.message });
}
});

export const loginUserController = asyncHandler(async (req, res) => {

const { username, password } = req.body;

try {

console.log("Username:", username);
console.log("Password:", password);

const user = await UserModel.findOne({ username });
if (!user) {
return res
.status(401)
.json({ success: false, response: "User not found" });
}


const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {

return res
.status(401)
.json({ success: false, response: "Incorrect password" });
}
const token = generateToken(user._id);

res.status(200).json({
success: true,
response: {
username: user.username,
id: user._id,
accessToken: token,
},
});
} catch (e) {
res.status(500).json({ success: false, response: e.message });
}
});
54 changes: 54 additions & 0 deletions backend/middleware/authenticateUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// // Import the UserModel from the User model file
// import { UserModel } from "../models/userModel";
// // Define a function called authenticateUser that takes a request (req), response (res), and a next function as parameters
// export const authenticateUser = async (req, res, next) => {
// // Retrieve the access token from the request header
// const accessToken = req.header("Authorization");
// try {
// // Find a user in the database using the retrieved access token
// // Mongoose Method: UserModel.findOne({ accessToken: accessToken })
// // Description: This line of code serves the purpose of authenticating a user based on the provided access token. It checks if a user with the specified accessToken exists in the database using the UserModel. If a user is found, their user document is stored in the user variable. This allows the middleware to add the user object to the request, making it available for subsequent middleware or routes. If no user is found, it prepares to send a 401 Unauthorized response to indicate that the user needs to log in. This code is an essential part of user authentication in the Node.js application and helps control access to protected routes or endpoints.
// const user = await UserModel.findOne({ accessToken: accessToken });
// if (user) {
// // If a user is found, add the user object to the request object
// req.user = user; // Add user to the request object
// next(); // Continue to the next middleware or route
// } else {
// // If no user is found, send a 401 Unauthorized response
// res.status(401).json({ success: false, response: "Please log in" });
// }
// } catch (e) {
// // Handle any errors that occur during the database query or user authentication
// res.status(500).json({ success: false, response: e.message });
// }
// };
import { UserModel } from "../models/userModel";
import jwt from "jsonwebtoken";

export const authenticateUser = async (req, res, next) => {
// Retrieve the access token from the request header and remove the "Bearer " prefix if present
const authHeader = req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ success: false, response: "No token provided, please log in" });
}

const token = authHeader.split(' ')[1]; // Get the token from the header

try {
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);

// Fetch the user using the ID decoded from the token
const user = await UserModel.findById(decoded.id).select("-password"); // Exclude password from user data
if (!user) {
return res.status(404).json({ success: false, response: "User not found" });
}

// Attach the user to the request object
req.user = user;
next(); // Continue to the next middleware or route handler
} catch (error) {
console.error(`JWT Error: ${error.message}`);
res.status(401).json({ success: false, response: "Invalid token, please log in again" });
}
};
50 changes: 50 additions & 0 deletions backend/models/userModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import mongoose from "mongoose";
import crypto from "crypto";

const { Schema } = mongoose;

const userSchema = new Schema(
{

username: {
type: String, // Specifies that 'username' should be a string
required: true, // Indicates that 'username' is a required field
unique: true, // Ensures that 'username' values are unique
minlength: 2, // Sets a minimum length of 2 characters for 'username'
},
// Define the 'password' field with a String data type
password: {
type: String, // Specifies that 'password' should be a string
required: true, // Indicates that 'password' is a required field
minlength: 6, // Sets a minimum length of 6 characters for 'password'
},
email: {
type: String,
required: true,
unique: true,
},
//Define the 'accessToken' field with a String data type
accessToken: {
type: String, // Specifies that 'accessToken' should be a string
default: () => crypto.randomBytes(128).toString("hex"), // Sets a default value using a cryptographic random string
},
},
{
timestamps: true,
}
);


export const UserModel = mongoose.model("User", userSchema);












8 changes: 7 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^16.3.1",
"express": "^4.17.3",
"mongoose": "^8.0.0",
"express-async-handler": "^1.2.0",
"express-list-endpoints": "^6.0.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.2",
"nodemon": "^3.0.1"
}
}
Loading