OAuth2 with AngularJS to access Twitter

Please also read my update on this topic.
angularjsCurrently my latest activities tend to be more front-end centric. In the course of building a smart information dashboard I have chosen AngularJS as the major building block for the frontend application, mainly for its many sophisticated architectural concepts such as loose coupling, dependency injection, modularity, testability. All of them promise a sustaining application design.

Twitter-LogoA significant functional element of this dashboard is among others a classic Twitter integration. In the first place this may sound as a relatively trivial task to accomplish given the fact, that Twitter’s REST API offers numerous services and documented examples in the internet are well-spread. The initial solution would integrate with the search API before a more complex solution via the streaming API is developed in a later stage of development.

A typical AngularJS service may become here very handy to access the Twitter Search-API which then may be easily used in controller layers of the application: The following ist an excerpt from the service layer:

(function() {
   var serviceModule = angular.module('InfoDashboard.services', ['ngResource'])
   serviceModule.factory('twitter', function ($resource, $http) {
       var r = $resource('http://search.twitter.com/:action',
		{action:'search.json',
		 callback:'JSON_CALLBACK',
		 rpp:10,
		 randomParam:Math.random() * 1000},
                 { get : {method : 'JSONP'},
		   paginate: {method : 'JSONP', params : { page : '@page'}
                 }
		})

		return r;
})()

We are done – more or less. The magical $resource factory provides an easy facility to build RESTful clients quickly. The technical specification details about the Search-API may be found here.

The above solution did work quite flawlessly…since yesterday. Twitter has decided – for very good reasons imho – to drop support for the v1.0 APIs and switch to REST v1.1. API which now fully integrate with OAuth2. This would require to think about extending the above solution with some client authentication.

Twitter’s OAuth features and services are pretty well documented so you wont’t trip up that quickly. It is even possible to choose an application-only authentication. Twitter offers applications the ability to issue authenticated requests on behalf of the application itself (as opposed to on behalf of a specific user). Twitter’s implementation is based on the Client Credentials Grant flow of the OAuth 2 specification.

In the first place the current REST access needs to change to the new API

(function() {
        angular.module('InfoDashboard.services', ['ngResource'])
         .factory('twitter', function ($resource, $http) {
            var r = $resource('https://api.twitter.com/1.1/search/:action',
                {action: 'tweets.json',
                    count: 10,
                },
                {
                    paginate: {method: 'GET'}
                })

            return r;})()

Despite of more or less trivial changes our we are not granted any access by the new search service and are replied with 403 Forbidden. As previously described all of Twitter services need to be OAuth authenticated. The OAuth service itself is available unter https://api.twitter.com/oauth2/token.

(function() {
         var serviceModule = angular.module('InfoDashboard.services', ['ngResource'])

         serviceModule.factory('twitter', function ($resource, $http) {
            var consumerKey = encodeURIComponent('<your consumer key>')
            var consumerSecret = encodeURIComponent('<your consumer secret>')
            var credentials = Base64.encode(consumerKey + ':' + consumerSecret)
            // Twitters OAuth service endpoint
            var twitterOauthEndpoint = $http.post(
                'https://api.twitter.com/oauth2/token'
                , "grant_type=client_credentials"
                , {headers: {'Authorization': 'Basic ' + credentials, 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}}
            )
            twitterOauthEndpoint.success(function (response) {
                // a successful response will return
                // the "bearer" token which is registered
                // to the $httpProvider
                serviceModule.$httpProvider.defaults.headers.common['Authorization'] = "Bearer " + response.access_token
            }).error(function (response) {
                  // error handling to some meaningful extent
                })

            var r = $resource('https://api.twitter.com/1.1/search/:action',
                {action: 'tweets.json',
                    count: 10,
                },
                {
<span style="line-height: 1.5;">                    paginate: {method: 'GET'}</span>
                })

            return r;
        }
...
        .config(function ($httpProvider) {
           serviceModule.$httpProvider = $httpProvider
        });
})()

An access token needs to be requested from the OAuth server. That given it then may be used as a bearer token in subsequent search requests. Since the bearer token must be provided as an additional HTTP header

GET HTTP 1.1
...
Authorization: Basic <bearer token>
...

using $resource without further modifications to the $http layer will fail. So as a final move, a workaround is proposed where the required HTTP header is registered as a default HTTP header to Angular’s $httpProvider.

The current solution is a working solution but comes with a price of an additional global HTTP header. A second option would be to fully switch to a $http based solution or – even more exiting – wait for the Angular 1.2 release featuring $resource interceptors.

Please be aware that you probably do not want to request an access token from your client side since your Twitter’s consumer key and secret need to be provided. So that step should be most probably performed on your server side – unless you don’t care of course.

Please also be aware of potential CORS problems you will probably encounter depending on your local development setup.

Cheers !!!

7 thoughts on “OAuth2 with AngularJS to access Twitter

  1. I really wonder how can you send $http request to the external resource? How did you deal with «XMLHttpRequest cannot load https://api.twitter.com/oauth2/token. No ‘Access-Control-Allow-Origin’ header is present on the requested resource.» problem?

    In every blog post about AngularJS + Twitter authorization everyone is using POST to the api.twitter.com/oauth2/token via $http service… and it works for everyone! Amazing! I really wonder — how?

  2. Pingback: OAuth2 with NodeJS , Express and AngularJS to access Twitter | Beautiful Bytes

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.