Yahoo! Developer Network Blog
« Previous | Main | Next »
July 7, 2008
Using the Yahoo Address Book APIs with ROR - a step by step tutorial
Yahoo's new Address Book APIs is a welcome addition to the arsenal of APIs that Yahoo already has and provides very interesting possibilities to developers who would like to include their users' list of contacts in their application. One very obvious and straightforward example is the ability to virally invite the user's contacts to experience the new application that the user might find interesting, ala Facebook and the myriad of social networking applications out there.
In the following tutorial we'll show in an easy step-by-step format how to use the API with Ruby on Rails. You can download all the code examples in the article so you don't have to type them in.
One of the more interesting features of the API is that besides querying the user's contact, Yahoo allows external applications to create, delete and modify the address book contents. However that requires an explicit request for permission from the Address Book API team. Also, Yahoo recognizes that one of the main and frequent features in address books will be synchronization between mobile devices or multiple address books and provide capabilities to find out if contact information has changed since last access for a specific user. These 2 more advanced capabilities of the APIs provides additional weight behind the address book APIs which is considerably more than other APIs available currently provide.
This blog post focuses on the most basic of the 3 capabilities of the APIs, that is to query and retrieve information on the contacts in the user's address book. I will be using Ruby on Rails to flesh out the examples but since the APIs are REST-based and returns XML or JSON formatted data, you can use almost any technology you wish to access the address book.
Regardless of the capabilities, the Yahoo Address Book APIs require you as the application developer to register your application and authenticate the user through a browser-based authentication (BBAuth) method. This means that to get to the Address Book APIs we first need to understand and authenticate with BBAuth.
The BBAuth mechanism is simple. Assume that you want to develop a web application that needs to interact with Yahoo. First, you need to register for an application ID with Yahoo. Once you're registered, you'll be given an application ID and a shared secret. You will then use the application ID and shared secret to generate a URL and you will redirect your user to that URL. Once your user is at that URL he will be asked to give permission to your application to access Yahoo's services (in our case, the Address Book APIs). When your user does that, your application will be called once again and a token is passed to it. Finally you will use the token to get a WSSID and a cookie, which you can use to call the Yahoo service you want.
With that brief overview out of the way, let's step through creating a simple Rails application that allows the user to search for contacts in his address book. The first thing you need to do is to register for an application ID. Remember to register this as a BBAuth application. When you do this you will need to enter a BBAuth Success URL. This URL is the one that BBAuth calls once the user grants permission to your application. Take note that this URL cannot have a port number other than the default port 80, for security reasons. Also, the success URL needs to be accessible over the Internet. If you don't have access to port or a public IP, don't despair yet, there are other ways of going around it, which I will go into in a short while. If you do have access, set the URL to http://
If you're testing the Address Book APIs on your own, you're unlikely to have a publicly accessible web server or access to port 80 or both.
If you don't have a web server that's publicly available, an alternative is to use a dynamic DNS service to redirect the URL to your PC or laptop. The only problem here is that this only works if your connection has a real public IP address (and if you have to do port forwarding, that you are able to control the forwarding). If you're behind a firewall in your company, you're out of luck but if you're connected from home through your ISP, this should work.
If you don't have access to port 80 of a publicly available web server (at least to host the Rails app), that is a bit trickier. In my case I use another public server which I have access to and place this simple PHP script to redirect the success URL to my application, then indicate the URL to this script as the BBAuth success URL:
<?php
$success_url = "http://<my_private_rails_app_server>:<portnumber>/addr/success";
$query = "";
foreach ($_REQUEST as $key => $val) {
$query .= "$key=$val&";
}
$full_url = "$success_url?$query";
header ("Location: $full_url");
?>
What this code does is to grab all the parameters sent by BBAuth and forward it to your Rails application that you host somewhere else, with a different port number.
Now that you have an application ID and a shared secret, let's create the Rails application. Create a Rails application with this command:
$ rails addr
We're going to create a library that can be used by any controller to log into BBAuth, so add a file named ybbauth.rb in the lib folder.
require 'digest/md5'
require 'xmlsimple'
module Net
class HTTPS < HTTP
def initialize(address, port = nil, verify = :no_verify)
super(address, port)
self.use_ssl = true
self.verify_mode = OpenSSL::SSL::VERIFY_NONE if verify == :no_verify
end
end
end
module Ybbauth
# this is the action that is called upon successful authentication in Yahoo
def success
if auth?(params['token'], params['ts']) then
session['wssid'] = @wssid
session['ycookie'] = @ycookie
render :action => params[:appdata]
end
end
private
# call this to start authenticating through BBAuth
def login_yahoo(appdata=nil)
redirect_to url appdata
end
# check if the authorization is given
def auth?(token,ts)
sigstr = "/WSLogin/V1/wspwtoken_login?appid=#{YAHOO_CFG['appid']}&token=#{token}&ts=#{ts}#{YAHOO_CFG['secret']}"
sig = Digest::MD5.hexdigest sigstr
req_url = "https://api.login.yahoo.com/WSLogin/V1/wspwtoken_login?appid=#{YAHOO_CFG['appid']}&token=#{token}&ts=#{ts}&sig=#{sig}"
res = Net::HTTPS.get_response(URI.parse(req_url))
results = XmlSimple::xml_in(res.body, 'force_array' => false) ['Success']
@wssid = results['YahooSOAPAuthHeader']['Security']['YahooAuthToken']['WSSID']
@ycookie = results['YahooSOAPAuthHeader']['Security']['YahooAuthToken']['Cookies']
results['YahooSOAPAuthHeader']['Security']['YahooAuthToken']['AppID'] == YAHOO_CFG['appid']
end
# get the url to redirect to for logging in the user
def url(appdata=nil)
ts = Time.now.to_i
"https://api.login.yahoo.com/WSLogin/V1/wslogin?appid=#{YAHOO_CFG['appid']}&appdata=#{appdata}&ts=#{ts}&sig=#{signature(appdata, ts)}"
end
# sign the url
def signature(appdata, ts)
sig = "/WSLogin/V1/wslogin?appid=#{YAHOO_CFG['appid']}&appdata=#{appdata}&ts=#{ts}#{YAHOO_CFG['secret']}"
Digest::MD5.hexdigest sig
end
end
I'll explain the library in several parts.
The first is the HTTPS class, which is just a convenient class that extends the default Net::HTTP class to allow HTTPS connections.
The second is a module that we will be extending in our controller later on. There is only one public method, success, which is the action that BBAuth will call after it authenticates the user successfully. The rest are private methods to hide the implementation of the authentication mechanism.
The first private method is login_yahoo, and it is used by a public login action in our controller later on, to initiate the login process. It does this by forming the correct Yahoo URL to redirect the user to. Forming the URL requires us to attach the application ID, a piece of user application data, the timestamp and a signature that is in turn generated by hashing the path portion of the URL with the shared secret appended to it.
You will notice that we used an array YAHOO_CFG['appid'] and YAHOO_CFG['secret']. This nicely formatted configuration is courtesy of the initializer mechanism. Add a line like this:
YAHOO_CFG = YAML.load_file("#{RAILS_ROOT}/config/yahoo.yml")[RAILS_ENV]
in config/initializers/load_config.rb and then create the yahoo.yml file in the config folder containing the application ID and shared secret you have created:
development:
appid: YOUR_DEV_YAHOO_APP_ID
secret: YOUR_DEV_YAHOO_SHARED_SECRET
test:
appid: YOUR_TEST_YAHOO_APP_ID
secret: YOUR_TEST_YAHOO_SHARED_SECRET
production:
appid: YOUR_PRODUCTION_YAHOO_APP_ID
secret: YOUR_PRODUCTION_YAHOO_SHARED_SECRET
This will give you a nice configuration array anywhere in your entire Rails application.
Now that we have the BBAuth authentication request URL, just redirect the user to it. Note that we're passing a piece of application data to this URL. This piece of data is optional in the BBAuth documentation but I use it to generalize this Rails BBAuth library, and you will see how in a short while.
Once the user authorizes our user, the success action will be called. This action basically calls the auth? method and if the token and timestamp are valid, then it will store the WSSID and cookie into the session for future use.
The auth? method again is quite straightforward and follows almost the same steps as before. However, instead of redirecting the user to the URL, we use NET/HTTP to get the response, then use XML Simple to parse it into arrays and extract the WSSID and cookie. Finally as a security measure we test the received Application ID with our Application ID to make sure things are ok. Take care that this simple piece of code does absolutely nothing to catch any exceptions so if you want to use it in any serious work you need to put those in yourself.
Now that we have the library, let's head on to the controller. Create a addr_controller.rb file in the app/controllers folder.
class AddrController < ApplicationController
include Ybbauth
def index
end
def login
login_yahoo "search"
end
def search
end
end
Make sure that this controller includes the Ybbauth module in our library. We have a index action with a simple template that will just allow the user to click on a link that calls the login action. The login action in turn calls the private login_yahoo method with the parameter "search". This parameter tells the Ybbauth module to call the search action after successfully logging in through BBAuth.
With this we have successfully logged in and can start to use the Address Book API proper. Create a corresponding search view template named search.html.erb in the app/views/addr folder:
<h1>Search for contacts</h1>
<% form_tag :action => :find do %>
<%= text_field_tag :name %>
<%= submit_tag %>
<% end %>
This simple template creates a form that sends a single parameter called name to the find action. The find action is the one that users the real Address Book API. Add a find method in the addr_controller.rb:
def find
hash = {:appid => YAHOO_CFG['appid'],
:WSSID => session['wssid'],
:format => 'xml',
:fields => 'name',
'name.first.contains' => params['name'] }
parameters = URI.escape(hash.to_a.collect {|pair| pair.join('=')}.join('&'))
req_url = "http://address.yahooapis.com/v1/searchContacts?#{parameters}"
headers = {'Cookie' => session['ycookie']}
url = URI.parse(req_url)
res = Net::HTTP.start(url.host, url.port) {|http|
http.get(req_url, headers)
}
@results = XmlSimple::xml_in(res.body, 'force_array' => false)
end
This method first creates the parameters list, indicating that we want to search for the first name that contains the value that comes from the name parameter from the search form, and also format (XML) and the information we want (just the name). We append these parameters to the Address Book API searchContacts URL endpoint, and create a header hash to pass on the cookie. Then we use Net/HTTP and send a GET request to the URL, also providing the cookie in the header and parse the reponse through XML Simple. You should remember to send the cookie in, otherwise you get a blank response page. The results are then passed to the find view, find.html.erb, to display:
<h1>Results</h1>
<ol>
<% @results['contact'].each { |contact| %>
<li><%= contact['name']['first']%> <%= contact['name']['last']%></li>
<% } %>
</ol>
<%= link_to 'search again', :action => :search%>
The short step-by-step tutorial above ran through how you can use the most basic method in the Address Book API. However there is a lot of further depth in the APIs which you can use creatively to add to your existing application or simply to enhance the display of the address book as you like it. For further details read the documentation to the Address Book APIs or subscribe to the Address Book API tech group.
Sau Sheong Chang
Yahoo Developer Network
Posted at July 7, 2008 2:50 AM | Permalink
Comments
Nice clean rails snippet of yahoo address api integration. Thanks!
Posted by: Jason at July 7, 2008 7:09 AM
Thanks for tutorial! Any chances for OAuth version using oauth gem?
Posted by: Jarosław Skrzypek at September 23, 2008 11:57 PM
Umm, I think there's a small error in your code:
res = Net::HTTP.start(url.host, url.port) {|http|
http.get(req_url, headers)
}
should read something like:
res = Net::HTTP.start(url.host, url.port) {|http|
http.get("/v1/searchContacts?#{parameters}", headers)
}
At least that is the way I got my code to work.
Posted by: Eugen Minciu at September 26, 2008 8:52 AM
Post a comment
Comment Policy: We encourage comments and look forward to hearing from you. Please note that Yahoo! may, in our sole discretion, remove comments if they are off topic, inappropriate, or otherwise violate our Terms of Service. Fields marked with asterisk '*' are required.
Subscribe
Recent Blog Articles
view all
YQL Open Table for Google Buzz now live
Tue, 09 Feb 2010
INSERT INTO twitter.status ...
Mon, 08 Feb 2010
Announcing the Yahoo! Brasil Open Hack Day 2010, 20-21 March
Mon, 08 Feb 2010
Marketing hacks, linchpins, and tech women of valor
Sun, 07 Feb 2010
Yahoo! India invites you to join the first India Hadoop Summit
Thu, 04 Feb 2010
Recent Links
Appcelerator Titanium + Yahoo YQL on Vimeo
Mon, 08 Feb 2010
Tue, 02 Feb 2010
PhoneGap | Cross platform mobile framework
Sat, 30 Jan 2010
Web developers can rule the iPad - O'Reilly Radar
Sat, 30 Jan 2010
rc3.org - Is the iPad the harbinger of doom for personal computing?
Thu, 28 Jan 2010
Archives
2010
2009
2008
2007
2006
2005
Recent Readers

