Skip to content

Commit

Permalink
feat: added csrf token to login form
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbrusegard committed Oct 6, 2023
1 parent 13df75f commit 222230c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 69 deletions.
78 changes: 23 additions & 55 deletions admin/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,64 +38,36 @@ app.use(
})
);

const loginHtml = `
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=10" />
<title>Admin | Niclas Nordlund Photography</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=Poppins&display=swap"
rel="stylesheet"
/>
<style>
* {
font-family: "Poppins", sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
font-size: 18px;
}
</style>
</head>
<body>
<form method="POST">
<input type="hidden" id="csrfToken" name="csrfToken" value="" />
<label for="password">Password:</label>
<input type="password" id="password" name="password" />
<button type="submit">Submit</button>
<p></p>
</form>
</body>
</html>
`;

const limiter = rateLimit({
windowMs: 60 * 1000 * 15, // 15 minutes
max: 10, // 10 failed attempts allowed in that window
message: loginHtml.replace(
"<p></p>",
"<p>Too many login attempts, try again in 15 minutes.</p>"
),
message: (req, res) => {
res.render("login", {
csrfToken: req.session.csrfToken,
errorMessage: "Too many requests. Please try again in 15 minutes.",
});
},
});

app.get("/", limiter, (req, res) => {
const token = crypto.randomBytes(64).toString("hex");
req.session.csrfToken = token;
if (req.session && req.session.userid) {
const token = crypto.randomBytes(64).toString("hex");
req.session.csrfToken = token;
res.render("index", { csrfToken: token });
} else {
res.send(loginHtml);
res.render("login", {
csrfToken: token,
errorMessage: "",
});
}
});

app.post("/", limiter, (req, res) => {
if (req.session.csrfToken !== req.body.csrfToken) {
res.status(401).send("Unauthorized");
return;
}

bcrypt.compare(
req.body.password,
process.env.ADMIN_PASSWORD,
Expand All @@ -107,12 +79,10 @@ app.post("/", limiter, (req, res) => {
req.session.userid = "admin";
res.redirect("/");
} else {
res.send(
loginHtml.replace(
"<p></p>",
"<p>Incorrect password. Please try again.</p>"
)
);
res.render("login", {
csrfToken: req.session.csrfToken,
errorMessage: "Incorrect password. Please try again.",
});
}
}
);
Expand All @@ -124,7 +94,6 @@ function authenticate(req, res, next) {
req.session.userid &&
req.session.csrfToken === req.headers["csrf-token"]
) {
console.log("Authenticated");
next();
} else {
res.status(401).send("Unauthorized");
Expand Down Expand Up @@ -168,15 +137,14 @@ app.get("/download-photo", async (req, res) => {
version: "v4",
action: "read",
expires: Date.now() + 5 * 60 * 1000,
responseDisposition: 'attachment; filename="' + name + "",
};
const [signedUrl] = await storage
.bucket(bucketName)
.file(name)
.getSignedUrl(options);

res.setHeader("Content-Type", "image/jpeg");
res.setHeader("Content-Disposition", `attachment; filename="${name}"`);
res.redirect(signedUrl);
res.status(200).json({ signedUrl });
} catch (error) {
console.error("Error generating signed URL:", error);
res.status(500).send("Error generating URL for photo to download");
Expand Down
23 changes: 9 additions & 14 deletions admin/views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,16 @@
if (response.status === 401) {
location.reload();
}
return response.text();
return response.json();
})
.then((response) => {
if (response.ok) {
response.text().then((url) => {
const a =
document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
}
.then((data) => {
const url = data.signedUrl;
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
})
.catch((error) => {
alert(
Expand Down
37 changes: 37 additions & 0 deletions admin/views/login.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=10" />
<title>Admin | Niclas Nordlund Photography</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=Poppins&display=swap"
rel="stylesheet"
/>
<style>
* {
font-family: "Poppins", sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
font-size: 18px;
height: 80vh;
}
</style>
</head>
<body>
<form method="POST">
<input type="hidden" name="csrfToken" value="<%= csrfToken %>" />
<label for="password">Password:</label>
<input type="password" id="password" name="password" />
<button type="submit">Submit</button>
<p><%= errorMessage %></p>
</form>
</body>
</html>

0 comments on commit 222230c

Please sign in to comment.