diff --git a/config/dev.json b/config/dev.json
index a5de5d0..b665c8d 100644
--- a/config/dev.json
+++ b/config/dev.json
@@ -3,7 +3,7 @@
"name": "WebAuthn UX Test API - Dev Mode",
"port": 4000,
"mode": "development",
- "siteUrl": "https://localhost:3000",
+ "siteUrl": "https://localhost:3000/",
"protocol": "http",
"serverUrl": "localhost"
}
diff --git a/config/production.json b/config/production.json
index 5e22cd5..2785b22 100644
--- a/config/production.json
+++ b/config/production.json
@@ -3,7 +3,7 @@
"name": "WebAuthn UX Test API - Prod Mode",
"mode": "production",
"protocol": "https",
- "siteUrl": "https://web-authn.studio",
- "serverUrl": "api.web-authn.studio"
+ "siteUrl": "https://fido2.app",
+ "serverUrl": "api.fido2.app"
}
}
\ No newline at end of file
diff --git a/helpers/emailTemplates.js b/helpers/emailTemplates.js
new file mode 100644
index 0000000..6f4a695
--- /dev/null
+++ b/helpers/emailTemplates.js
@@ -0,0 +1,7 @@
+export const accountVerificationTemplate = (url) => ({
+ subject: `🎉 Successfully created a new account on Fakebook`,
+ bodyHtml: `
+
Thanks for helping me with the study 😎
+ Verify your account by clicking here
+ `
+});
\ No newline at end of file
diff --git a/helpers/mailer.js b/helpers/mailer.js
new file mode 100644
index 0000000..b8973ae
--- /dev/null
+++ b/helpers/mailer.js
@@ -0,0 +1,46 @@
+/**
+ * Mailer service to handle email sending
+ * @module service/mailer
+ */
+
+import nodemailer from "nodemailer";
+
+/**
+ * Sends an email
+ * @param {Array} recipients email addresses of recipients
+ * @param {String} subject email subject
+ * @param {String} bodyHtml email body
+ * @returns {Object} status of the email
+ */
+const mailTo = async (recipients, { subject, bodyHtml }) => {
+ const transporter = nodemailer.createTransport({
+ host: "mail.privateemail.com",
+ port: 465,
+ auth: {
+ user: process.env.MAILER_USER,
+ pass: process.env.MAILER_PASSWORD,
+ },
+ });
+ const data = {
+ from: process.env.MAILER_USER,
+ to: recipients,
+ subject: subject,
+ html: bodyHtml,
+ };
+ return await sendEmail(transporter, data);
+};
+
+/**
+ * Wrapper for sending emails that returns a promise
+ * @param {Object} transporter Nodemailer transporter object
+ * @param {Object} data data to be send
+ */
+const sendEmail = async (transporter, data) =>
+ new Promise((resolve, reject) => {
+ transporter.sendMail(data, (err, info) => {
+ if (err) reject(err);
+ resolve(info);
+ });
+ });
+
+export default mailTo;
diff --git a/models/accountVerificationCode.js b/models/accountVerificationCode.js
new file mode 100644
index 0000000..61bbfe1
--- /dev/null
+++ b/models/accountVerificationCode.js
@@ -0,0 +1,22 @@
+import mongoose from "../helpers/db.js";
+
+const AccountVerificationCode = mongoose.model(
+ "AccountVerificationCode",
+ new mongoose.Schema({
+ email: {
+ type: String,
+ required: true,
+ },
+ code: {
+ type: String,
+ required: true,
+ },
+ dateCreated: {
+ type: Date,
+ default: Date.now(),
+ expires: 600,
+ },
+ })
+);
+
+export default AccountVerificationCode;
diff --git a/models/user.js b/models/user.js
index 95b7be5..eaeaa9b 100644
--- a/models/user.js
+++ b/models/user.js
@@ -34,6 +34,10 @@ const User = mongoose.model(
},
userAgent: {
type: Object
+ },
+ status: {
+ type: String,
+ default: 'pending'
}
})
);
diff --git a/package-lock.json b/package-lock.json
index b30a3f4..5d0bf57 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2128,6 +2128,11 @@
"webcrypto-core": "^0.1.27"
}
},
+ "nodemailer": {
+ "version": "6.4.17",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz",
+ "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ=="
+ },
"nodemon": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
diff --git a/package.json b/package.json
index f434733..1e96693 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"glob": "^7.1.6",
"helmet": "^4.4.1",
"mongoose": "^5.11.12",
+ "nodemailer": "^6.4.17",
"nodemon": "^2.0.7",
"xss-clean": "^0.1.1"
},
diff --git a/routes/register.js b/routes/register.js
index d1c4eb6..4c36b3e 100644
--- a/routes/register.js
+++ b/routes/register.js
@@ -6,6 +6,9 @@ import bcrypt from "bcrypt";
import cfg from "config";
import User from "../models/user.js";
import { verifyPassword } from "../helpers/utils.js";
+import { accountVerificationTemplate } from "../helpers/emailTemplates.js";
+import mailTo from "../helpers/mailer.js";
+import AccountVerificationCode from "../models/accountVerificationCode.js";
const mode = process.env.NODE_ENV || "dev";
const config = cfg.get(mode);
@@ -81,6 +84,7 @@ router.post("/register", async (req, res) => {
req.session.publicKey = regResult.authnrData.get("credentialPublicKeyPem");
req.session.prevCounter = regResult.authnrData.get("counter");
const hash = bcrypt.hashSync(password, 10);
+
const user = await User.create({
id: req.session.userHandle,
credentialId: base64RawId,
@@ -91,10 +95,22 @@ router.post("/register", async (req, res) => {
regDuration: regDuration,
userAgent: userAgent,
});
-
user.save();
- console.log("Created new account for: ", email);
+ const secretVerificationCode = crypto.randomBytes(32).toString("hex")
+ const verificationCode = await AccountVerificationCode.create({
+ email: user.email,
+ code: secretVerificationCode,
+ });
+
+ verificationCode.save();
+ console.log("Created new account for: ", email);
+ await mailTo(
+ [email],
+ accountVerificationTemplate(
+ `${config.siteUrl}verification/verify-account/${user.email}/${secretVerificationCode}`
+ )
+ );
res.json({ status: "ok" });
} catch (e) {
console.log("error", e);
@@ -125,7 +141,6 @@ router.post("/authenticate", async (req, res) => {
const user = await User.findOne({ email });
if (user) {
if (verifyPassword(user, password)) {
- console.log("All good");
return res.status(200).json({ status: "ok" });
} else return res.status(401).json({ error: "Incorrect login details" });
} else return res.status(401).json({ error: "Incorrect login details" });
diff --git a/routes/verification.js b/routes/verification.js
new file mode 100644
index 0000000..7a544aa
--- /dev/null
+++ b/routes/verification.js
@@ -0,0 +1,26 @@
+import express from "express";
+// import cfg from "config";
+import User from "../models/user.js";
+import AccountVerificationCode from "../models/accountVerificationCode.js";
+
+const mode = process.env.NODE_ENV || "dev";
+// const config = cfg.get(mode);
+const router = express.Router();
+
+router.post("/verify-account", async (req, res) => {
+ const { email, secretCode } = req.body;
+ 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" });
+ return res.status(200).json({ status: "Success" });
+ } else {
+ return res.status(401).json({ error: "Incorrect verification code" });
+ }
+ } catch (err) {
+ return res.status(500).json({ error: "Server error: " + err });
+ }
+});
+
+export default router;
diff --git a/server.js b/server.js
index dda095e..c140c42 100644
--- a/server.js
+++ b/server.js
@@ -10,6 +10,7 @@ import session from "express-session";
import errorHandler from "errorhandler";
import connectMongo from "connect-mongo";
import registerRoutes from "./routes/register.js";
+import verificationRoutes from "./routes/verification.js";
import dbConnection from "./helpers/db.js";
const app = express();
@@ -39,7 +40,6 @@ app.use(
})
);
-
app.use(
bodyParser.urlencoded({
extended: false,
@@ -73,6 +73,7 @@ app.use(
app.use(errorHandler());
app.use("/api/v1", registerRoutes);
+app.use("/api/v1/verification", verificationRoutes);
app.listen(process.env.PORT || config.port, () => {
console.log(chalk.yellow("......................................."));