2023-04-07 17:54:47 +00:00
|
|
|
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(<IYubiKey>{
|
|
|
|
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;
|