I am learning Expressjs, so today i was looking at bcrypt, jsonwebtoken. so i created this after all the readings but after login the server console has this
> [email protected] start
> nodemon server.js
[nodemon] 3.1.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server.js`
Server running on port 5000
Connected to the database as id 123
Authorization Header: undefined
Token: undefined
No token provided
.env
PORT=5000
ACCESS_TOKEN_SECRET=61ba47d9af34a25e73da8349cf628ab4
Database mysql connection
const mysql = require("mysql");
const connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "xxxxxxxx",
database: "web_dev_classroom",
});
connection.connect((err) => {
if (err) {
console.error("Error connecting to the database:", err.stack);
return;
}
console.log("Connected to the database as id " + connection.threadId);
});
module.exports = connection;
server.js
const express = require("express");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const connection = require("./db");
const dotenv = require("dotenv");
const path = require("path");
const cors = require("cors");
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static("public"));
app.use(cors()); // Use cors middleware to enable CORS for all routes
// app.use(
// cors({
// origin: "*",
// methods: ["GET", "POST"],
// allowedHeaders: ["Content-Type", "Authorization"],
// })
// );
// -------------------------------------------Routes-------------------------------------
// Serve home page
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "views", "index.html"));
});
// Serve signup page
app.get("/signup.html", (req, res) => {
res.sendFile(path.join(__dirname, "views", "signup.html"));
});
// Serve login page
app.get("/login.html", (req, res) => {
res.sendFile(path.join(__dirname, "views", "login.html"));
});
// Signup route
app.post("/signup", async (req, res) => {
const {
username,
password,
email,
course_type,
state_of_origin,
phone_no,
} = req.body;
try {
// Check if email already exists
connection.query(
"SELECT * FROM users WHERE email = ?",
[email],
async (err, results) => {
if (err) throw err;
if (results.length > 0) {
return res
.status(400)
.json({ message: "Email already in use" });
}
const hashedPassword = await bcrypt.hash(password, 10);
connection.query(
"INSERT INTO users (username, password, email, course_type, state_of_origin, phone_no) VALUES (?, ?, ?, ?, ?, ?)",
[
username,
hashedPassword,
email,
course_type,
state_of_origin,
phone_no,
],
(err, results) => {
if (err) throw err;
res.status(201).json({
message: "User registered successfully",
});
}
);
}
);
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error" });
}
});
// Login route
app.post("/login", async (req, res) => {
const { email, password } = req.body;
connection.query(
"SELECT * FROM users WHERE email = ?",
[email],
async (err, results) => {
if (err) return res.status(500).json({ message: "Server error" });
if (results.length === 0) {
return res.status(400).json({ message: "User not found" });
} else {
const user = results[0];
const isMatch = await bcrypt.compare(password, user.password);
if (isMatch) {
const accessToken = jwt.sign(
{ id: user.id, username: user.username },
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: "2h" }
);
return res.json({ accessToken });
} else {
return res
.status(401)
.json({ message: "Incorrect password" });
}
}
}
);
});
// Protected route
app.get("/classroom.html", authenticateToken, (req, res) => {
console.log("Headers received:", req.headers);
res.sendFile(path.join(__dirname, "views", "classroom.html"));
});
// -------------------------------------------Routes Ends -------------------------------------
// Middleware to authenticate JWT token
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
console.log("Authorization Header:", authHeader);
const token = authHeader && authHeader.split(" ")[1];
console.log("Token:", token); // Log the extracted token
if (!token) {
console.log("No token provided");
return res.sendStatus(401);
}
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) {
console.error("JWT verification error:", err);
return res.sendStatus(403); // 403 for token expired or invalid
}
req.user = user;
console.log("Authenticated user:", user); // Log the authenticated user
next();
});
}
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
login.html and .js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login</title>
<link rel="stylesheet" href="/styles/style.css" />
</head>
<body>
<h1>Login</h1>
<form id="loginForm">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
<button type="submit">Login</button>
</form>
<script src="/scripts/login.js"></script>
</body>
</html>
document.getElementById("loginForm").addEventListener("submit", async (e) => {
e.preventDefault();
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
try {
const response = await fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password }),
});
if (response.ok) {
const data = await response.json();
if (data.accessToken) {
localStorage.setItem("accessToken", data.accessToken);
console.log(
"Token stored:",
localStorage.getItem("accessToken")
);
alert("Login successful!");
window.location.href = "/classroom.html";
} else {
alert("Login failed");
}
} else {
const error = await response.json();
alert(`Error: ${error.message}`);
}
} catch (error) {
alert("An error occurred: " + error.message);
}
});
classroom.html and .js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Classroom</title>
<link rel="stylesheet" href="/styles/style.css" />
<script defer src="/scripts/classroom.js"></script>
</head>
<body>
<h1>Welcome to the Classroom</h1>
<p>You have successfully logged in!</p>
</body>
</html>
document.addEventListener("DOMContentLoaded", async () => {
const token = localStorage.getItem("accessToken");
console.log("Token retrieved from localStorage:", token); // Log the retrieved token
if (!token) {
alert("No access token found, please login first.");
window.location.href = "/login.html";
return;
}
try {
const response = await fetch("/classroom", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
console.log("Request headers sent:", {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}); // Log the headers sent with the request
if (response.ok) {
const classroomContent = await response.text();
document.body.innerHTML = classroomContent;
} else {
console.error("Unauthorized access. Status code:", response.status);
alert("Unauthorized access. Please login again.");
window.location.href = "/login.html";
}
} catch (error) {
console.error("Fetch error:", error);
alert("An error occurred: " + error.message);
window.location.href = "/login.html";
}
});
I tested with Restclient in vscode and it was successful, redirected me to classroom.html
POST http://localhost:5000/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "Password2#"
}
###
GET http://localhost:5000/classroom.html
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwidXNlcm5hbWUiOiJPZ2FTb2xvIiwiaWF0IjoxNzIwMzgyNDA0LCJleHAiOjE3MjAzODk2MDR9.EHmd7f9nGx7bc6KrxaQTJYcVIk3Cns3jzWEuthvSRj0
But in browser after successful login, i will have unauthorized 401
GET http://localhost:5000/classroom.html 401 (Unauthorized)
General headers
Request URL:
http://localhost:5000/classroom.html
Request Method:
GET
Status Code:
401 Unauthorized
Remote Address:
[::1]:5000
Referrer Policy:
strict-origin-when-cross-origin
Response header
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/plain; charset=utf-8
Content-Length: 12
ETag: W/"c-dAuDFQrdjS3hezqxDTNgW7AOlYk"
Date: Sun, 07 Jul 2024 20:47:21 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Request header
GET /classroom.html HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Cookie: SL_G_WPT_TO=en; SL_GWPT_Show_Hide_tmp=undefined; SL_wptGlobTipTmp=undefined
Host: localhost:5000
Referer: http://localhost:5000/login.html
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36
sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"
sec-ch-ua-mobile: ?1
sec-ch-ua-platform: "Android"
I have added logs to see errors and these errors are what I can get.