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-Mina #318

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
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
26 changes: 26 additions & 0 deletions backend/authenticateUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { UserModel } from "./models/UserModel"



export const authenticateUser = async(req , res , next) =>{
const accessToken = req.header("Authorization")

if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
)
try{
const user = await UserModel.findOne({accessToken: accessToken})
if(user){
req.user = user
next()
}else{
res.status(401).json({success: false , response: "please log in" })
}
}
catch(e){
res.status(500).json({success: false , response: e.message })
}
}


37 changes: 37 additions & 0 deletions backend/models/UserModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import mongoose from "mongoose";
import crypto from "crypto";

const { Schema } = mongoose;


const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
minlenght: 2
},
password: {
type: String,
required: true,
minlenght: 6

},
email: {
type: String,
required: true,
unique: true,

},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString("hex"),
required: false
}
},
{
timestamps: true
}
)

export const UserModel = mongoose.model('User', userSchema);
5 changes: 5 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
"@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",
"express-async-handler": "^1.2.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1"
}
Expand Down
99 changes: 99 additions & 0 deletions backend/routes/userRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

import express from "express";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";

import { UserModel } from "../models/UserModel";
import asyncHandler from "express-async-handler";
import dotenv from "dotenv";
import { authenticateUser } from "../authenticateUser";
dotenv.config()





const router = express.Router()

const geneerateToken = (user) => {
return jwt.sign({id: user._id} , process.env.JWT_SECRET, {
expiresIn: "24h"
});

};

router.post("/register" , asyncHandler(async(req , res) => {
const { username , password , email } = req.body;
try{
if(!username || !password || !email){
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();
res.status(201).json({
success: true,
response: {
username: newUser.username,
email: newUser.email,
id: newUser._id,
accessToken: geneerateToken(newUser)

}
});
}catch(e){
res.status(500).json({success: false , response: e.message })
}

}));

router.post("/login", asyncHandler(async(req , res) => {
const { username , password} = req.body;
try {
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"});
}
res.status(200).json({success: true , response: {

username: user.username,
email: user.email,
id: user._id,
accessToken: geneerateToken(user)


}})
} catch (error) {
res.status(500).json({success: false , response: {

message: error.message


}})
}

}))

router.get("/logged-in", authenticateUser, asyncHandler(async (req, res) => {
res.status(200).json({success: true , response: {
message: "user is logged in" }})
}))

export default router
9 changes: 8 additions & 1 deletion backend/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import express from "express";
import express, { urlencoded } from "express";
import cors from "cors";
import mongoose from "mongoose";
import dotenv from "dotenv";
dotenv.config()
import userRoutes from "./routes/userRoutes"

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true });
Expand All @@ -15,6 +18,10 @@ const app = express();
// Add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({extended: false}))
app.use(userRoutes);



// Start defining your routes here
app.get("/", (req, res) => {
Expand Down
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",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.1",
"styled-components": "^6.1.1",
"zustand": "^4.4.7"
},
"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 } from "react-router-dom";
import {routes} from "./routes/RoutesComp"



export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return <div>
<BrowserRouter>
<div className="wrapper">
<Routes>{routes}</Routes>
</div>
</BrowserRouter>
</div>;
};
87 changes: 87 additions & 0 deletions frontend/src/components/LogIn.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { userStore } from "./UserStore";


const apiEnv = import.meta.env.VITE_BACKEND_API;

export const Login = () => {

const {
username,
setUsername,
password,
setPassword,
user,
setUser,
setAccessToken,
} = userStore();
const navigate = useNavigate();

const handleLogin = async () => {

if (!username || !password) {
alert("Please enter both username and password");
return;
}

try {

const response = await fetch(`${apiEnv}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username, password }),
});
console.log(response);


if (response.ok) {

const data = await response.json();

setAccessToken(data.accessToken);

setUser(data);

localStorage.setItem("accessToken", data.accessToken);
setUsername("");
setPassword("");

alert("Login successful!");
navigate("/logged-in");
} else {

const errorData = await response.json();
alert(`Login failed: ${errorData.error}`);
}
} catch (error) {

alert("Error during login:", error.message);
}
};


useEffect(() => {
console.log("User:", user);
}, [user]);

return (
<div className="login-form">
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
);
};
28 changes: 28 additions & 0 deletions frontend/src/components/LogOut.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useNavigate } from "react-router-dom";
import { userStore } from "./UserStore";


export const Logout = () => {

const navigate = useNavigate();

const { setAccessToken, setIsLoggedIn, setUser } = userStore();


const handleLogOut = () => {

setUser(null);
setAccessToken(null);
setIsLoggedIn(false);
localStorage.removeItem("accessToken");

alert("Log out successful!");
navigate("/");
};

return (
<>
<button onClick={handleLogOut}>Log out</button>
</>
);
};
Loading