Easy Node Authentication: Twitter

Free Course

Build Your First Node.js Website

Node is a powerful tool to get JavaScript on the server. Use Node to build a great website.

Welcome to Part 3 of our Easy Node Authentication series. We will be using the foundation of that tutorial to use Twitter authentication with our application. We already have a good application structure for our application packages, user model, application setup and views.

node-auth-twitter Since we set up our application to be as clean and efficient as possible, we will only have to create our Twitter application and add to 4 different files:

Creating the Twitter App and App ID and Secret config/auth.js
Configuring the Passport Twitter Strategy config/passport.js
Generating Routes app/routes.js
Updating Views views/

Since we are already familiar with code organization (the first tutorial) and where we need to add code to authenticate with a social network (the second tutorial), we’ll jump right into configuring our Passport Twitter Strategy.

Authenticating with Twitter using Passport

Creating Our Twitter Application

Let’s get the easiest part out of the way and create our Twitter application over at Twitter Developers. We will need the consumer key, consumer secret, and callback URL. twitter-auth-secrets Callback URL: Using http://localhost:8080/auth/twitter/callback wasn’t working for me. Twitter liked http://127.0.0.1:8080/auth/twitter/callback however so that’s what I used.

Let’s add our consumer key and our consumer secret to our auth.js file so that our application knows the secrets it needs to authenticate with Twitter.

// config/auth.js

// expose our config directly to our application using module.exports
module.exports = {

    'facebookAuth' : {
        'clientID'      : 'your-secret-clientID-here', // your App ID
        'clientSecret'  : 'your-client-secret-here', // your App Secret
        'callbackURL'   : 'http://localhost:8080/auth/facebook/callback'
    },

    'twitterAuth' : {
        'consumerKey'       : 'your-consumer-key-here',
        'consumerSecret'    : 'your-client-secret-here',
        'callbackURL'       : 'http://localhost:8080/auth/twitter/callback'
    },

    'googleAuth' : {
        'clientID'      : 'your-secret-clientID-here',
        'clientSecret'  : 'your-client-secret-here',
        'callbackURL'   : 'http://localhost:8080/auth/google/callback'
    }

};

Fill in your Twitter key and secret and let’s move on.

Configuring Passport’s Twitter Strategy config/passport.js

We’ll make this short and sweet. I’ll add comments for where our old code went for local and Facebook authentication.

// config/passport.js

// load all the things we need
var LocalStrategy    = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
var TwitterStrategy  = require('passport-twitter').Strategy;

// load up the user model
var User       = require('../app/models/user');

// load the auth variables
var configAuth = require('./auth');

module.exports = function(passport) {

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });
    
    // code for login (use('local-login', new LocalStategy))
    // code for signup (use('local-signup', new LocalStategy))
    // code for facebook (use('facebook', new FacebookStrategy))

    // =========================================================================
    // TWITTER =================================================================
    // =========================================================================
    passport.use(new TwitterStrategy({

        consumerKey     : configAuth.twitterAuth.consumerKey,
        consumerSecret  : configAuth.twitterAuth.consumerSecret,
        callbackURL     : configAuth.twitterAuth.callbackURL

    },
    function(token, tokenSecret, profile, done) {

        // make the code asynchronous
    // User.findOne won't fire until we have all our data back from Twitter
        process.nextTick(function() {

            User.findOne({ 'twitter.id' : profile.id }, function(err, user) {

                // if there is an error, stop everything and return that
                // ie an error connecting to the database
                if (err)
                    return done(err);

                // if the user is found then log them in
                if (user) {
                    return done(null, user); // user found, return that user
                } else {
                    // if there is no user, create them
                    var newUser                 = new User();

                    // set all of the user data that we need
                    newUser.twitter.id          = profile.id;
                    newUser.twitter.token       = token;
                    newUser.twitter.username    = profile.username;
                    newUser.twitter.displayName = profile.displayName;

                    // save our user into the database
                    newUser.save(function(err) {
                        if (err)
                            throw err;
                        return done(null, newUser);
                    });
                }
            });

    });

    }));

};


Twitter Passport Profile

This is where the Twitter Strategy differs from the Facebook and Google Strategies for Passport.

Passport standardizes the information that is sent back in profile. To see exactly how they lay out their information, see their User Profile docs. As you can see, you can retrieve information like displayName, name, and emails (in an array).

In my experience, using Passport with Twitter returns some different information. The Twitter information that gets passed back looks like:


    {
        id: '130668248',
        username: 'sevilayha',
        displayName: 'Chris Sevilleja',
        photos: [{ value: 'https://pbs.twimg.com/profile_images/805686037/IMG_0075_normal.JPG' }]
    }

Getting Email from Twitter: You might ask why we don’t get a user’s email back from Twitter. In Twitter’s eyes, accounts are not really tied to a real email like Facebook or Google are and is less firmly attached to a real-world identity. Email is seen more as a formality to Twitter and accounts are more directly tied to their username.

Now that we have our application and our Twitter Passport Strategy, we just need to add in our Twitter auth routes and show our user on the profile page.

Generating Our Routes app/routes.js

Just like our Facebook authentication, we will need just two routes.

  • /auth/twitter: Send our user to Twitter to authenticate
  • /auth/twitter/callback: Twitter sends our user back to our application here with token and profile information
// app/routes.js

module.exports = function(app, passport) {

    // route for home page
    app.get('/', function(req, res) {
        res.render('index.ejs'); // load the index.ejs file
    });

    // route for login form
    // route for processing the login form
    // route for signup form
    // route for processing the signup form

    // route for showing the profile page
    app.get('/profile', isLoggedIn, function(req, res) {
        res.render('profile.ejs', {
            user : req.user // get the user out of session and pass to template
        });
    });

        // route for logging out
    app.get('/logout', function(req, res) {
        req.logout();
        res.redirect('/');
    });

    // facebook routes

    // =====================================
    // TWITTER ROUTES ======================
    // =====================================
    // route for twitter authentication and login
    app.get('/auth/twitter', passport.authenticate('twitter'));

    // handle the callback after twitter has authenticated the user
    app.get('/auth/twitter/callback',
        passport.authenticate('twitter', {
            successRedirect : '/profile',
            failureRedirect : '/'
        }));

};

// route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {

    // if user is authenticated in the session, carry on
    if (req.isAuthenticated())
        return next();

    // if they aren't redirect them to the home page
    res.redirect('/');
}

Our routes are very simple. Just authenticate and handle the callback.

Showing Our User index.ejs, profile.ejs

Now that our user is authenticated and saved to the database, they will be redirected to our profile page. The last thing to do is show that user’s Twitter information.

Showing the Login Button

We’ll add this button to our index.ejs file.

<!-- views/index.ejs -->

<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
    <style>
        body        { padding-top:80px; }
    </style>
</head>
<body>
<div class="container">

    <div class="jumbotron text-center">
        <h1><span class="fa fa-lock"></span> Node Authentication</h1>

        <p>Login or Register with:</p>

        <a href="/auth/twitter" class="btn btn-info"><span class="fa fa-twitter"></span> Twitter</a>

    </div>

</div>
</body>
</html>   

node-auth-twitter Now we have our login button for Twitter. It will point to our /auth/twitter route and then once a user successfully authenticates, they’ll be pushed to the profile page.

The Profile Page views/profile.ejs

After a user registers with Twitter, here they are in our database.

node-auth-twitter-database Let’s show their profile page.

<!-- views/profile.ejs -->
<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
    <style>
        body        { padding-top:80px; word-wrap:break-word; }
    </style>
</head>
<body>
<div class="container">

    <div class="page-header text-center">
        <h1><span class="fa fa-anchor"></span> Profile Page</h1>
        <a href="/logout" class="btn btn-default btn-sm">Logout</a>
    </div>

    <div class="row">

        <!-- TWITTER INFORMATION -->
        <div class="col-sm-6">
            <div class="well">
                <h3 class="text-info"><span class="fa fa-twitter"></span> Twitter</h3>

                    <p>
                        <strong>id</strong>: <%= user.twitter.id %><br>
                        <strong>token</strong>: <%= user.twitter.token %><br>
                        <strong>username</strong>: <%= user.twitter.username %><br>
                        <strong>displayName</strong>: <%= user.twitter.displayName %>
                    </p>

            </div>
        </div>

    </div>

</div>
</body>
</html>

Here is their profile page.

node-auth-twitter-profile

Bam!

We’re all done. We have configured Passport for Twitter, added our routes, authenticated with Twitter, and shown our user’s profile.

In the next article, we’ll deal with Google authentication. Then we move on to the big one. Linking all these social accounts and local account together into one gigantic user profile.

Chris Sevilleja

Co-founder of Scotch.io. Slapping the keyboard until something good happens.