Guide: Email Verification (with JWT & Sendgrid) using MERN stack with Redux — 2019

Arthur Truong
5 min readDec 18, 2018

--

Hey all,

There weren’t many guides on how to create email verifications with MERN so I decided I would create this guide to show you all how I did it and maybe it can help someone else that had the same problem I had.

This post will cover

  • How to setup SendGrid
  • Setting it up with redux
  • Backend logic (we’ll use postman to test our api’s)
  • Use JWT to create our temporary token

Let’s implement emails using sendgrid. Make sure that you create an account with sendgrid — > https://sendgrid.com/. Below is the code to get it working

const nodemailer = require("nodemailer");const sgTransport = require("nodemailer-sendgrid-transport");
const options = {
auth: {api_user: {YOUR SENDGRID USERNAME},api_key: {YOUR SENDGRID PASSWORD}}};const client = nodemailer.createTransport(sgTransport(options));

Add this to your api’s if you want to send an email. You can customize it however you like. Also to note that if for some reason HTML is not parsed, the default message is the text.

const emailActivate = {from: "LocalHost Staff, staff@localhost.com",to: user.email,subject: "LocalHost Account Activated",text: `Hello ${user.name}, Your account has been successfully activated!`,html: `Hello<strong> ${user.name}</strong>,<br><br>Your account has been successfully activated!`};

And after that add this to send the email. I’ve also console logged it to make sure that we get a message in the terminal once email has been sent.

// Send e-mail object to userclient.sendMail(emailActivate, function(err, info) {if (err) {console.log(err);} else {console.log("Activiation Message Confirmation -  : " + info.response);}});res.json({succeed: true,message: "User has been successfully activated"});

Okay now that we know how to send emails, let’s do our verifications. Firstly we want to make sure that when a user registers an account, that it creates a temporary token which the user will later use to activate by clicking the link. When the user clicks the link, it will send a PUT request and change it in database.

In your user model, you want to make sure you add temporarytoken as a string and active as a boolean. The default value will be false because the user isn’t active yet. Once the user clicks the link we’ll change default to true.

temporarytoken: {type: String,required: true},active: {type: Boolean,required: true,default: false}

Under your /register api we’re going to generate a token using JWT. You can set the expiry date anytime (this saves us time and we don’t need to add anymore logic because JWT does it for us) yay for JWT.

const newUser = new User({name: req.body.name,email: req.body.email,number: req.body.number,company: req.body.company,password: req.body.password,temporarytoken: jwt.sign(payload, keys.secretOrKey, {expiresIn: 12000})

Okay so now when a user registers an account. It will create a temporary token. Lets check this with postman.

You can see here that the registered user is assigned a temporary token. Now we need to verify the token and change active to true once verified.

The below code explains how I did it.

// Route to activate the user's accountrouter.put("/verify/:token", (req, res) => {User.findOne({ temporarytoken: req.params.token }, (err, user) => {if (err) throw err; // Throw error if cannot loginconst token = req.params.token; // Save the token from URL for verificationconsole.log("the token is", token);// Function to verify the user's tokenjwt.verify(token, keys.secretOrKey, (err, decoded) => {if (err) {res.json({ success: false, message: "Activation link has expired." }); // Token is expired} else if (!user) {res.json({ success: false, message: "Activation link has expired." }); // Token may be valid but does not match any user in the database} else {user.temporarytoken = false; // Remove temporary tokenuser.active = true; // Change account status to Activated// Mongoose Method to save user into the databaseuser.save(err => {if (err) {console.log(err); // If unable to save user, log error info to console/terminal} else {// If save succeeds, create e-mail objectconst emailActivate = {from: "Localhost Staff, staff@localhost.com",to: user.email,subject: "Localhost Account Activated",text: `Hello ${user.name}, Your account has been successfully activated!`,html: `Hello<strong> ${user.name}</strong>,<br><br>Your account has been successfully activated!`};// Send e-mail object to userclient.sendMail(emailActivate, function(err, info) {if (err) {console.log(err);} else {console.log("Activiation Message Confirmation -  : " + info.response);}});res.json({succeed: true,message: "User has been successfully activated"});}});}});});});

Now test it with postman and it should work.

Lets move onto the frontend client now.

Make sure that you create a token component and add it to your AppJS file

import Token from "./components/auth/Token";<Route path="/verify/:token" component={Token} />

Your token component should look similar like this

import React, { Component } from "react";import { connect } from "react-redux";import { getVerifyUser } from "../../actions/tokenAction";class Token extends Component {constructor(props) {super(props);}// called before initial rendercomponentWillMount() {this.props.getVerifyUser(this.props.match.params.token);console.log("token", this.props.match.params.token);}render() {return (<div><h3 className="lead text-muted text-center">Your activation token has been confirmed, you can now sign in.</h3></div>);}}export default connect(null,{ getVerifyUser })(Token);

Since we’re using redux, we’re going to fetch the api with an axios request

export function getVerifyUser(token) {return dispatch => {console.log("put request");axios.put(`/api/users/verify/${token}`).then(res => {}).catch(err => console.log(err));};

Now that should work! Once the user clicks on the link, we will send a PUT request through expressJS which will change active to true in the database. This will activate the account and the user can now log in.

Part 2 will be how to resend a temporary token to the user once the JWT token expires.

--

--

Arthur Truong

I’m a software engineer based in Sydney. I’m currently the CTO and co-founder of a property platform called osdoro.com.sg