UNPKG

blite

Version:

Lightweight batteries-included backend library.

282 lines (245 loc) 7.95 kB
const jwt = require("jsonwebtoken"); const formidable = require('formidable'); const sanitizer = require('sanitizer'); const bcrypt = require('bcrypt'); const blite = require("./main"); const path = require('path'); function generateAuthToken(params, expire = blite.config.auth.access_token_life) { const token = jwt.sign(params, blite.config.server.jwt_private_key, { algorithm: "HS256", expiresIn: expire }); return token; } async function register(req, res, next) { try { const username = sanitizer.sanitize(req.body.username)?.toLowerCase(); const email = sanitizer.sanitize(req.body.email)?.toLowerCase(); const password = req.body.password; if (blite.db.users.exists({ '$or': [{ username }, { email }] })) { res.error = { code: "exists", message: "User already exists." }; return next(); } let hash = bcrypt.hashSync(password, bcrypt.genSaltSync(10)); req.user = blite.db.users.insert({ username, email, hash }); next(); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; async function login(req, res, next) { try { const username = sanitizer.sanitize(req.body.username)?.toLowerCase(); const password = req.body.password; const remember = req.body.remember === "on" || req.body.remember === "true" || req.body.remember === true; const user = blite.db.users.findOne({ '$or': [{ username }, { email: username }] }); if (!user) { res.error = { code: "invlid", message: "Invalid credentials." }; return next(); } if (!bcrypt.compareSync(password, user.hash)) { res.error = { code: "invlid", message: "Invalid credentials." }; return next(); } const refreshCookie = blite.config.auth.cookie_name + "_rt"; const refreshToken = generateAuthToken({ id: user.id }, blite.config.auth.refresh_token_life); res.cookie(refreshCookie, refreshToken, { secure: req.secure, httpOnly: true, sameSite: true, maxAge: remember ? blite.config.auth.refresh_token_life : undefined }); next(); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; async function resetPassword(req, res, next) { try { if (res.error) { return next(); } if (!req?.decoded?.email || !req.body.password) { res.error = { code: "exists", message: "Invalid link." }; return next(); } const email = req.decoded.email const password = req.body.password; const user = blite.db.users.findOne({email}) if (!user) { res.error = { code: "exists", message: "User already exists." }; return next(); } let hash = bcrypt.hashSync(password, bcrypt.genSaltSync(10)); user.hash = hash req.user = blite.db.users.update(user); next(); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; async function logout(req, res, next) { try { const refreshCookie = blite.config.auth.cookie_name + "_rt"; const accessCookie = blite.config.auth.cookie_name + "_at"; res.clearCookie(refreshCookie); res.clearCookie(accessCookie); next(); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; async function authenticateSession(req, res, next) { try { const refreshCookie = blite.config.auth.cookie_name + "_rt"; const accessCookie = blite.config.auth.cookie_name + "_at"; let refreshToken = req.cookies[refreshCookie]; if (!refreshToken) { res.error = { code: "nocookie", message: "No active user season." }; return next(); } const decoded = jwt.verify(refreshToken, blite.config.server.jwt_private_key); if (!blite.db.users.exists({ id: decoded.id })) { res.error = { code: "notfound", message: "User not found." }; return next(); } let accessToken = generateAuthToken({ id: decoded.id }); // 1 hour res.cookie(accessCookie, accessToken, { secure: req.secure, httpOnly: true, sameSite: true }); next(); } catch (error) { res.error = { code: error.code, message: "Session expired." }; next(); } }; async function authenticateUser(req, res, next) { try { const accessCookie = blite.config.auth.cookie_name + "_at"; let accessToken = req.cookies[accessCookie]; if (!accessToken) { res.error = { code: "nocookie", message: "No active user season." }; return next(); } const decoded = jwt.verify(accessToken, blite.config.server.jwt_private_key); // this might be failed to catch block let user = blite.db.users.findOne({ id: decoded.id }); if (!user) { res.error = { code: "notfound", message: "User not found." }; return next(); } req.user = user; next(); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; async function decodeToken(req, res, next) { try { let token = req.body.token; if (!token) { res.error = { code: "notoken", message: "No token provided." }; return next(); } const decoded = jwt.verify(token, blite.config.server.jwt_private_key); req.decoded = decoded; next(); } catch (error) { res.error = { code: error.code, message: "Link expired." }; next(); } }; async function decodeUpload(req, res, next) { try { const uploadDir = path.join(__dirname, blite.config.upload.dir || "uploads") if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } var form = new formidable.IncomingForm({ maxFileSize: blite.config.upload.max_file_size || undefined, uploadDir }); form.parse(req, async function (error, fields, files) { if (error) { res.error = { code: error.code, message: error.message }; return next(); }; if (!files.upload.size || !files.upload.originalFilename) { res.error = { code: "nofile", message: "No file provided." }; return next(); } req.fields = fields; req.files = files; next(); }); } catch (error) { res.error = { code: error.code, message: error.message }; next(); } }; exports.register = register; exports.login = login; exports.logout = logout; exports.authenticateSession = authenticateSession; exports.authenticateUser = authenticateUser; exports.decodeToken = decodeToken; exports.decodeUpload = decodeUpload; exports.generateAuthToken = generateAuthToken exports.resetPassword = resetPassword