1

So this question has been asked several times, but I believe I've finally boiled down exactly what goes wrong here. I'm using the Yelp API with Angular, which requires OAuth 1.0a to access. Like most people doing this type of implementation, I'm using the following code:

.factory('YelpApi', ['$http',
      function ($http) {
        var randomString = function (length, chars) {
          var result = '';
          for (var i = length; i > 0; --i) {
            result += chars[Math.round(Math.random() * (chars.length - 1))];
          }
          return result;
        };

        var retrieveYelp = function (name, callback) {
          var method =  'GET';
          var url =     'http://api.yelp.com/v2/search';
          var params = {
            callback:                 'angular.callbacks._0',
            ll:                       /* hidden */,
            radius_filter:            '3219',
            oauth_consumer_key:       /* hidden */, // consumer key
            oauth_token:              /* hidden */, //Token
            oauth_signature_method:   'HMAC-SHA1',
            oauth_timestamp:          new Date().getTime(),
            oauth_nonce:              randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
            term:                     name || 'food', 
            actionlinks:              true
          }; // end params
          var consumerSecret =        /* hidden */; //Consumer Secret
          var tokenSecret =           /* hidden */; //Token Secret
          var signature = 
            oauthSignature.generate(
              method, 
              url, 
              params, 
              consumerSecret, 
              tokenSecret, 
              { encodeSignature: false }
            ); 
            // end signature
          params['oauth_signature'] = signature;
          console.log('inside yelpapi factory');
          console.log('Term searched for: ' + params.term);
          $http.jsonp(url, { params : params })
            .then(callback, function(err) {
              console.log('An error occured: ', err);
            });
            console.log("inside end of yelpapi factory");
        }; // end retrieveYelp

        return {
          retrieveYelp: retrieveYelp      
        };

      } // end function

    ]) // end factory

Here's the issue:

Using 'angular.callbacks._0' as the callback makes it work the first time as it forces it to use the first callback, however breaks subsequent requests. You are actually supposed to use JSON_CALLBACK to properly increment callbacks, however that breaks everything. The error generated by Yelp is "invalid signature". I believe this is caused by Angular not replacing JSON_CALLBACK until after it's already encoded, thus causing the expected signature to be different than the actual one. I'm using this signature generator.

How could I get around this, ensuring Angular properly increments the callbacks while ensuring proper signature generation?

Community
  • 1
  • 1
RGStrat
  • 25
  • 7

1 Answers1

0

I kind of find an ugly way to solve the problem. In following post, there is a description of how to create your own interceptor to increment the jsonp callback parameter (angular.callbacks._0, angular.callbacks._1, etc.)

how to custom set angularjs jsonp callback name?

One to one this solution didn't work in my case. With yelp and queries where you have to create a oauth signature, the param callback should be included in the signature respectively should then be already incremented before doing the jsonp request.

So what I did, in my service, before doing the signature and before doing the jsonp request, I just call the counter to generate the new increment.

In your case, I would then suggest to change your code like following:

var retrieveYelp = function (name, callback) {

// GENERATE increment and use it in the params
var callbackId = angular.callbacks.counter.toString(36);

      var method =  'GET';
      var url =     'http://api.yelp.com/v2/search';
      var params = {
        callback:                 'angular.callbacks._' + callbackId,
        ll:                       /* hidden */,
        radius_filter:            '3219',
        oauth_consumer_key:       /* hidden */, // consumer key
        oauth_token:              /* hidden */, //Token
        oauth_signature_method:   'HMAC-SHA1',
        oauth_timestamp:          new Date().getTime(),
        oauth_nonce:              randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
        term:                     name || 'food', 
        actionlinks:              true
      };

I found that solution not super duper, I'm agree, but well then at least it works and doesn't seems to have side effects.

Community
  • 1
  • 1
David Dal Busco
  • 6,683
  • 10
  • 41
  • 84