Skip to content
Permalink
Browse files
feat: adds time tracking for all auth stages
  • Loading branch information
MantasMikal committed Feb 6, 2021
1 parent 36e4c48 commit 1231f36974de9638c3802063dc249bc6b6ef7f17
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 47 deletions.
@@ -29,8 +29,14 @@ const User = mongoose.model(
authDuration: {
type: Number,
},
regDuration: {
type: Number,
registrationTimings: {
type: Object,
},
passwordAuthTimings: {
type: Object,
},
fido2AuthTimings: {
type: Object,
},
userAgent: {
type: Object
@@ -1,5 +1,5 @@
import express from "express";
import fido2lib from "@dannymoerkerke/fido2-lib";
import fido2lib from "fido2-library";
import base64url from "base64url";
import crypto from "crypto";
import bcrypt from "bcrypt";
@@ -57,7 +57,7 @@ router.post("/registration-options", async (req, res) => {
});

router.post("/register", async (req, res) => {
const { credential, email, firstName, lastName, password, regDuration, userAgent } = req.body;
const { credential, email, firstName, lastName, password, userAgent } = req.body;

const challenge = new Uint8Array(req.session.challenge.data).buffer;
const base64RawId = credential.rawId;
@@ -91,12 +91,11 @@ router.post("/register", async (req, res) => {
lastName,
email,
password: hash,
regDuration: regDuration,
userAgent: userAgent,
});
user.save();

const secretVerificationCode = crypto.randomBytes(32).toString("hex")
const secretVerificationCode = crypto.randomBytes(32).toString("hex");
const verificationCode = await AccountVerificationCode.create({
email: user.email,
code: secretVerificationCode,
@@ -121,57 +120,66 @@ router.post("/authentication-options", async (req, res) => {
const authnOptions = await fido.assertionOptions();
const { email } = req.body;
const user = await User.findOne({ email });
if (user) {
req.session.challenge = Buffer.from(authnOptions.challenge);
req.session.userHandle = user.id;
authnOptions.challenge = Buffer.from(authnOptions.challenge);
authnOptions.rawId = user.credentialId;
res.json(authnOptions);
} else {
res.status(404).json({ error: "User does not exist." });

if (!user) {
return res.status(401).json({ error: "User does not exist." });
}

if (user.status !== "active") {
return res.status(401).json({ error: "Please activate your account first." });
}

req.session.challenge = Buffer.from(authnOptions.challenge);
req.session.userHandle = user.id;
authnOptions.challenge = Buffer.from(authnOptions.challenge);
authnOptions.rawId = user.credentialId;
res.json(authnOptions);
});

router.post("/authenticate", async (req, res) => {
const { credential, authDuration, password, email, method } = req.body;

const user = await User.findOne({ email });
console.log("🚀 ~ file: register.js ~ line 138 ~ router.post ~ user", user);

if (!user) {
return res.status(401).json({ error: "Incorrect login details" });
}

if (user.status !== "active") {
return res.status(401).json({ error: "Please activate your account first." });
}

if (method === "password") {
try {
const user = await User.findOne({ email });
if (user) {
if (verifyPassword(user, password)) {
return res.status(200).json({ status: "ok" });
} else return res.status(401).json({ error: "Incorrect login details" });
if (verifyPassword(user, password)) {
return res.status(200).json({ status: "ok" });
} else return res.status(401).json({ error: "Incorrect login details" });
} catch (err) {
return res.status(500).json({ error: "Server error: " + err.message });
}
}

credential.rawId = new Uint8Array(Buffer.from(credential.rawId, "base64")).buffer;

const challenge = new Uint8Array(req.session.challenge.data).buffer;
const { publicKey, prevCounter } = req.session;

if (publicKey === "undefined" || prevCounter === undefined) {
console.log("Not found");
res.status(404).json({ error: "Credential not found" });
} else {
const assertionExpectations = {
challenge,
origin: config.siteUrl,
factor: "either",
publicKey,
prevCounter,
userHandle: new Uint8Array(Buffer.from(req.session.userHandle, "base64")).buffer,
};
try {
await fido.assertionResult(credential, assertionExpectations);
await User.findOneAndUpdate({ email: email }, { authDuration: authDuration });
res.json({ status: "ok" });
} catch (e) {
console.log(e);
res.status(500).json({ error: "Failed due internal server error" });
credential.rawId = new Uint8Array(Buffer.from(credential.rawId, "base64")).buffer;
const challenge = new Uint8Array(req.session.challenge.data).buffer;
const { publicKey, prevCounter } = req.session;
if (publicKey === "undefined" || prevCounter === undefined) {
res.status(404).json({ error: "Credential not found" });
} else {
const assertionExpectations = {
challenge,
origin: config.siteUrl,
factor: "either",
publicKey,
prevCounter,
userHandle: new Uint8Array(Buffer.from(req.session.userHandle, "base64")).buffer,
};
try {
await fido.assertionResult(credential, assertionExpectations);
await User.findOneAndUpdate({ email: email }, { authDuration: authDuration });
res.json({ status: "ok" });
} catch (e) {
res.status(500).json({ error: "Failed due internal server error" });
}
}
}
});
@@ -0,0 +1,40 @@
import express from "express";
// import cfg from "config";
import User from "../models/user.js";

// const mode = process.env.NODE_ENV || "dev";
// const config = cfg.get(mode);
const router = express.Router();

router.post("/registration", async (req, res) => {
const { email, registrationTimings } = req.body;
try {
await User.findOneAndUpdate({ email: email }, { registrationTimings: registrationTimings });
return res.status(200).json({ status: "Success" });
} catch (err) {
return res.status(500).json({ error: "Server error: " + err });
}
});

router.post("/fido2-authentication", async (req, res) => {
const { email, fido2AuthTimings } = req.body;
try {
await User.findOneAndUpdate({ email: email }, { fido2AuthTimings });
return res.status(200).json({ status: "Success" });
} catch (err) {
return res.status(500).json({ error: "Server error: " + err });
}
});

router.post("/pw-authentication", async (req, res) => {
const { email, passwordAuthTimings } = req.body;
try {
await User.findOneAndUpdate({ email: email }, { passwordAuthTimings });
return res.status(200).json({ status: "Success" });
} catch (err) {
return res.status(500).json({ error: "Server error: " + err });
}
});


export default router;
@@ -12,8 +12,17 @@ router.post("/verify-account", async (req, res) => {
try {
const verificationCode = await AccountVerificationCode.findOne({ code: secretCode });
const user = await User.findOne({ email: email });
if ((user && verificationCode) && verificationCode.email === user.email) {
await User.findOneAndUpdate({ email: email }, { status: "active" });
if (user && verificationCode && verificationCode.email === user.email) {
await User.findOneAndUpdate(
{ email: email },
{
status: "active",
registrationTimings: {
...user.registrationTimings,
verificationDuration: (Date.now() - user.registrationTimings.regEndTime) / 1000,
},
}
);
return res.status(200).json({ status: "Success" });
} else {
return res.status(401).json({ error: "Incorrect verification code" });
@@ -11,6 +11,7 @@ import errorHandler from "errorhandler";
import connectMongo from "connect-mongo";
import registerRoutes from "./routes/register.js";
import verificationRoutes from "./routes/verification.js";
import timingRoutes from "./routes/timings.js";
import dbConnection from "./helpers/db.js";

const app = express();
@@ -74,7 +75,7 @@ app.use(errorHandler());

app.use("/api/v1", registerRoutes);
app.use("/api/v1/verification", verificationRoutes);

app.use("/api/v1/timing", timingRoutes)
app.listen(process.env.PORT || config.port, () => {
console.log(chalk.yellow("......................................."));
console.log(chalk.green(config.name));

0 comments on commit 1231f36

Please sign in to comment.