import { Router } from "express"; import Stacker from "../../../middlewares/stacker"; import { GetUserMiddleware } from "../../../middlewares/user"; import TwoFactor, { TFATypes as TwoFATypes, IOTC, } from "../../../../models/twofactor"; import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; import moment = require("moment"); import { upgradeToken } from "../helper"; import Logging from "@hibas123/nodelogging"; import * as speakeasy from "speakeasy"; import * as qrcode from "qrcode"; import config from "../../../../config"; const OTCRoute = Router(); OTCRoute.post( "/", Stacker(GetUserMiddleware(true, true), async (req, res) => { const { type } = req.query; if (type === "create") { //Generating new let secret = speakeasy.generateSecret({ name: config.core.name, issuer: config.core.name, }); let twofactor = TwoFactor.new({ user: req.user._id, type: TwoFATypes.OTC, valid: false, data: secret.base32, }); let dataurl = await qrcode.toDataURL(secret.otpauth_url); await TwoFactor.save(twofactor); res.json({ image: dataurl, id: twofactor._id, }); } else if (type === "validate") { // Checking code and marking as valid const { code, id } = req.body; Logging.debug(req.body, id); let twofactor: IOTC = await TwoFactor.findById(id); const err = () => { throw new RequestError("Invalid ID!", HttpStatusCode.BAD_REQUEST); }; if ( !twofactor || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.OTC || !twofactor.data || twofactor.valid ) { Logging.debug("Not found or wrong user", twofactor); err(); } if (twofactor.expires && moment().isAfter(moment(twofactor.expires))) { await TwoFactor.delete(twofactor); Logging.debug("Expired!", twofactor); err(); } let valid = speakeasy.totp.verify({ secret: twofactor.data, encoding: "base32", token: code, }); if (valid) { twofactor.expires = undefined; twofactor.valid = true; await TwoFactor.save(twofactor); res.json({ success: true }); } else { throw new RequestError("Invalid Code!", HttpStatusCode.BAD_REQUEST); } } else { throw new RequestError("Invalid type", HttpStatusCode.BAD_REQUEST); } }) ); OTCRoute.put( "/", Stacker( GetUserMiddleware(true, false, undefined, false), async (req, res) => { let { login, special } = req.token; let { id, code } = req.body; let twofactor: IOTC = await TwoFactor.findById(id); if ( !twofactor || !twofactor.valid || !twofactor.user.equals(req.user._id) || twofactor.type !== TwoFATypes.OTC ) { throw new RequestError( "Invalid Method!", HttpStatusCode.BAD_REQUEST ); } if (twofactor.expires && moment().isAfter(twofactor.expires)) { twofactor.valid = false; await TwoFactor.save(twofactor); throw new RequestError( "Invalid Method!", HttpStatusCode.BAD_REQUEST ); } let valid = speakeasy.totp.verify({ secret: twofactor.data, encoding: "base32", token: code, }); if (valid) { let [login_exp, special_exp] = await Promise.all([ upgradeToken(login), upgradeToken(special), ]); res.json({ success: true, login_exp, special_exp }); } else { throw new RequestError("Invalid Code", HttpStatusCode.BAD_REQUEST); } } ) ); export default OTCRoute;