Ever wondered how to build a clean, secure system for sharing user content via unique links? It gets interesting when you have to separate public access from password-protected private shares.
Here is a breakdown of how the frontend and backend interact to extract IDs, enforce security via bcrypt, and protect against brute-force attacks with rate-limiting:
How It Works
- URL Activation: A user opens the link
http://localhost:3000/?id=POST_ID. - ID Extraction: The frontend parses the URL and extracts the ID using
URLSearchParams. - Request Initiation: If an ID is present, the frontend requests data from
/api/v1/get/save-from-link/:id. - Backend Verification: The backend checks if the ID exists in the database.
- Public Access: If the link is public, the backend returns the content (
link.for.content). - Private Access: If the link is private, the backend responds with a success signal and
need_password: truewithout sending any content. - Frontend Evaluation: The frontend checks the
need_passwordflag. - Content Display: If false, it renders the public content.
- Password Prompt: If true, a popup prompts the user to enter a password.
- Rate Limiting: Users get 10 password attempts per 7 minutes across all private saves globally.
- Password Validation: The frontend sends the ID and password to
/api/v1/validate-save-password/:id. - Security Match: The backend queries the ID ensuring
status: "private", then verifies the password usingawait bcrypt.compare(password, link.password).
The Code
// In server.js
app.get("/api/v1/get/save-from-link/:id", checkValidID, async (req, res) => {
try {
const id = req.params.id;
const link = await schemas.Links.findOne({ for: id }).populate("for").lean(); // Do you have permissions to see this doc?
if (!link) return res.status(400).json({ error: "Invalid link!" });
if (link.status === "private") return res.status(400).json({ success: true, need_password: true }); // Send a success singal, but warns that a password must be entered
return res.status(200).json({ success: true, content: link.for.content });
} catch (e) {
console.log("Error: " + e.message);
return res.status(500).json({ error: "Server error" });
}
});
// In actions.js
router.post("/api/v1/validate-save-password/:id", validatePasswordRateLimiter, checkAuth, checkValidID, async (req, res) => {
try {
const id = req.params.id;
const { password } = req.body;
if (typeof password !== "string") return res.status(400).json({ error: "Password must be a type of string!" });
if (!password) return res.status(400).json({ error: "You didn't enter a password!" }); // Find link
const link = await schemas.Links.findOne({ for: id, status: "private" }).populate("for").lean(); // Check password match
const isMatch = await bcrypt.compare(password, link.password);
if (!isMatch) return res.status(400).json({ error: "Invalid password. Do you want to try again?", invalid_password: true });
return res.status(200).json({ success: true, content: link.for.content });
} catch (e) {
console.log("Error: " + e.message);
return res.status(400).json({ error: "Server error" });
}
});
Liked the project? Please consider giving it a star on GitHub at https://github.com/Hfs2024/Markdown-Previewer
Top comments (0)