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 #346

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6e18d4f
Delete netlify.toml
AntonellaMorittu May 22, 2024
774efcc
Creating the backend
Paulajungaker May 23, 2024
f8e4b53
Adding netlify file
Paulajungaker May 23, 2024
11e3f91
Creating config file for axios
Paulajungaker May 23, 2024
eb3de0b
Setting up routes and creating navbar
Paulajungaker May 23, 2024
700c5ea
First styling of navbar
Paulajungaker May 23, 2024
f2cd33e
Creating homepage
Paulajungaker May 23, 2024
0886f20
First styling of homepage
Paulajungaker May 23, 2024
5233d74
Changing some styling
Paulajungaker May 23, 2024
53e5133
Making responsive
Paulajungaker May 23, 2024
4e36f26
Creating the Register component
Paulajungaker May 23, 2024
55f8c5d
Creating login component and styling login and register
Paulajungaker May 24, 2024
ee656f0
Creating protected component
Paulajungaker May 24, 2024
d2c37a2
Adding .env file
Paulajungaker May 26, 2024
8df362b
API working
Paulajungaker May 26, 2024
9a381b1
Adding new backend link
Paulajungaker May 26, 2024
f9b925f
Getting API to work for registration
Paulajungaker May 26, 2024
135021f
Working on getting the login to work
Paulajungaker May 26, 2024
69194c0
Trying to get login to work by debugging
Paulajungaker May 27, 2024
46a731b
Trying to solve issues
Paulajungaker May 27, 2024
126e8e7
Redoing backend and testing with postman. Successfull
Paulajungaker May 27, 2024
bad04a0
Login and register is working
Paulajungaker May 27, 2024
48bc4ec
Making able to see protected content and logout
Paulajungaker May 27, 2024
0a1411c
Styling the protected page
Paulajungaker May 27, 2024
aac8108
Changing to deployed backend api
Paulajungaker May 27, 2024
0fc937d
Small changes in paths
Paulajungaker May 27, 2024
6a05786
Changing route
Paulajungaker May 27, 2024
fcb7b43
Small changes
Paulajungaker May 27, 2024
d37ade6
Changing to deployed API
Paulajungaker May 27, 2024
20e9be3
Removing unnecessary components
Paulajungaker May 27, 2024
dd56dba
Testing
Paulajungaker May 27, 2024
80125c0
Updating to store token
Paulajungaker May 27, 2024
d052b22
Correcting spelling error
Paulajungaker May 28, 2024
9544c72
Getting registering to store token
Paulajungaker May 28, 2024
df9ab2f
Updating README
Paulajungaker May 28, 2024
95c2b50
Final styling changes
Paulajungaker May 28, 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
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +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.
Creating my own Authentication API and using it in a simple website.

## 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 used tools like MongoDB Atlas and postman to test my API. I did a small plan for the project using Figma. If I had more time I would work on the strech goals.

## 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 deploy: https://project-auth-backend-kfgp.onrender.com/
Frontend deploy: https://sweet-lily-05d48d.netlify.app/
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
11 changes: 11 additions & 0 deletions backend/models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
username: { type: String, unique: true, required: true },
password: { type: String, required: true },
accessToken: { type: String },
});

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

export default User;
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",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.17.3",
"mongoose": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
104 changes: 94 additions & 10 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,110 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
import User from "./models/User";

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

// 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 app = express();
const port = process.env.PORT || 8080;

// Add middlewares to enable cors and json body parsing
// Middleware
app.use(cors());
app.use(express.json());

// Start defining your routes here
// Connect to MongoDB
const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-auth";
mongoose.connect(mongoUrl);
mongoose.Promise = Promise;

// Routes
app.get("/", (req, res) => {
res.send("Hello Technigo!");
res.send("Welcome to your Express API");
});

app.post("/api/register", async (req, res) => {
const { username, password } = req.body;

try {
// Check if the username already exists
let user = await User.findOne({ username });
if (user) {
return res.status(400).json({ message: "Username already taken" });
}

// Hash the password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);

// Create a new user
user = new User({ username, password: hashedPassword });
await user.save();

// Generate JWT token
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: "1h",
});

res.status(201).json({ message: "User registered successfully", token });
} catch (error) {
console.error("Registration error:", error);
res.status(500).json({ message: "Server error" });
}
});

app.post("/api/login", async (req, res) => {
const { username, password } = req.body;

try {
// Find the user by username
const user = await User.findOne({ username });
if (!user) {
return res.status(400).json({ message: "Invalid credentials" });
}

// Check if the password matches
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ message: "Invalid credentials" });
}

// Generate JWT token
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: "1h",
});

res.json({ token });
} catch (error) {
console.error("Login error:", error);
res.status(500).json({ message: "Server error" });
}
});

app.get("/api/protected", authenticateToken, (req, res) => {
res.json({ message: "This is protected content" });
});

function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];

if (!token) {
return res.status(401).json({ message: "No token, authorization denied" });
}

jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
console.error("Token verification error:", err);
return res.status(403).json({ message: "Token is not valid" });
}
req.user = decoded;
next();
});
}

// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
Expand Down
3 changes: 2 additions & 1 deletion frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ dist-ssr
*.sln
*.sw?

package-lock.json
package-lock.json
.env
10 changes: 8 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<!doctype html>
<!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>
<title>Cute kitties</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=Bebas+Neue&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Quicksand:wght@300..700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="root"></div>
Expand Down
5 changes: 4 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"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/Kittens.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/LogoIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/homeKitten.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 as Router } from "react-router-dom";
import AppRoutes from "./routes/AppRoutes";

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return (
<Router>
<div className="app">
<AppRoutes />
</div>
</Router>
);
};

export default App;
41 changes: 41 additions & 0 deletions frontend/src/components/Home.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Link } from "react-router-dom";
import { FaLongArrowAltDown } from "react-icons/fa";
import "../styling/Home.css";

const Home = () => {
return (
<div className="homeContainer">
<div className="homeIntro">
<h1 className="homeTitle">Ready to see some very cute kitties?</h1>
<div className="homeSection">
<img src="/homeKitten.jpg" alt="cute kitten" className="homeImage" />
<p className="homeText">
"A cat will do what it wants, when it wants, and there is not a
thing you can do about it"
</p>
</div>
</div>

<div className="arrowIcon">
<FaLongArrowAltDown />
</div>

<div className="homeAccount">
<p className="accountIntro">
To get access to the kittens you need to log in.
</p>
<p className="accountText">Already have an account?</p>
<Link to="/login">
<button className="accountButton">Login</button>
</Link>

<p className="accountText">Need to create an account?</p>
<Link to="/register">
<button className="accountButton">Register</button>
</Link>
</div>
</div>
);
};

export default Home;
34 changes: 34 additions & 0 deletions frontend/src/components/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Link } from "react-router-dom";
import "../styling/Navbar.css";

const Navbar = () => {
return (
<nav className="navbar">
<div className="navContainer">
<div className="navTitleSection">
<img src="/LogoIcon.png" alt="Cat icon" className="logoIcon" />
<h1 className="navTitle">Cute kitties</h1>
</div>
<ul className="navList">
<li className="navItem">
<Link to="/" className="navLink">
Home
</Link>
</li>
<li className="navItem">
<Link to="/register" className="navLink">
Register
</Link>
</li>
<li className="navItem">
<Link to="/login" className="navLink">
Login
</Link>
</li>
</ul>
</div>
</nav>
);
};

export default Navbar;
62 changes: 62 additions & 0 deletions frontend/src/components/auth/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import "../../styling/Auth.css";
import { useState } from "react";
import axiosInstance from "../axiosInstance";
import { useNavigate } from "react-router-dom";

const Login = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();

const handleLogin = async (e) => {
e.preventDefault();
try {
const response = await axiosInstance.post("/api/login", {
username,
password,
});
const { token } = response.data;
localStorage.setItem("token", token);
console.log("Login successfull");
navigate("/protected");
} catch (error) {
setError("Invalid credentials. Please try again");
console.error("Login error", error);
}
};

return (
<div className="authContainer">
<h1 className="authTitle">Login</h1>
<form className="authForm" onSubmit={handleLogin}>
<label className="authLabel">
Username:
<input
type="text"
required
className="textInput"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</label>
<label className="authLabel">
Password:
<input
type="password"
required
className="passwordInput"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type="submit" className="authButton">
Login
</button>
</form>
{error && <p className="errorMessage">{error}</p>}
</div>
);
};

export default Login;
Loading