const express = require('express'); const crypto = require('crypto'); const Config = require('./config.js'); const Database = require('./db_interface.js'); let router = express.Router(); router.use(express.json()); let session_entropy = {}; user_cache = {}; email_cache = {}; async function get_user_details(id) { console.log(`search for user with id ${id}`); if (!user_cache[id]) { let user = await Database.schemas.user.findOne({ where: { id: id } }); if (!user) { return undefined; } user_cache[user.id] = { id: user.id, email: user.email, password_hash: user.password_hash }; email_cache[user.email] = user.id; } console.log(`returning ${JSON.stringify(user_cache[id])}`); return user_cache[id]; } async function get_user_details_by_email(email) { console.log(`search for user with email ${email}}`); if (!email_cache[email] || !user_cache[email_cache[email]]) { let user = await Database.schemas.user.findOne({ where: { email: email } }); if (!user) { return undefined; } user_cache[user.id] = { id: user.id, email: user.email, password_hash: user.password_hash }; email_cache[user.email] = user.id; } console.log(`returning ${JSON.stringify(user_cache[email_cache[email]])}`); return user_cache[email_cache[email]]; } router.get("/byEmail/:email", async (req, res) => { if (!req.params?.email) { res.status(400).json({ error: "email is a required parameter" }); } let user = get_user_details_by_email(req.params.email); console.log(user); if (user != null) { res.json({ id: user.id, email: user.email }); } else { res.sendStatus(404); } }); function hash(secret, password) { let pw_hash = crypto.pbkdf2Sync(password, secret, Config.config.key?.iterations || 1000, Config.config.key?.length || 64, "sha512"); return pw_hash.toString('base64'); } function verify(secret, password, hash) { let pw_hash = crypto.pbkdf2Sync(password, secret, Config.config.key?.iterations || 1000, Config.config.key?.length || 64, "sha512"); return hash === pw_hash.toString('base64'); } function hash_password(password) { return hash(Config.config.secret, password); } function verify_password(password, hash) { return verify(Config.config.secret, password, hash); } function get_session_token(id, token) { session_entropy[id] = crypto.randomBytes(Config.config.session_entropy || 32); return hash(session_entropy[id], token); } function verify_session_token(id, hash, token) { if (session_entropy[id]) { return verify(session_entropy[id], hash, token); } else { return false; } } async function enforce_session_login(req, res, next) { let userid = req.cookies?.userid; let session_token = req.cookies?._session; console.log("a", userid, session_token); if (!userid || !session_token) { res.sendStatus(401); } let user = await get_user_details(userid); if (!user) { res.sendStatus(401); } let verified_session = verify_session_token(userid, user.password_hash, session_token); if (!verified_session) { res.sendStatus(401); } return next(); } router.post("/new", async (req, res) => { if (!req.body?.email || !req.body?.password) { res.status(400).json({ error: "must have email and password fields" }); } let user = await get_user_details_by_email(req.body.email); console.log(user); if (user != null) { res.status(403).json({ error: `email ${req.body.email} is already in use.` }); } else { let user = await Database.schemas.user.create({ email: String(req.body.email), password_hash: hash_password(req.body.password) }); res.json({ id: user.id, email: user.email }); } }); router.post("/login", async (req, res) => { if (!req.body?.email || !req.body?.password) { res.status(400).json({ error: "must have email and password fields" }); } let user = await get_user_details_by_email(req.body.email); if (!user) { res.status(401).json({ error: "incorrect email or password" }); } let verified = verify_password(req.body.password, user.password_hash); if (!verified) { res.status(401).json({ error: "incorrect email or password" }); } res.cookie("userid", user.id); res.cookie("_session", get_session_token(user.id, user.password_hash)); res.sendStatus(204); }); router.get("/:id(([a-f0-9\-])+)", async (req, res) => { if (!req.params?.id) { res.status(400).json({ error: "must have id parameter" }); } let user = await get_user_details(req.body.id); console.log(user); if (user != null) { res.json({ id: user.id, email: user.email }); } else { res.sendStatus(404); } }); router.use("/authorized", enforce_session_login); router.get("/authorized", async (req, res) => { let userid = req.cookies?.userid; let user = get_user_details(userid); res.json({ authorized: true, user: { id: user.id, email: user.email } }); }); module.exports = { router: router, enforce_session_login: enforce_session_login };