106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
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(<IOTC>{
|
|
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;
|