Aiming for elegance, one thought at a time

Adding “verify” URLs to an express.js app to confirm user emails (Secure SPA Part 6)

Posted: May 17th, 2013 | Author: | Filed under: Uncategorized | 3 Comments »

This is the sixth part of a series of blog posts that will walk step-by-step through the process of creating a secure Single Page App (SPA) using node.js, passport.js, and Angular. In Part 5, we sent an email to confirm the users email address. In this instalment, we’ll be wiring up the backend to actually provide a working verification URL.

The verification token

The url we’ll be using will take the form:

http://www.yourhost.com/verify/token-uuid

The bit we’ll be most interested is the token-uuid, which we’ll be using node-uuid to generate. We’ll use a type-4 (random) UUID and we’ll make sure that these tokens expire in a given amount of time. Combine cryptographically strong random UUIDs with a short expiry date and the chance of collisions is practically nil. That said, if you’re worried about collisions, you could either change this code to protect against it, switch to Type 1 UUIDs (making sure each instance of node in your cluster has a unique node ID or swap to something like snowflake or noeqd. Seriously, though: the Type 4 uuid is random enough. Have faith in the maths.

Generating and storing the token

To make sure the tokens expire, we’ll be taking advantage of the TTL capability of mongodb. This is done simply enough: we add the “expires” index option to a “created_at” field when declaring the mongoose schema.

// Verification token model
var verificationTokenSchema = new Schema({
    _userId: {type: ObjectId, required: true, ref: 'User'},
    token: {type: String, required: true},
    createdAt: {type: Date, required: true, default: Date.now, expires: '4h'}
});

Our token is simply a random UUID, but here’s a convenience function to set the token and save the model, and then return the token in a callback.

var uuid = require('node-uuid');
verificationTokenSchema.methods.createVerificationToken = function (done) {
    var verificationToken = this;
    var token = uuid.v4();
    verificationToken.set('token', token);
    verificationToken.save( function (err) {
        if (err) return done(err);
        return done(null, token);
        console.log("Verification token", verificationToken);
    });
};

Create the and export the model:

var verificationTokenModel = mongoose.model('VerificationToken', verificationTokenSchema);
exports.verificationTokenModel = verificationTokenModel;

And finally, a convenience function to verify a user: it takes the token, locates the matching user, and sets “verified” to true.

exports.verifyUser = function(token, done) {
    verificationTokenModel.findOne({token: token}, function (err, doc){
        if (err) return done(err);
        userModel.findOne({_id: doc._userId}, function (err, user) {
            if (err) return done(err);
            user["verified"] = true;
            user.save(function(err) {
                done(err);
            })
        })
    })
}

Creating the token

In our user registration code, we can add the following. This wraps the ‘sendVerificationEmail’ function we created last time.

var verificationToken = new verificationTokenModel({_userId: user._id});
verificationToken.createVerificationToken(function (err, token) {
    if (err) return console.log("Couldn't create verification token", err);
    var message = {
        email: user.email,
        name: user.name,
        verifyURL: req.protocol + "://" + req.get('host') + "/verify/" + token};
    sendVerificationEmail(message, function (error, success) {
        if (error) {
            // not much point in attempting to send again, so we give up
            // will need to give the user a mechanism to resend verification
            console.error("Unable to send via postmark: " + error.message);
            return;
        }
        console.info("Sent to postmark for delivery")
    });
});

Verification middleware

The last remaining step is to create a simple middleware to check the token, and direct them to success and failure pages.

app.get("/verify/:token", function (req, res, next) {
    var token = req.params.token;
    verifyUser(token, function(err) {
        if (err) return res.redirect("verification-failure");
        res.redirect("verification-success");
    });
});

Wrapping up

You’re now sending verification emails and handling verification URLs: nice job! Stay tuned for more node.js, Angular, and SPA security next week!


Sign up for danielstudds.com
* = required field

3 Comments on “Adding “verify” URLs to an express.js app to confirm user emails (Secure SPA Part 6)”

  1. 1 Eric said at 2:56 pm on April 14th, 2014:

    This is a very helpful series. I’m sorry to see that it looks like you never completed it. Keep up the good work

  2. 2 Studds said at 6:30 pm on May 11th, 2014:

    Thanks Eric! Appreciate the feedback. I’ll see if I can find some time to get back to it.

  3. 3 raul said at 2:23 am on July 23rd, 2014:

    Excelent work, thanks for your help


Leave a Reply