AngularJS Twitter Authentication with OAuth.io

This rough example uses OAuth.io to connect an AngularJS app to Twitter’s OAuth API and display a user’s recent timeline. To get started create a new app on OAuth.io and add the Twitter provider (read this blog post for a good introduction).

The file structure:
Twitter OAuth.io example files

In index.html setup twitterApp and add all the Angular directives. Include OAuth.js in the header:

<!DOCTYPE html>
<html ng-app="twitterApp">
<head>
    <title>Twitter OAuth.io Example</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="oauth.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
    <script src="services.js"></script>
    <style>
        .container {
            margin-top: 10px;
            margin-bottom: 10px;
        }
        #results .row {
            margin-top: 15px;
            margin-bottom: 15px;

        }
    </style>
</head>
<body>
    <div class="container" ng-controller="TwitterController">
        <h1>Twitter OAuth.io Example</h1>
        <div class="row">
            <div class="col-xs-12">
                <button ng-click="connectButton()" id="connectButton" type="button" class="btn btn-primary">Connect Twitter</button>
                <button ng-click="refreshTimeline()" id="getTimelineButton" type="button" class="btn btn-info" style="display:none;">Get My Timeline</button>
                <button ng-click="signOut()" id="signOut" type="button" class="btn btn-link" style="display:none;">Sign Out</button>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12" id="results">
                <div class="row" ng-repeat="t in tweets">
                    <div class="col-xs-2 col-sm-1">
                        <img ng-src="{{t.user.profile_image_url}}" class="img-circle">
                    </div>
                    <div class="col-xs-10 col-sm-11">
                        <small>{{t.user.name}}</small><br>{{t.text}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

In app.js assign the Angular app to a variable called app and pass in twitterApp.services as a dependency:

//twitterApp is dependent on the twitterApp.services module
var app = angular.module('twitterApp', ['twitterApp.services']);

In services.js create a module called twitterApp.services and add a factory called twitterService. This factory handles communicating with OAuth.io. Inject Angular’s $q service into the factory to deal with asynchronous requests (watch this video to learn more about dealing with asynchronous issues):

angular.module('twitterApp.services', []).factory('twitterService', function($q) {

    var authorizationResult = false;

    return {
        initialize: function() {
            //initialize OAuth.io with public key of the application
            OAuth.initialize('e6u0TKccWPGCnAqheXQYg76Vf2M', {cache:true});
            //try to create an authorization result when the page loads, this means a returning user won't have to click the twitter button again
            authorizationResult = OAuth.create('twitter');
        },
        isReady: function() {
            return (authorizationResult);
        },
        connectTwitter: function() {
            var deferred = $q.defer();
            OAuth.popup('twitter', {cache:true}, function(error, result) { //cache means to execute the callback if the tokens are already present
                if (!error) {
                    authorizationResult = result;
                    deferred.resolve();
                } else {
                    //do something if there's an error
                }
            });
            return deferred.promise;
        },
        clearCache: function() {
            OAuth.clearCache('twitter');
            authorizationResult = false;
        },
        getLatestTweets: function () {
            //create a deferred object using Angular's $q service
            var deferred = $q.defer();
            var promise = authorizationResult.get('/1.1/statuses/home_timeline.json').done(function(data) { //https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
                //when the data is retrieved resolved the deferred object
                deferred.resolve(data)
            });
            //return the promise of the deferred object
            return deferred.promise;
        }
    }
    
});

In controller.js setup the TwitterController. This will call methods from twitterService and respond to user interactions. Inject twitterService into the controller along with $q and $scope:

//inject the twitterService into the controller
app.controller('TwitterController', function($scope, $q, twitterService) {

    $scope.tweets; //array of tweets
    
    twitterService.initialize();

    //using the OAuth authorization result get the latest 20 tweets from twitter for the user
    $scope.refreshTimeline = function() {
        twitterService.getLatestTweets().then(function(data) {
            $scope.tweets = data;
        });
    }

    //when the user clicks the connect twitter button, the popup authorization window opens
    $scope.connectButton = function() {
        twitterService.connectTwitter().then(function() {
            if (twitterService.isReady()) {
                //if the authorization is successful, hide the connect button and display the tweets
                $('#connectButton').fadeOut(function(){
                    $('#getTimelineButton, #signOut').fadeIn();
                    $scope.refreshTimeline();
                });
            }
        });
    }

    //sign out clears the OAuth cache, the user will have to reauthenticate when returning
    $scope.signOut = function() {
        twitterService.clearCache();
        $scope.tweets.length = 0;
        $('#getTimelineButton, #signOut').fadeOut(function(){
            $('#connectButton').fadeIn();
        });
    }

    //if the user is a returning user, hide the sign in button and display the tweets
    if (twitterService.isReady()) {
        $('#connectButton').hide();
        $('#getTimelineButton, #signOut').show();
        $scope.refreshTimeline();
    }

});

View a demo here. Download the source files.

Links:
- Twitter Apps Management
- Twitter GET statuses/home_timeline
- OAuth.io JavaScript API Reference

This entry was posted in Uncategorized. Bookmark the permalink.
  • Justin Vanderheide

    In Angular it’s typically bad style to call things like fadeOut and fadeIn within the controller. A better way to approach this would be to use ng-hide and ng-show in your template. You can apply fading transitions or any other animation by specifying styles for the animation classes that are managed by angular.

    https://docs.angularjs.org/api/ng/directive/ngHide#usage

    • matt

      Good point, thanks.

      • http://robvdl.co.nz/ Rob van der Linde

        Using jQuery code directly in the controller is generally a bad idea too, especially if you want to be able to easily test your controllers later without a browser, for example using the Karma test runner. I’ve always learned to move any jQuery code like that into a service or directive (whichever is more appropriate).

  • Walter Urbina

    how can I get the email of the user?
    I need to check on my BD

  • http://dfsq.info/ Aliaksandr Astashenkau

    It’s better to write such kind of services as providers rather than factory. Then you could expose some configuration method to set up service public key within application config phase.

  • max putilov

    BTW deferred.resolve() doesn’t work properly in Popup callback

  • donegroup

    Great post! I’m wondering if you can let me understand how can I use your code to build the Facebook Connect Auth. I think that with a few mods your script could be used to handle Facebook Connect too. Is it correct? Thanks

  • Sri Pal

    how can we retrieve the loged in user name

  • murugarajan s

    Its a very good example.. thanks … Can you tell me how to post our tweets..

  • John Cashmore

    Such a shame it uses jquery

  • Wade Bekker

    Hi and thanks for the tutorial. I’ve set it all up and clicking “Try auth” works fine for me. But when the popup initializes I get an error “Cannot find hostname in file:/// from static
    code: InvalidHeader
    message: Cannot find hostname in file:/// from static”

    Any ideas as to what this may mean? Thanks so much.

    • Carlos Villavicencio

      Try load this tutorial as a server, i.e. localhost/tutorial/index.html

  • Carlos Villavicencio

    Love this tutorial

  • Radhakrishna

    Is the public key of the application means Consumer Key (API Key)? its working with the demo key but if i replaced it with my Consumer Key (API Key) OAuth.popup function going to the else block and popup closing automatically. could somebody help me here.

    • Fredrik Olovsson

      No, it refers to the public key you get when registering your app on oauth.io.

    • Chirag Thakar

      I am having same issue.