Monday, August 1, 2016

Facebook not playing nice with Twitter in an Ionic app

This bug I encountered when my Ionic was logged in via Facebook. It caused my Twitter routine not to retrieve user timelines. My Twitter routine depends on a Application only login to get an Oauth token.

The symptom of my problem was when doing a call to api.twitter.com/oauth2/token and api.twitter.com/1.1/statuses/user_timeline.json, Angular's $http wasn't sending the correct header values and I kept getting invalid request (code 99) error from twitter.

POST https://api.twitter.com/oauth2/token HTTP/1.1
Authorization: Basic [base64 code here]
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

The thing to focus here is the Authorization value. Instead of a base64 encoded value for Twitter, Facebook overwrites it or I can't overwrite it via a $http config object.

With the bug understood, I found the solution was to use a $httpProvider interceptor to modify the header before sending it to the server.

function AuthHeaderInterceptor($rootScope, $q, $location, localStorageService) {
    // Function to handle a request
    var request = function (config) {

        var expression = /(https?:\/\/(.+?\.)?api.twitter.com\/1.1\/statuses\/user_timeline.json(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/g;
        var regex = new RegExp(expression);
        var test_url = config.url;

        if(test_url.match(regex)){           
            var authToken = localStorageService.get('twitter_token'); // get token from local storage
            config.headers.Authorization = 'Bearer ' + authToken;     // Force overwrite the Authorization header
        }

        // Return our config
        return config;
    };

    // Function to handle errors
    var responseError = function (rejection) {
        // Return our rejected promise
        return $q.reject(rejection);
    };

    return {
        request: request,
        responseError: responseError
    };
}

All that's left is to set this to my app config and I was good to go.