How to confirm email address using express/node?

node.jsExpress

node.js Problem Overview


I'm trying to build verification of email address for users, to verify their email is real. What package should I use to confirm the email address of the user? So far Im using mongoose and express

Code Example

var UserSchema = new mongoose.Schema({
    email: { type: String, unique: true, lowercase: true }
    password: String
});

var User = mongoose.model('User', UserSchema);

app.post('/signup', function(req, res, next) {
   // Create a new User
   var user = new User();
   user.email = req.body.email;
   user.password = req.body.password;
   user.save();
});

In the app.post codes, how do i confirm the email address of the user?

node.js Solutions


Solution 1 - node.js

What you're looking for is called "account verification" or "email verification". There are plenty of Node modules that can perform this, but the principle goes like this:

  • Your User model should have an active attribute that is false by default
  • When the user submits a valid signup form, create a new User (who's active will be false initially)
  • Create a long random string (128 characters is usually good) with a crypto library and store it in your database with a reference to the User ID
  • Send an email to the supplied email address with the hash as part of a link pointing back to a route on your server
  • When a user clicks the link and hits your route, check for the hash passed in the URL
  • If the hash exists in the database, get the related user and set their active property to true
  • Delete the hash from the database, it is no longer needed

Your user is now verified.

Solution 2 - node.js

var express=require('express');
var nodemailer = require("nodemailer");
var app=express();
/*
    Here we are configuring our SMTP Server details.
    STMP is mail server which is responsible for sending and recieving email.
*/
var smtpTransport = nodemailer.createTransport("SMTP",{
    service: "Gmail",
    auth: {
        user: "Your Gmail ID",
        pass: "Gmail Password"
    }
});
var rand,mailOptions,host,link;
/*------------------SMTP Over-----------------------------*/

/*------------------Routing Started ------------------------*/

app.get('/',function(req,res){
    res.sendfile('index.html');
});
app.get('/send',function(req,res){
        rand=Math.floor((Math.random() * 100) + 54);
    host=req.get('host');
    link="http://"+req.get('host')+"/verify?id="+rand;
    mailOptions={
        to : req.query.to,
        subject : "Please confirm your Email account",
        html : "Hello,<br> Please Click on the link to verify your email.<br><a href="+link+">Click here to verify</a>" 
    }
    console.log(mailOptions);
    smtpTransport.sendMail(mailOptions, function(error, response){
     if(error){
            console.log(error);
        res.end("error");
     }else{
            console.log("Message sent: " + response.message);
        res.end("sent");
         }
});
});

app.get('/verify',function(req,res){
console.log(req.protocol+":/"+req.get('host'));
if((req.protocol+"://"+req.get('host'))==("http://"+host))
{
    console.log("Domain is matched. Information is from Authentic email");
    if(req.query.id==rand)
    {
        console.log("email is verified");
        res.end("<h1>Email "+mailOptions.to+" is been Successfully verified");
    }
    else
    {
        console.log("email is not verified");
        res.end("<h1>Bad Request</h1>");
    }
}
else
{
    res.end("<h1>Request is from unknown source");
}
});

/*--------------------Routing Over----------------------------*/

app.listen(3000,function(){
    console.log("Express Started on Port 3000");
});

Follow the code example, you can use nodemailer to send the link, and then verify it. Here is a link: https://codeforgeek.com/2014/07/node-email-verification-script/

Solution 3 - node.js

Step 1:

User Model

var userSchema = new mongoose.Schema({
    email: { type: String, unique: true },
    isVerified: { type: Boolean, default: false },
    password: String,
  });

Token Model

const tokenSchema = new mongoose.Schema({
    _userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
    token: { type: String, required: true },
    expireAt: { type: Date, default: Date.now, index: { expires: 86400000 } }
});

Step 2: Login

exports.login = function(req, res, next) {
    User.findOne({ email: req.body.email }, function(err, user) {
        // error occur
        if(err){
            return res.status(500).send({msg: err.message});
        }
        // user is not found in database i.e. user is not registered yet.
        else if (!user){
            return res.status(401).send({ msg:'The email address ' + req.body.email + ' is not associated with any account. please check and try again!'});
        }
        // comapre user's password if user is find in above step
        else if(!Bcrypt.compareSync(req.body.password, user.password)){
            return res.status(401).send({msg:'Wrong Password!'});
        }
        // check user is verified or not
        else if (!user.isVerified){
            return res.status(401).send({msg:'Your Email has not been verified. Please click on resend'});
        } 
        // user successfully logged in
        else{
            return res.status(200).send('User successfully logged in.');
        }
    });

});

Step 3: Sign Up

exports.signup = function(req, res, next) {
  User.findOne({ email: req.body.email }, function (err, user) {
    // error occur
    if(err){
        return res.status(500).send({msg: err.message});
    }
    // if email is exist into database i.e. email is associated with another user.
    else if (user) {
        return res.status(400).send({msg:'This email address is already associated with another account.'});
    }
    // if user is not exist into database then save the user into database for register account
    else{
        // password hashing for save into databse
        req.body.password = Bcrypt.hashSync(req.body.password, 10);
        // create and save user
        user = new User({ name: req.body.name, email: req.body.email, password: req.body.password });
        user.save(function (err) {
            if (err) { 
              return res.status(500).send({msg:err.message});
            }
            
            // generate token and save
            var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
            token.save(function (err) {
              if(err){
                return res.status(500).send({msg:err.message});
              }

                // Send email (use credintials of SendGrid)
                var transporter = nodemailer.createTransport({ service: 'Sendgrid', auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD } });
                var mailOptions = { from: '[email protected]', to: user.email, subject: 'Account Verification Link', text: 'Hello '+ req.body.name +',\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + user.email + '\/' + token.token + '\n\nThank You!\n' };
                transporter.sendMail(mailOptions, function (err) {
                    if (err) { 
                        return res.status(500).send({msg:'Technical Issue!, Please click on resend for verify your Email.'});
                     }
                    return res.status(200).send('A verification email has been sent to ' + user.email + '. It will be expire after one day. If you not get verification Email click on resend token.');
                });
            });
        });
    }
    
  });

});

Step 4: Verify Account

// It is GET method, you have to write like that
//    app.get('/confirmation/:email/:token',confirmEmail)

exports.confirmEmail = function (req, res, next) {
    Token.findOne({ token: req.params.token }, function (err, token) {
        // token is not found into database i.e. token may have expired 
        if (!token){
            return res.status(400).send({msg:'Your verification link may have expired. Please click on resend for verify your Email.'});
        }
        // if token is found then check valid user 
        else{
            User.findOne({ _id: token._userId, email: req.params.email }, function (err, user) {
                // not valid user
                if (!user){
                    return res.status(401).send({msg:'We were unable to find a user for this verification. Please SignUp!'});
                } 
                // user is already verified
                else if (user.isVerified){
                    return res.status(200).send('User has been already verified. Please Login');
                }
                // verify user
                else{
                    // change isVerified to true
                    user.isVerified = true;
                    user.save(function (err) {
                        // error occur
                        if(err){
                            return res.status(500).send({msg: err.message});
                        }
                        // account successfully verified
                        else{
                          return res.status(200).send('Your account has been successfully verified');
                        }
                    });
                }
            });
        }
        
    });
});

Step 5: Resend Link

exports.resendLink = function (req, res, next) {

    User.findOne({ email: req.body.email }, function (err, user) {
        // user is not found into database
        if (!user){
            return res.status(400).send({msg:'We were unable to find a user with that email. Make sure your Email is correct!'});
        }
        // user has been already verified
        else if (user.isVerified){
            return res.status(200).send('This account has been already verified. Please log in.');
    
        } 
        // send verification link
        else{
            // generate token and save
            var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
            token.save(function (err) {
                if (err) {
                  return res.status(500).send({msg:err.message});
                }
    
                // Send email (use credintials of SendGrid)
                    var transporter = nodemailer.createTransport({ service: 'Sendgrid', auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD } });
                    var mailOptions = { from: '[email protected]', to: user.email, subject: 'Account Verification Link', text: 'Hello '+ user.name +',\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + user.email + '\/' + token.token + '\n\nThank You!\n' };
                    transporter.sendMail(mailOptions, function (err) {
                       if (err) { 
                        return res.status(500).send({msg:'Technical Issue!, Please click on resend for verify your Email.'});
                     }
                    return res.status(200).send('A verification email has been sent to ' + user.email + '. It will be expire after one day. If you not get verification Email click on resend token.');
                });
            });
        }
    });
});

You can take help from this link:https://medium.com/@slgupta022/email-verification-using-sendgrid-in-node-js-express-js-mongodb-c5803f643e09

Solution 4 - node.js

I would like to present a slightly different approach from the ones proposed.

This method does not put the hash into the database (therefore less interaction with it)

You don't need to register the hash in the database. Here's an overview after receiving a registration request:

  1. You encode the user id + registration time
  2. You send the token to the user
  3. When the user triggers his registration request, you decode the token.
  4. Because The decoded token contains the user id + the time, you can mark the user as registered by increasing their role (registered, subscriber, admin, etc.) for instance

Translated into code, you would have something like this:

1- Encode the token

function encodeRegistrationToken()
{
	// jsonweb automatically adds a key that determines the time, but you can use any module
	const jwt = require('jsonwebtoken');

	// The information we need to find our user in the database (not sensible info)
	let info = {id: yourUserId};

	// The hash we will be sending to the user
	const token = jwt.sign(info, "yoursecretkey");
	
	return token;
}

// ... 
let token = encodeRegistrationToken();

2- Send token to the user via any appropriate way

// Your implementation of sending the token
sendTokenToUser(token);

3- Decode the token

function decodeRegistrationToken(token)
{	
	const jwt = require('jsonwebtoken');
	let decoded = jwt.verify(token, "yoursecretkey");

	let userId = decoded.id;

	// Check that the user didn't take too long
	let dateNow = new Date();
	let tokenTime = decoded.iat * 1000;

	// Two hours
    let hours = 2;
	let tokenLife = hours * 60 * 1000;

	// User took too long to enter the code
	if (tokenTime + tokenLife < dateNow.getTime())
	{
	    return {        	
	        expired: true
	    };
	}

	// User registered in time
	return {
	    userID
	};
	
}

4 - Update your database

  • Upgrade the user role to subscriber

or

  • Set their "register" key to true

Quick note: You can further encode the user id when encoding your token if you want (it's easily accessible).

Solution 5 - node.js

I spent a lot of time figuring out the perfect way to send confirmation mail. Here is the method I used.

Libraries

const jwt = require('jsonwebtoken');
const nodemailer = require("nodemailer");

Step 1 Encode the user id in a jwt token with an expiration date

var date = new Date();
var mail = {
            "id": user.id,
            "created": date.toString()
            }

const token_mail_verification = jwt.sign(mail, config.jwt_secret_mail, { expiresIn: '1d' });

var url = config.baseUrl + "verify?id=" + token_mail_verification;

Step 2 Send the token to the user email address using nodemailer library

 let transporter = nodemailer.createTransport({
        name: "www.domain.com",
        host: "smtp.domain.com",
        port: 323,
        secure: false, // use SSL
        auth: {
            user: "[email protected]", // username for your mail server
            pass: "Password", // password
        },

    });

    // send mail with defined transport object
    let info = await transporter.sendMail({
        from: '"NAME" <[email protected]>', // sender address
        to: user.email, // list of receivers seperated by comma
        subject: "Account Verification", // Subject line
        text: "Click on the link below to veriy your account " + url, // plain text body
    }, (error, info) => {

        if (error) {
            console.log(error)
            return;
        }
        console.log('Message sent successfully!');
        console.log(info);
        transporter.close();
    });

Step 3 Accept the verification link

app.get('/verify', function(req, res) {
    token = req.query.id;
    if (token) {
        try {
            jwt.verify(token, config.jwt_secret_mail, (e, decoded) => {
                if (e) {
                    console.log(e)
                    return res.sendStatus(403)
                } else {
                    id = decoded.id;

                
//Update your database here with whatever the verification flag you are using 



                }

            });
        } catch (err) {

            console.log(err)
            return res.sendStatus(403)
        }
    } else {
        return res.sendStatus(403)

    }

})

Step 4 Have a coffee and THANK ME for saving your so much time

PS: This nodemailer SMTP method will even work with your hosting. So no need to go for third party. You can also find ways to use gmail with nodemailer.

Solution 6 - node.js

if you are just testing on your local machine, one simple way of understanding how to do it can be :

Assuming you already know sending mails through nodemailer..

Once user signs up, after storing sign-up data in your database, on your server side take user email from sign-up data received and a random generated number and build a custom url with the address of page where user will be directed after he/she clicks on the link given in mail.

var customUrl = "http://"+ your host + "/" + your verification web-page + "?email=" + userEmail + "&id=" + randomNumber;

An example can be:

var userEmail = [email protected]
var host = localhost:8080
var directWebPage = verifyUserEmail.html
var randomNumber = // generate with math.random() // lets say 111

Putting in above format of customUrl it looks something like this

customUrl:http://localhost:8080/[email protected]&id=111

Save this customUrl somewhere (probably in your database) Now, send an email to user with email body containing this cutomUrl link.

<a href="customUrl">Click to verify your email</a>

When user clicks on the link he/she will be directed to verifyUserEmail.html page and when that happens you can extract the page url containing email and id information

For example in angular I go like this-

var urlVerifyData = $location.url(); or $location.absUrl();

Now extract email form urlVerifyData string using javascript string methods

Request your server with this email and urlVerifyData

Now query your database for this email and verify previously stored customUrl with user's urlVerifyData

If they match, hola ! You got yourself a genuine user !!!

Solution 7 - node.js

    function generateLink() {
        var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        var token = '';
        for (var i = 16; i > 0; --i) {
            var rand = Math.round(Math.random() * (chars.length - 1))
            token += chars[rand];
        }
        var link = "http://localhost" + "/verify?id=" + token;
        return link;
    }

    // npm install @sendGrid/mail --save

    //library for generating link using SendGrid
    const sgMail = require('@sendgrid/mail');
    sgMail.setApiKey("SENDGRID_API_KEY"); //create an account on sendgrid and get an API key

    // generated link is send to the user's email for email verification
    let sendVerifyEmailLink = (req, res) => {
        var link = generateLink();
        const msg = {
            to: '[email protected]',
            from: '[email protected]',
            subject: 'Account Verifictaion',
            text: 'Hello,\n\n' + 'Please verify your account by clicking the link:\/\/\n',
            html: 'Hello,\n\n <br> Please verify your account by clicking the link: \n <br> <strong><a href = ' + link + '>http:\/\/ Click here to verify the given Link </a></strong>.\n .<br>Thanks<br>',
        };
        sgMail.send(msg).then(() => { }, error => {
            console.error(error);

            if (error.response) {
                console.error(error.response.body)
            }
        });
        console.log(msg)
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsinusGobView Question on Stackoverflow
Solution 1 - node.jsSoviutView Answer on Stackoverflow
Solution 2 - node.jsBlackMambaView Answer on Stackoverflow
Solution 3 - node.jsSohan Lal GuptaView Answer on Stackoverflow
Solution 4 - node.jsPatrice ThimotheeView Answer on Stackoverflow
Solution 5 - node.jsSarthak SinghalView Answer on Stackoverflow
Solution 6 - node.jsPiyush UpadhyayView Answer on Stackoverflow
Solution 7 - node.jsAYUSHI AGARWALView Answer on Stackoverflow