const dbConfig = new Config("myDataBase", true, false, '/')
/**
* Creates a Node-json-db JSON storage file
* @param {instance} dbConfig - Node-json-db configuration
*/
const db = new JsonDB(dbConfig);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/api", (req,res) => {
res.json({ message: "Welcome to the two factor authentication exmaple" })
});
app.post("/api/register", (req, res) => {
const id = uuid.v4();
try {
const path = `/user/${id}`;
// Create temporary secret until it it verified
const temp_secret = speakeasy.generateSecret();
// Create user in the database
db.push(path, { id, temp_secret });
// Send user id and base32 key to user
res.json({ id, secret: temp_secret.base32 })
} catch(e) {
console.log(e);
res.status(500).json({ message: 'Error generating secret key'})
}
})
app.post("/api/verify", (req,res) => {
const { userId, token } = req.body;
try {
// Retrieve user from database
const path = `/user/${userId}`;
const user = db.getData(path);
console.log({ user })
const { base32: secret } = user.temp_secret;
const verified = speakeasy.totp.verify({
secret,
encoding: 'base32',
token
});
if (verified) {
// Update user data
db.push(path, { id: userId, secret: user.temp_secret });
res.json({ verified: true })
} else {
res.json({ verified: false})
}
} catch(error) {
console.error(error);
res.status(500).json({ message: 'Error retrieving user'})
};
})
app.post("/api/validate", (req,res) => {
const { userId, token } = req.body;
try {
// Retrieve user from database
const path = `/user/${userId}`;
const user = db.getData(path);
console.log({ user })
const { base32: secret } = user.secret;
// Returns true if the token matches
const tokenValidates = speakeasy.totp.verify({
secret,
encoding: 'base32',
token,
window: 1
});
if (tokenValidates) {
res.json({ validated: true })
} else {
res.json({ validated: false})
}
} catch(error) {
console.error(error);
res.status(500).json({ message: 'Error retrieving user'})
};
})
const port = 9000;
app.listen(port, () => {
console.log(`App is running on PORT: ${port}.`);
});
Next steps
The focus of this article was on implementing the two-factor authentication functionality, mostly on the backend. The entire process is, however, more complex than this. In a normal application, the user would register and choose whether to enable two-factor authentication or not. The next time they log in, we sent their main login identifier, e.g username, to the server to check whether they have two-factor authentication enabled. If they don’t have it enabled, we submit the username and password and sign them in.
Comments
Post a Comment