Refactor & implement verification

This commit is contained in:
Cohen Erickson 2023-02-07 00:22:54 +00:00
parent ec2807105a
commit 326d59bff2
20 changed files with 1348 additions and 69 deletions

208
app.js
View file

@ -5,42 +5,195 @@ import createBareServer from "@tomphttp/bare-server-node";
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
import path from "node:path";
import config from "./deployment.config.json" assert { type: "json" };
import sgMail from "@sendgrid/mail";
import nodemailer from "nodemailer";
import * as uuid from "uuid";
import fs from "node:fs";
import bcrypt from "bcrypt";
const PORT = process.env.PORT || 3000;
const __dirname = process.cwd();
const ACTIVE_CODES = new Set();
let TOKENS = fs
.readFileSync("./memory.txt", "utf-8")
.trim()
.split("\n")
.map((token) => {
const parts = token.split(":");
return {
id: parts[0],
token: parts[1],
expiration: parts[2]
};
});
const server = http.createServer();
const app = express(server);
const bareServer = createBareServer("/bare/");
// Static files
// Middleware
app.use(cookieParser());
app.use(express.json());
app.use(
express.urlencoded({
extended: true
})
);
app.patch("/generate-otp", (req, res) => {
});
app.post("/validate-otp", (req, res) => {
});
app.all("/", (req, res, next) => {
console.log(req.url);
// validate verification
// Verification
app.patch("/generate-otp", async (req, res) => {
if (
config.sendgrid_verification == true ||
config.discord_verification == true ||
config.smtp_verificaton == true
) {
if (!req.cookies["verification"]) {
res.redirect("/unv.html");
} else {
next();
const OTP = generateCode();
ACTIVE_CODES.add(OTP);
setTimeout(() => {
ACTIVE_CODES.delete(OTP);
}, 1000 * 60 * 5);
let email = {
to: "",
from: "",
subject: `NebulaWEB personal access code ${OTP}`,
text: `
####### ACCESS CODE (OTP) ${OTP} #######
####### DO NOT SHARE THIS CODE! #######
(this message is automated)`
};
if (config.sendgrid_verification == true) {
sgMail.setApiKey(config.sendgrid_options.api_key);
email.to = config.sendgrid_options.to_email;
email.from = config.sendgrid_options.sendFromEmail;
try {
await sgMail.send(msg);
} catch {
return res.status(504).end();
}
}
if (config.smtp_verification == true) {
const smtpMailerAgent = nodemailer.createTransport(config.smtp_options);
email.to = config.smtp_options.to_email;
email.from = config.smtp_options.sendFromEmail;
try {
smtpMailerAgent.sendMail(email);
} catch {
return res.status(504).end();
}
}
if (config.discord_verification == true) {
try {
await fetch(config.webhook_url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
content: `Your NebulaWEB access code is \`${OTP}\``
})
});
} catch {
return res.status(500).end();
}
}
res.status(200).end();
} else {
res.status(404).end();
}
});
function generateCode() {
const code = Math.floor(Math.random() * 1000000);
return code.toString().padStart(6, "0");
}
app.post("/validate-otp", (req, res) => {
if (
config.sendgrid_verification == true ||
config.discord_verification == true ||
config.smtp_verificaton == true
) {
const OTP = req.body.otp;
if (ACTIVE_CODES.has(OTP)) {
ACTIVE_CODES.delete(OTP);
const token = uuid.v4();
TOKENS.push({
id: OTP,
token: hash(token),
expiration: Date.now() + 1000 * 60 * 60 * 24 * 30
});
fs.writeFileSync(
"./memory.txt",
TOKENS.map((token) => {
return `${token.id}:${token.token}:${token.expiration}`;
}).join("\n"),
"utf-8"
);
res.status(200).json({
success: true,
validation: `${OTP}:${token}`
});
} else {
res.status(401).json({
success: false
});
}
} else {
res.status(404).end();
}
});
// Static files
app.use("/uv/", express.static(uvPath));
app.use(express.static(path.join(__dirname, "static")));
app.use(express.static(path.join(__dirname, "public")));
// Login route
app.get("/login", (req, res) => {
if (
config.sendgrid_verification == true ||
config.discord_verification == true ||
config.smtp_verificaton == true
) {
res.sendFile(path.join(__dirname, "src", "unv.html"));
} else {
res.redirect("/");
}
});
// General Routes
app.use((req, res, next) => {
const verification = req.cookies["validation"];
if (!verification || !validateToken(verification)) {
res.redirect("/login");
} else {
next();
}
});
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "src", "index.html"));
});
app.get("/options", (req, res) => {
res.sendFile(path.join(__dirname, "src", "options.html"));
});
app.get("/privacy", (req, res) => {
res.sendFile(path.join(__dirname, "src", "privacy.html"));
});
// Bare Server
server.on("request", (req, res) => {
@ -66,3 +219,24 @@ server.on("listening", () => {
server.listen({
port: PORT
});
function hash(token) {
const salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(token, salt);
}
function validateToken(verification) {
console.log(verification);
const [id, token] = verification.split(":");
const tokenData = TOKENS.find((token) => token.id == id);
if (!tokenData) {
return false;
}
if (tokenData.expiration < Date.now()) {
return false;
}
return bcrypt.compareSync(token, tokenData.token);
}

View file

@ -7,7 +7,7 @@
},
"discord_verification": true,
"webhook_url": "YOUR DISCORD WEBHOOK URL",
"webhook_url": "https://discord.com/api/webhooks/1072301556461998172/rsMxTsD5AvGFqLoVRt_mmMcVFWJ9xQDLuiy_FsbWapHoYTamL9tsURZO-4j7P1OT4n0Z",
"smtp_verification": false,
"smtp_options": {

1152
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,14 +13,17 @@
"author": "Nebula Services",
"license": "AGPL-3.0-only",
"dependencies": {
"@sendgrid/mail": "^7.7.0",
"@titaniumnetwork-dev/ultraviolet": "^1.0.8-beta",
"@tomphttp/bare-server-node": "^1.2.2",
"bcrypt": "^5.1.0",
"cookie-parser": "^1.4.6",
"express": "^4.18.2",
"node-fetch": "^3.3.0",
"nodemailer": "^6.8.0",
"nodemailer-sendgrid-transport": "^0.2.0",
"serve-static": "^1.15.0"
"serve-static": "^1.15.0",
"uuid": "^9.0.0"
},
"engines": {
"node": ">=18"

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

@ -310,42 +310,6 @@ if (storedSetTheme == null) {
}
window.onload = function () {
function setCookie(cname, cvalue, exdays) {
const d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
let expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function httpGet(theUrl) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", theUrl, false); // false for synchronous request
xmlHttp.send(null);
return xmlHttp.responseText;
}
if (httpGet("/verification") == "true") {
if (getCookie("verifiedAccess") == "") {
console.log("COOKIE NOT FOUND - ENTRY NOT PERMITTED");
window.location = "/unv.html";
} else {
console.log("COOKIE RECOGNIZED - ENTRY PERMITTED ");
}
}
let background = localStorage.getItem("--background-primary");
let navbar = localStorage.getItem("--navbar-color");
let navbarHeight = localStorage.getItem("--navbar-height");

View file

@ -19,7 +19,8 @@ validateOTP.onclick = () => {
return response.json();
}).then((data) => {
if (data.success) {
setCookie("validation", data.validation, 30);
location.href = "/";
} else {
alert("Invalid OTP.");
}
@ -27,3 +28,14 @@ validateOTP.onclick = () => {
alert("An error occurred while validating your OTP.")
});
};
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}