import { Router, Request } from "express"; import Stacker from "../../../middlewares/stacker"; import { UserMiddleware, GetUserMiddleware } from "../../../middlewares/user"; import * as u2f from "u2f"; import config from "../../../../config"; import TwoFactor, { TFATypes as TwoFATypes, IYubiKey, } from "../../../../models/twofactor"; import RequestError, { HttpStatusCode } from "../../../../helper/request_error"; import moment = require("moment"); import LoginToken from "../../../../models/login_token"; import { upgradeToken } from "../helper"; import Logging from "@hibas123/nodelogging"; const U2FRoute = Router(); /** * Registerinf a new YubiKey */ U2FRoute.post( "/", Stacker(GetUserMiddleware(true, true), async (req, res) => { const { type } = req.query; if (type === "challenge") { const registrationRequest = u2f.request(config.core.url); let twofactor = TwoFactor.new({ user: req.user._id, type: TwoFATypes.U2F, valid: false, data: { registration: registrationRequest, }, }); await TwoFactor.save(twofactor); res.json({ request: registrationRequest, id: twofactor._id, appid: config.core.url, }); } else { const { response, id } = req.body; Logging.debug(req.body, id); let twofactor: IYubiKey = 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.U2F || !twofactor.data.registration || 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(); } const result = u2f.checkRegistration( twofactor.data.registration, response ); if (result.successful) { twofactor.data = { keyHandle: result.keyHandle, publicKey: result.publicKey, }; twofactor.expires = undefined; twofactor.valid = true; await TwoFactor.save(twofactor); res.json({ success: true }); } else { throw new RequestError( result.errorMessage, HttpStatusCode.BAD_REQUEST ); } } }) ); U2FRoute.get( "/", Stacker( GetUserMiddleware(true, false, undefined, false), async (req, res) => { let { login, special } = req.token; let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true, }); if (!twofactor) { throw new RequestError( "Invalid Method!", HttpStatusCode.BAD_REQUEST ); } if (twofactor.expires) { if (moment().isAfter(twofactor.expires)) { twofactor.valid = false; await TwoFactor.save(twofactor); throw new RequestError( "Invalid Method!", HttpStatusCode.BAD_REQUEST ); } } let request = u2f.request(config.core.url, twofactor.data.keyHandle); login.data = { type: "ykr", request, }; let r; if (special) { special.data = login.data; r = LoginToken.save(special); } await Promise.all([r, LoginToken.save(login)]); res.json({ request }); } ) ); U2FRoute.put( "/", Stacker( GetUserMiddleware(true, false, undefined, false), async (req, res) => { let { login, special } = req.token; let twofactor: IYubiKey = await TwoFactor.findOne({ user: req.user._id, type: TwoFATypes.U2F, valid: true, }); let { response } = req.body; if ( !twofactor || !login.data || login.data.type !== "ykr" || (special && (!special.data || special.data.type !== "ykr")) ) { 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 login_exp; let special_exp; let result = u2f.checkSignature( login.data.request, response, twofactor.data.publicKey ); if (result.successful) { if (special) { let result = u2f.checkSignature( special.data.request, response, twofactor.data.publicKey ); if (result.successful) { special_exp = await upgradeToken(special); } else { throw new RequestError( result.errorMessage, HttpStatusCode.BAD_REQUEST ); } } login_exp = await upgradeToken(login); } else { throw new RequestError( result.errorMessage, HttpStatusCode.BAD_REQUEST ); } res.json({ success: true, login_exp, special_exp }); } ) ); export default U2FRoute;