How to confirm email address using express/node?
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 isfalse
by default - When the user submits a valid signup form, create a new User (who's
active
will befalse
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 totrue
- Delete the hash from the database, it is no longer needed
Your user is now verified.
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
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:
- You encode the user id + registration time
- You send the token to the user
- When the user triggers his registration request, you decode the token.
- 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).
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/