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
21 changes: 21 additions & 0 deletions backend/middleware/authenticateUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import jwt from "jsonwebtoken"
Copy link
Contributor

Choose a reason for hiding this comment

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

Fun that you tried out jwt 👍

import User from "../model/User.js"

const authenticateUser = async (req, res, next) => {
try {
const token = req.header("Authorization").replace("Bearer ", "")
const decoded = jwt.verify(token, process.env.JWT_SECRET)
console.log('Token:', token);
console.log('Dekrypterad token:', decoded);
const user = await User.findOne({ _id: decoded.userId })
if (!user) {
throw new Error()
}
req.user = user
next()
} catch (error) {
res.status(401).json({ error: "Please authenticate" })
}
}

export default authenticateUser
10 changes: 10 additions & 0 deletions backend/model/Thought.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import mongoose from 'mongoose';

const thoughtSchema = new mongoose.Schema({
text: { type: String, required: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }
});

const Thought = mongoose.model('Thought', thoughtSchema);

export default Thought;
23 changes: 23 additions & 0 deletions backend/model/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';

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

userSchema.pre('save', async function(next) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In your routes you're using arrow functions, try to be consistent with that throughout the whole code base

const user = this;
if (!user.isModified('password')) return next();
try {
const hash = await bcrypt.hash(user.password, 10);
user.password = hash;
next();
} catch (error) {
return next(error);
}
});

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

export default User;
10 changes: 8 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
{
"name": "project-auth-backend",
"version": "1.0.0",
"main": "server.js",
"type": "module",
"description": "Starter project to get up and running with express quickly",
"scripts": {
"start": "babel-node server.js",
"dev": "nodemon server.js --exec babel-node"
"start": "node server.js",
"dev": "nodemon --env-file=.env server.js --exec babel-node"
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1"
}
Expand Down
78 changes: 78 additions & 0 deletions backend/routes/Auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import express from "express"
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import User from "../model/User.js"
import authenticateUser from "../middleware/authenticateUser.js"
import Thought from "../model/Thought.js"

const authRouter = express.Router()

authRouter.post("/signup", async (req, res) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Endpoints are most often named nouns in plural, so instead of signup and login, it could be POST /users and POST /sessions

try {
const { username, password } = req.body
const user = new User({ username, password: password })
await user.save()
res.status(201).json({ message: "User registered successfully" })
} catch (error) {
res.status(500).json({ error: error.message })
}
})

authRouter.get("/signup", async (req, res) => {
try {
const users = await User.find()
res.status(200).json(users)
} catch (error) {
res.status(500).json({ error: error.message })
}
})
Comment on lines +21 to +28
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the thought behind this route? And how about security?


authRouter.post("/login", async (req, res) => {
try {
const { username, password } = req.body
console.log(username)
console.log(password)
Comment on lines +33 to +34
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove logs

const user = await User.findOne({ username })
console.log(user)
console.log("Inkommande lösenord:", password)
console.log("Hashat lösenord i databasen:", user.password)
if (!user) {
return res.status(401).json({ error: "Invalid username or password" })
}
const isPasswordValid = await bcrypt.compare(password, user.password)
console.log(isPasswordValid)
if (!isPasswordValid) {
return res.status(401).json({ error: "Invalid username or password" })
}
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET)
console.log("Genererat JWT-token:", token)
res.status(200).json({ token })
} catch (error) {
res.status(500).json({ error: error.message })
}
})

authRouter.get("/thoughts", authenticateUser, async (req, res) => {
try {
const userId = req.user._id
const userThoughts = await Thought.find({ user: userId })

res.status(200).json(userThoughts)
} catch (error) {
res.status(500).json({ error: error.message })
}
})

authRouter.post("/thoughts", authenticateUser, async (req, res) => {
try {
const { thought } = req.body
const userId = req.user._id
const newThought = new Thought({ text: thought, user: userId })
await newThought.save()
res.status(201).json({ message: "Thought saved successfully" })
} catch (error) {
res.status(500).json({ error: error.message })
}
})

export default authRouter
46 changes: 26 additions & 20 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import express from "express"
import mongoose from "mongoose"
import cors from "cors"
import listEndpoints from "express-list-endpoints"
import authRouter from "./routes/Auth.js"

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

// 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();
app.use(cors())
app.use(express.json())

// Add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());
mongoose.connect(`mongodb://${process.env.DB_HOST}:${process.env.DB_PORT}`, {
dbName: process.env.DB_NAME,
user: process.env.DB_USER,
pass: process.env.DB_PASS,
})
const db = mongoose.connection
db.on("error", console.error.bind(console, "Anslutningsfel:"))
db.once("open", () => {
console.log("Ansluten till databasen")
})
Comment on lines +18 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! Just remember that even logs and comments should be in English


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

app.use(authRouter)

// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
console.log(`Chattservern lyssnar på port ${port}`)
})
4 changes: 4 additions & 0 deletions backend/utils/generateSecretKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import crypto from "crypto"

const jwtSecret = crypto.randomBytes(32).toString("hex")
console.log(jwtSecret)
7 changes: 6 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"jwt-decode": "^4.0.0",
"prop-types": "^15.8.1",
"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
26 changes: 23 additions & 3 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
export const App = () => {
return <div>Find me in src/app.jsx!</div>;
};
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"
import Register from "./components/Register"
import Login from "./components/Login"
import Thoughts from "./components/Thoughts"
import Home from "./components/Home"
import { AuthProvider } from "./context/AuthContext"

function App() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, arrow function

return (
<AuthProvider>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/thoughts" element={<Thoughts />} />
</Routes>
</Router>
</AuthProvider>
)
}

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

const Home = () => {
return (
<div>
<h2>Welcome to the Home Page</h2>
<p>Please select an option:</p>
<div>
<Link to="/register">
<button>Register</button>
</Link>
<Link to="/login">
<button>Login</button>
</Link>
</div>
</div>
);
};

export default Home;
80 changes: 80 additions & 0 deletions frontend/src/components/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useState, useContext, useEffect } from "react"
import axios from "axios"
import { useNavigate } from "react-router-dom"
import { AuthContext } from "../context/AuthContext"
import { getUserIdFromToken } from "../context/authHelpers"

const Login = () => {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const { login } = useContext(AuthContext)
const navigate = useNavigate()

useEffect(() => {
const token = localStorage.getItem("token")
if (token && getUserIdFromToken(token)) {
navigate("/thoughts")
}
}, [navigate])

const handleSubmit = async (e) => {
e.preventDefault()
try {
console.log("Starting login process...")

const userResponse = await axios.get(
`https://project-auth-7ju7.onrender.com/signup?username=${username}`
)
console.log("User response:", userResponse.data)

if (!userResponse.data || !userResponse.data.length) {
alert("User does not exist. Please register first.")
return
}

console.log("User exists. Proceeding with login...")
Copy link
Contributor

Choose a reason for hiding this comment

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

Alerts are enough, no need for logs


const response = await axios.post(
"https://project-auth-7ju7.onrender.com/login",
{
username,
password,
}
)

console.log("Login response:", response.data)

localStorage.setItem("token", response.data.token)


login(response.data.token)

console.log("Login successful. Navigating to /thoughts...")
navigate("/thoughts")
} catch (error) {
console.error("Login failed:", error)
alert("Login failed")
}
}

return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<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 type="submit">Login</button>
</form>
)
}

export default Login
Loading