Password-less authentication in Node JS, Mongo DB
In this tutorial, we will teach you how to develop a password-less authentication system in Node JS and Mongo DB. We will create a simple form with just one field, email. The user will enter his email address and hit submit. As the form submits, we are going to send him an email with a verification code and also redirect him to a new page to enter that verification code. As soon as he enters the verification code and hit submit, we will check that code against that email address, if verified, then we will show him a success message. Otherwise, we will show him an error message. You can write your own authentication logic in either JWT or in sessions.
Video tutorial:
Let’s get started – Password-less authentication
First, you need to create an empty folder for your project. Then open the command prompt or terminal in that folder and run the following command:
npm init
It will ask some configuration questions, you can press enter for all to set the default values. Then run the following command to install the required modules:
npm install express http mongodb nodemailer express-formidable ejs
Then create a file named server.js and run the following command to start the server:
nodemon server.js
Write the following code in server.js:
server.js
const express = require("express")
const app = express()
const http = require("http").createServer(app)
const expressFormidable = require("express-formidable")
app.use(expressFormidable())
// we will be using EJS for templating
app.set("view engine", "ejs")
const mongodb = require("mongodb")
const mongoClient = mongodb.MongoClient
const nodemailer = require("nodemailer")
const port = process.env.PORT || 3000
http.listen(port, function () {
console.log("Server started at: " + port)
mongoClient.connect("mongodb://localhost:27017", function (error, client) {
if (error) {
console.error(error)
return
}
const db = client.db("password_less_authentication")
console.log("Database connected")
// [login route goes here]
app.get("/", function (request, result) {
result.render("index")
})
})
})
This will first include all the modules and set the templating engine to EJS. We will be using express-formidable middleware to handle input values from HTML form. Starts the HTTP server at port 3000. If you are on a production, then process.env.PORT will get the port as per your hosting server. Open the URL http://localhost:3000/ in your browser. You might see an error that “the index.ejs is not found in views directory”. So go ahead and create a folder named “views” at the root of your project. Inside this folder, create a file named “index.ejs”. Following will be the content of that file:
index.ejs
<form method="POST" action="/login">
<input type="email" name="email" placeholder="Enter email" />
<input type="submit" value="Login" />
</form>
Refresh your page and now you will see a simple email input field and a submit button. User does not have to remember the password, an email will automatically be sent to him with a verification code. This is the beauty of password-less authentication system. When this form submits, it redirects to the “/login” POST route. So we need to create that route in our “server.js” file at the place of [login route goes here] section:
// server.js
app.post("/login", async function (request, result) {
const email = request.fields.email
const hash = Math.floor(100000 + Math.random() * 900000)
const user = await db.collection("users").findOne({
email: email
})
if (user == null) {
await db.collection("users").insertOne({
email: email,
hash: hash
})
} else {
await db.collection("users").findOneAndUpdate({
_id: user._id
}, {
$set: {
hash: hash
}
})
}
const transport = nodemailer.createTransport({
host: "mail.yourdomain.com",
port: 465,
secure: true,
auth: {
user: "your email address",
pass: "your password"
}
})
const info = await transport.sendMail({
from: "Your email address",
to: email,
subject: "Verification",
text: "Your verification code is " + hash,
html: "Your verification code is <b>" + hash + "</b>"
})
console.log(info)
result.render("verification", {
email: email
})
})
// [verify route goes here]
This will first get the email address from the form input field. Then it generates a random number of 6 digits, we will send this number in the email as a verification code. Then it creates a new user in the database if the user with that email address does not exist already. If the user already exists, then it will simply update the hash (verification code) of that user. And finally, it will send an email to the user with a verification code and display him a page to enter the verification code.
Make sure to change your domain name, email account, and password associated with that domain from where you want to send an email. If you refresh the page now, enter your email, and hit submit, you will be given an error that the view “verification” is not found. But you will receive an email with a verification code. Then we need to create a file named “verification.ejs” inside the “views” folder. Following will be the content of this file:
verification.ejs
<form method="POST" action="/verify">
<input type="hidden" name="email" value="<%= email %>" />
<input type="text" name="hash" placeholder="Enter verification code" />
<input type="submit" value="Verify" />
</form>
This will create a hidden input field for email. The email variable is sent from the server.js file in the previous step. A text field to enter the verification code from the email and a submit button. Then we need to create a POST route in the server.js that will handle this request. So write the following code in server.js file in [verify route goes here] section:
// server.js
app.post("/verify", async function (request, result) {
const email = request.fields.email
const hash = request.fields.hash
const user = await db.collection("users").findOne({
$and: [{
email: email
}, {
hash: parseInt(hash)
}]
})
if (user == null) {
result.send("Not logged in")
return
}
// do your login logic here
result.send("Logged in")
})
It simply checks if the user with that email address has the same verification code as entered in the text field. If the verification code is correct, then it displays a success message, otherwise, it displays an error message. In the success message place, you can write your own logic to do the authentication. Check out our tutorial to do the authentication via JWT token.
That’s how you can create a password-less authentication system in Node JS and Mongo DB. If you face any problems in following this, kindly do let me know. After downloading the following files, kindly run the command “npm update” at the root.