Tutorial

Easy Node Authentication: Google

Draft updated on Invalid Date
Default avatar

By Chris on Code

Easy Node Authentication: Google

This tutorial is out of date and no longer maintained.

Note: This article is part of our Easy Node Authentication series.

Introduction

Welcome to Part 4 of our Easy Node Authentication with Passport series. We will be using the foundation of that tutorial to use Google authentication with our application. We already have a good application structure for our application packages, user model, application setup and views. node-auth-google-index. Since we set up our application to be as clean and efficient as possible, we will only have to create our Google application and add to 4 different files:

  • Creating the Google App, Client ID, and Client Secret (config/auth.js)
  • Configuring the Passport Google 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 Google Strategy.

Authenticating with Google using Passport

Creating Our Google Application

The place to create Google applications can be found at their Cloud API Console. Applications can be found under Project > APIs & auth.

Let’s go ahead and create our application with the correct redirect URL: http://localhost:8080/auth/google/callback.

If Google doesn’t like http://localhost:8080, then use http://127.0.0.1:8080.

Let’s add our Client ID and our client secret to our auth.js file so that our application knows the secrets it needs to authenticate with Google.

    // 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'
        }

    };

Customizing Your Application Screen

Google let’s you change the default login screen so that you can customize it with your brand’s logo and text. This can be found on the same page where your client ID and secret are. The option is under Consent Screen.

Here is the customization screen:

With our application ready to go, let’s set up our Passport Google Strategy.

Configuring Passport’s Google Strategy config/passport.js

We will be using the passport-google-oauth package by Jared Hanson so that we can authenticate with OAuth2.

There will be comments as placeholders for the previous article’s strategies.

    // 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;
    var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

    // 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))
        // code for twitter (use('twitter', new TwitterStrategy))

        // =========================================================================
        // GOOGLE ==================================================================
        // =========================================================================
        passport.use(new GoogleStrategy({

            clientID        : configAuth.googleAuth.clientID,
            clientSecret    : configAuth.googleAuth.clientSecret,
            callbackURL     : configAuth.googleAuth.callbackURL,

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

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

                // try to find the user based on their google id
                User.findOne({ 'google.id' : profile.id }, function(err, user) {
                    if (err)
                        return done(err);

                    if (user) {

                        // if a user is found, log them in
                        return done(null, user);
                    } else {
                        // if the user isnt in our database, create a new user
                        var newUser          = new User();

                        // set all of the relevant information
                        newUser.google.id    = profile.id;
                        newUser.google.token = token;
                        newUser.google.name  = profile.displayName;
                        newUser.google.email = profile.emails[0].value; // pull the first email

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

        }));

    };

Now we have the Google Strategy that will search for a user based on their google.id that corresponds to their profile.id we get back from Google. Next, let’s handle our routes to use our new Strategy.

Generating Our Routes app/routes.js

We will need 2 routes.

  • /auth/google: Send our user to Google to authenticate
  • /auth/google/callback: Google sends our user back to our application with token and profile
    // 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

        // =====================================
        // GOOGLE ROUTES =======================
        // =====================================
        // send to google to do the authentication
        // profile gets us their basic information including their name
        // email gets their emails
        app.get('/auth/google', passport.authenticate('google', { scope : ['profile', 'email'] }));

        // the callback after google has authenticated the user
        app.get('/auth/google/callback',
                passport.authenticate('google', {
                        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

Once a user is authenticated, they will be redirected to their profile page. Last thing we need to do is show their user information.

Google Login Button views/index.ejs

Let’s create the Google login button.

    <!-- 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/google" class="btn btn-danger"><span class="fa fa-google-plus"></span> Google</a>

        </div>

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

When a user clicks on our authentication link, they will be directed to Google. A user will have to login and then grant permissions.

Profile Page views/profile.ejs

Once a user registers, they will be added to our database. Let’s show their user information.

    <!-- 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">

            <!-- GOOGLE INFORMATION -->
            <div class="col-sm-6">
                <div class="well">
                    <h3 class="text-danger"><span class="fa fa-google-plus"></span> Google</h3>

                        <p>
                            <strong>id</strong>: <%= user.google.id %><br>
                            <strong>token</strong>: <%= user.google.token %><br>
                            <strong>email</strong>: <%= user.google.email %><br>
                            <strong>name</strong>: <%= user.google.name %>
                        </p>

                </div>
            </div>

        </div>

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

Now we have their profile page!

Conclusion

Just like the other articles in this series, it is fairly straightforward to authenticate a user using a social network and OAuth 2.0.

In our next and final article, we will be dealing with the giant task of combining all of these methods of authentication into one user.

We’ll be diving into many ideas like linking social accounts under one user, unlinking an account, and handling all the other complex scenarios involved with this process.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Chris on Code

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel