Published using Google Docs
commands_yahoo

Yahoo API contacts (Authentication , authorization & API GET request)

This document is created by datnt

1> Register an Application with Yahoo

2> Setup consumer_id & secret to our application

3> CHAPTER 1: Begin AUTHENTICATION & AUTHORIZATION WITH YAHOO

3.1> STEP 1: send request to retrieve: oauth_token & xoauth_request_auth_url

   nonce = SecureRandom.hex()

   ts=Time.now.to_i

   q = URI.encode_www_form(

     "oauth_nonce" => nonce,

     "oauth_timestamp" => ts,

     "oauth_consumer_key" => YAHOO_CONFIG[:client_id],

     "oauth_signature" => "#{YAHOO_CONFIG[:client_secret]}&",

     "oauth_signature_method" => "plaintext",

     "oauth_version" => "1.0",

     "xoauth_lang_pref" => "en-us",

     "oauth_callback" => "http://localhost:3000/auth/yahoo/callback"

   )  

    response = RestClient.get "https://api.login.yahoo.com/oauth/v2/get_request_token?#{q}"    

    p=CGI.parse(response)    

    oauth_token = p["oauth_token"].first

    session[:yahoo_oauth_token] = oauth_token

    oauth_token_secret = p["oauth_token_secret"].first

    session[:yahoo_oauth_token_secret] = oauth_token_secret

    xoauth_request_auth_url = p["xoauth_request_auth_url"]

 

NOTE:  Take care with the sign “&” and “plaintext” which I colored in Red color above.

3.2> STEP 2: you will need to open a popup on web browser to let end-user authenticating (by email&password) with yahoo. The url for popup windows is retrieved from STEP-1 above (it is p["xoauth_request_auth_url"]):

a) Javascript code function to open popup

function popupCenter(url, width, height, name) {

    var left = (screen.width / 2) - (width / 2);

    var top = (screen.height / 2) - (height / 2);

    width = screen.width / 2;

    height = screen.height / 2;

    return window.open(url, name, "menubar=no,scrollbars=yes,toolbar=no,status=no,width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);

}

b) Calling to function popup:

popupCenter(yahoo_url, 400, 400, "Yahoo");

NOTE 1(again): yahoo_url is p["xoauth_request_auth_url"] from STEP1

NOTE 2: Based on my code, yahoo will callback to “http://localhost:3000/auth/yahoo/callback”. I had already defined this route in routes.rb

You can see from the routes above, yahoo will callback to controller “authenticaions”, at action “create”

3.3> Receive the callback triggered from yahoo.com, at controller “authenticaions”, at action “create”:

NOTE: by default, you have object name “request” at this action.

So, at this step, you will have “oauth_verifier” and “provider”. 

      session[:oauth_verifier] = request["oauth_verifier"]

      session[:yahoo_provider] = request["provider"]

NOTE: the parameter oauth_verifier means end user had allowed your web application to access end_user’s yahoo contacts

3.4> Now, you can send another request to yahoo, to retrieve the access_token (yahoo called it oauth_token).

NOTE: also, within controller “authenticaions”, at action “create”, you insert the code below:

      nonce = SecureRandom.hex()

      ts=Time.now.to_i

      q = URI.encode_www_form(

        "oauth_nonce" => nonce,

        "oauth_timestamp" => ts,

        "oauth_consumer_key" => YAHOO_CONFIG[:client_id],

        "oauth_signature" => "#{YAHOO_CONFIG[:client_secret]}&#{session[:yahoo_oauth_token_secret]}",

        "oauth_signature_method" => "plaintext",

        "oauth_version" => "1.0",

        "xoauth_lang_pref" => "en-us",

        "oauth_token" => session[:yahoo_oauth_token],

        "oauth_verifier" => session[:oauth_verifier]

      )

      response = RestClient.get "https://api.login.yahoo.com/oauth/v2/get_token?#{q}"

      p=CGI.parse(response)

      session[:yahoo_access_token] = p["oauth_token"].first

      session[:yahoo_access_token_secret] = p["oauth_token_secret"].first

NOTE: be careful with sign “&” and “plaintext” I colored with Red color above. You also take note that, “oauth_signature” takes another value with another format from the previous request to Yahoo.

3.5> End of chapter 1:

Now, you have yahoo access token, you can send request to query end_user contacts. But, the tragedy is not over yet.

END OF CHAPTER 1.

4> CHAPTER 2: Send request to Yahoo API to query for end_user contacts:

Before begin with coding, we need some foundation knowledge. I describe line by line below:

a) A short note from yahoo:”However, calls made to actual Yahoo APIs are sent insecurely over HTTP and thus require HMAC-SHA1 signatures” (http://developer.yahoo.com/oauth/guide/oauth-requesttoken.html#). I urge you to open the yahoo link I provided here and ready through everything, if you do not have time, learn by heart these words “require HMAC-SHA1 signatures

b) A hint from (http://nullinfo.wordpress.com/oauth-yahoo/): I urge you to read through everything. But the most important paragraph is at “A Signature Base String is a RFC 3986 encoded concatenation of”  and “A Signature Key is the concatenation of:

c) Another hint, (http://stackoverflow.com/questions/4524911/creating-signature-and-nonce-for-oauth-ruby), open that stackoverflow page, you will see these words “this gist by erikeldridge on github”. It is posted by iain , answered on Jan 21, 2011. Open that link, you will see 2 files, “oauth_util.rb” and “example.rb

5> Coding the query to send to yahoo:

 Now, you may want to apply the above knowledge and write the code by your self, or continue reading and make some experiment with me:

STEP 1:

- Using the TOKEN & TOKEN SECRET which you had received.

- Adding more library to your authentications_controller, if needed.

require 'uri'

require 'cgi'

require 'openssl'

require 'base64'

require 'net/http'

STEP 2 (add more code into action create of authentications_controller.rb)

consumer_key = YAHOO_CONFIG[:client_id]

consumer_secret = YAHOO_CONFIG[:client_secret]

token = "#{session[:yahoo_access_token]}"

token_secret = "#{session[:yahoo_access_token_secret]}"

req_method = "GET"

sig_method = "HMAC-SHA1"

oauth_version = "1.0"

callback_url = ""

STEP 3(add more code into action create of authentications_controller.rb

url = "http://social.yahooapis.com/v1/user/me/contacts"

parsed_url = URI.parse( url )

Note for step3: I will declare some non-sense behaviors of Yahoo API URL at the end of this document

STEP 4(add more code into action create of authentications_controller.rb

nonce = Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first

STEP 5: Define an action within file authentications_controller.rb

  def percent_encode( string )

   

    # ref http://snippets.dzone.com/posts/show/1260

    return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')

  end

STEP 6: Define another action within file authentications_controller.rb

  def signature(consumer_secret, token_secret, base_str)

    key = percent_encode( consumer_secret ) + '&' + percent_encode( token_secret )

   

    # ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks

    digest = OpenSSL::Digest::Digest.new( 'sha1' )

    hmac = OpenSSL::HMAC.digest( digest, key, base_str )

   

    # ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81

    Base64.encode64( hmac ).chomp.gsub( /\n/, '' )

  end

STEP 7: Define another action within file authentications_controller.rb

  def query_string(params)

    pairs = []

    params.sort.each { | key, val |

      pairs.push( "#{ percent_encode( key ) }=#{ percent_encode( val.to_s ) }" )

    }

    pairs.join '&'

  end

STEP 8:(add more code into action create of authentications_controller.rb)

    params = {

      'format' => 'json',

      'oauth_consumer_key' => consumer_key,

      'oauth_nonce' => nonce,

      'oauth_signature_method' => sig_method,

      'oauth_token' => token, #DATNT: ADDED THIS PARAMETER

      'oauth_timestamp' => Time.now.to_i.to_s,

      'oauth_version' => oauth_version

    }

    if parsed_url.query

      params.merge! CGI.parse( parsed_url.query )

    end

    req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path

    base_str = [

      req_method,

      percent_encode( req_url ),

     

      # normalization is just x-www-form-urlencoded

      percent_encode( query_string(params) )

     

    ].join( '&' )

    params[ 'oauth_signature' ] = signature(consumer_secret, token_secret, base_str)

    datnt_query_string= query_string (params)

STEP 9: Send and receive the data

yahoo_response=nil

Net::HTTP.start( parsed_url.host ) { | http |

  req = Net::HTTP::Get.new "#{ parsed_url.path }?#{ datnt_query_string }"

  response = http.request(req)

  yahoo_response = response

  response.read_body

}

STEP 10:

a) If you request json, then you parse json

yahoo_json = JSON.parse(yahoo_response.read_body)

b) If you request json, but you receive xml (yeah, yahoo did return xml, when I requested for json), then parse xml

my_string = yahoo_response.read_body.force_encoding("utf-8")

doc = Nokogiri::XML(my_string)

APPENDIX:

Nonsense behaviors of yahoo api url:

No 1>

Reference: http://developer.yahoo.com/social/rest_api_guide/contact-resource.html#ContactObject

- Yahoo provide you this api uri: http://social.yahooapis.com/v1/user/me/contacts?format=json

But when you use this uri to Step 3 about, combine with removing “format” => “json” at Step 8, yahoo will send you xml.

That’s why I have to put “format” => “json” at step 8, and remove “?format=json” from the uri

No 2>

Happens with YQL, refer to: http://developer.yahoo.com/yql/console/

- If you enter query “select * from social.contacts where guid = me”

you will get this uri:

http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20social.contacts%20where%20guid%20%3D%20me&diagnostics=true

Actually, when I used this query for Step 3 above, Yahoo returns to me an error message contained within an xml file

And this is it: http://developer.yahoo.com/forum/YQL/Strange-sample-query-object-Object-/1311985846289-4c7a5f1e-d4b5-49ff-b354-1056f9245fa3

Syntax error(s) [line 1:0 no viable alternative at character '[',line 1:14 no viable alternative at character ']']>

No 3>

Happens with YQL, refer to: http://developer.yahoo.com/yql/console/

- If you enter query “select * from social.contacts where guid = me” ,and choose the JSON format

you will get this uri:

http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20social.contacts%20where%20guid%20%3D%20me&format=json&diagnostics=true&callback=

What you will get when using this as url for Step 3 is

+ Success, you received response code 200 (which mean everything fine)

+ And, there is no data at all, there is no response data at all

No 4>

Many times, yahoo returns its json data on its YQL console , or API REST, but the real response data format is different with what it is showing on its document.