Flickr Authorization

Authentication is a bit tricky to follow, and ultimately you may want to leave the details to one of the Flickr API kits (covered later in the chapter). However, you may still be interested in working through the details at least once so that you know what’s going on below the hood before you use someone else’s library. Besides, there will be other authentication schemes out there besides Flickr’s that you will want to use. Getting a solid handle on Flickr’s authentication scheme is good preparation for more quickly understanding those other authentication schemes.

You can find the specification for Flickr authentication here:

http://www.flickr.com/services/api/auth.spec.html

There are three types of authentication cases to handle with Flickr:

Each scheme is different because of the differing natures of each type of application. For example, the author of a web application can configure it to have a URL through which Flickr would be able to communicate. It’s hard to guarantee that a desktop application would have a URL through which such communication could happen. In this book, I cover only the specific case of authentication for web applications. Once you understand this case, you will be able to understand the others without much problem.

Three parties are involved in the authentication dance:

Authorization is required when an app is calling a Flickr method that requires a permission level of read, write, or delete—anything but none. Through authorization, the app is granted a specific permission level by the user to access the Flickr API on the user’s behalf. Flickr creates a token that ties a specific app with a specific user and a specific permission level to embody this authorization act. The authentication dance is all about how that token gets created, used in conjunction with specific API calls, and can be managed and possibly revoked by the user. The details are a bit complicated because this process must also fulfill certain design criteria, which you can surmise from how the authorization scheme is designed:

Why Passing Passwords Around Doesn’t Work Too Well

The current Flickr authorization scheme is not the first one it used. In the early days of Flickr, users granted the power to an app to act on their behalf by giving the apps their Flickr username and password. Doing so meant that in order to revoke an app’s permission, users would have to change their Flickr password. Of course, doing that would also instantly revoke permissions of other third-­party apps with knowledge of the user’s password. The new authorization scheme is meant to correct obvious problems with the old scheme. Why should you as a user have to use your Flickr password for anything other than your dealings with Flickr? Why should revoking permissions to one app mean revoking permissions to other apps?

Authorization for Web Apps

Let’s now look at the authorization scheme used with Flickr. We first need to set up some permissions.

Setting Up the Example of Lois and ContactLister

Let’s now get down to the details of the process of authentication for web-­based applications, keeping the authorization design criteria in mind. Let’s have a specific example in mind. The app, which I will call ContactLister, displays the list of contacts for a given Flickr user. It specifically uses the flickr.contacts.getList method, which requires authorization with read permission. (A Flickr user’s contacts list is private.) Let’s also make up a hypothetical user called Lois.

Basic Lesson: Flickr Needs to Mediate the Authorization Dance

For ContactLister to get permission from Lois, why couldn’t ContactLister just directly display a screen asking Lois to give it permission to read her contact list—and then relay that fact to Flickr? For starters, how does ContactLister prove to Flickr that Lois did in fact give ContactLister permission to access her photos? In the old days of Flickr, ContactLister would have Lois’s Flickr username and password. At that time, ContactLister might as well have been Lois since it’s the Flickr username/password that Flickr used to authenticate a user.

The solution that Flickr came up with is based on that Flickr needs to establish unambiguously that Lois is with full knowledge of (that is, not being tricked into) giving ContactLister (and not some other third-­party app) read (and not some other) permission. To do that, Flickr needs to mediate communication between Lois and ContactLister.

Step 1: ContactLister Directs Flickr to Ask Lois for Permission

So instead of ContactLister directly prompting Lois for permission, ContactLister directs Flickr to prompt Lois for read permission by formulating the following Flickr URL that it directs Lois to:

http://flickr.com/services/auth?api_key={api_key}&perms={perms}&api_sig={api_sig}

Let’s look at the various arguments. You are familiar with the api_key; perms would be set to read in this circumstance.

Signing a Call: How Does ContactLister Create and Send One?

The part that is new in this chapter is the api_sig. It is the act of calculating the api_sig and attaching it to method calls in the Flickr API that we refer to as signing the call. The purpose of signing a call is to reliably establish the identity of the signer, the one formulating the URL. Why isn’t the api_key enough to establish the identity of the caller? In some circumstances, it would be if no one but the author of ContactLister and Flickr knew this api_key. On another level, Flickr API keys are sent unencrypted every time a call is made to the Flickr API, akin to passwords being sent in plain text. Hence, the api_key alone is an insufficient foundation for signing this call. You shouldn’t be able to easily fake a signature.

When you sign up for a Flickr API key, in addition to getting a key, you get a corresponding string: secret. As the name implies, you are supposed to keep secret secret so that in theory only you and Flickr know it. Go to http://www.flickr.com/services/api/keys/ to see your own keys and secrets.

ContactLister has to use this secret to calculate the api_sig and thereby sign the call. The api_sig is calculated according to the following algorithm for any Flickr API call:

  • Make a signature string that starts with the secret followed by a concatenation of all the name/value pairs of the arguments to be passed to Flickr, sorted alphabetically by name—excluding the api_sig but including method. The values need to UTF-8 encoded but not URL-­encoded.

  • The api_sig is then the hexadecimal digest of the md5 hash of the signature string.

The following is a Python function that takes a secret and a dictionary of name/value pairs and returns the corresponding api_sig:

def calcSig(secret,params):
  import md5
  l = params.keys()
  l.sort()
  hash = ''
  for key in l:
      hash += str(key) + params[key].encode('utf-8')
  hash = secret + hash
  api_sig = md5.new(hash).hexdigest()
  return api_sig
               

Let’s first run through a concrete example and then discuss how this process constitutes signing the call. Consider the following sample key and secret:

  • Key: 020338ddabd2f41ae7ce9413a8d51429

  • Shared secret: f0fc085289c7677a

The signature string is then as follows:

{secret}api_key{api_key}perms{perms}

which is as follows:

f0fc085289c7677aapi_key{api_key}permsread

The md5 hexadecimal digest of the string is then as follows:

f9258a76e4ad3cb5fa40bd8b0098d119

Therefore, the signed call is as follows:

  http://flickr.com/services/auth?api_key={api_key}&perms=read&api_sig=f9258a76e4ad3cb5fa40bd8b0098d119
               
What Flickr Makes of the Signed Call

So when ContactLister directs Lois to go to this URL, Flickr first determines the integrity of this call by performing the same signature calculation as ContactLister did in the first place: find the secret that corresponds to the api_key, sort all the parameters by key (except for the api_sig parameter), form the signature string, and then compare it to the value of the api_sig parameter. If two match up, then Flickr can conclude the call did indeed come from ContactLister because presumably the author of ContactLister is the only one other than Flickr who knows the key/secret combination.

You might ask, why can’t someone take the api_sig from the call and reverse the md5 calculation to derive the secret? Although it’s straightforward to calculate the md5 hash of a string, it’s much more difficult computationally to go in the other direction. For the purposes here, you should think of this reverse direction for md5 as practically—but not theoretically—impossible. Moreover, using md5 makes it difficult to change the parameters of the call. If you change, say, perms=read to perms=delete, you get a different api_sig, which is very hard to calculate without knowing secret.

[Note]Note

md5, it turns out, does have limitations as a cryptographic hash function. Researchers have demonstrated how to take an md5 hash and create another string that will give you the same md5 hash. Can this weakness be used to issue fake Flickr calls? I don’t know; see http://en.wikipedia.org/wiki/MD5 for more information.

Step 2: Flickr Asks Lois for Permission on Behalf of ContactLister

At any rate, assuming a properly signed call to http://flickr.com/services/auth, Flickr now knows reliably that it is indeed ContactLister asking for read permission. Remember, though, that the end goal, a token, ties three things together: an app, a permission level, and a user. The call reliably ties the app and permission together for Flickr. However, the call has no explicit mention of a user at all. There’s no parameter for user_id, for instance.

That ContactLister doesn’t have to pass to Flickr anything about Lois’s Flickr account is a virtue—not a problem. Why should a third-­party app have to know anything a priori about a person’s relationship to Flickr? So, how does Flickr figure out the user to tie to the request by ContactLister for the read permission? The fact is that it’s Lois—and not someone else—who uses the authorization URL:

http://flickr.com/services/auth?api_key={api_key}&perms=read&api_sig=f9258a76e4ad3cb5fa40bd8b0098d119
            

When Lois loads the authorization URL in her browser, Flickr then determines the user in question. If Lois is logged in, then Flickr knows the user in question is Lois. If no one is logged in to Flickr, then Lois will be sent through the login process. In either case, it’s Flickr that is figuring out Lois’s identity as a Flickr user and taking care of her authenticating to Flickr. In that way, Flickr can establish to its own satisfaction the identity of the user involved in the authorization dance—rather than trusting ContactLister to do so.

Now that Flickr knows for sure the identity of the app, the permission level requested, and the user involved, it still needs to actually ask Lois whether it’s OK to let ContactLister have the requested read permission. If Lois had not already granted ContactLister such permission, then Flickr presents to Lois a screen that clearly informs her of ContactLister’s request. The fact that such a display comes from Flickr instead of ContactLister directly should give Lois some confidence that Flickr can track what ContactLister will do with any permissions she grants to it and thereby hold the authors of ContactLister accountable.

Step 3: Flickr Lets ContactLister Know to Pick Up a Token

Assuming that Lois grants ContactLister read permission, Flickr must now inform ContactLister of this fact. (Remember, the permission granting is happening on the Flickr site.) Flickr communicates this authorization act by sending the HTTP GET request to the callback-URL for ContactLister with what Flickr calls a frob. Flickr knows the callback-URL to use because part of registering a web application to handle authorization is specifying a callback URL at the following location:

http://www.flickr.com/services/api/keys/{api-key}/

where the api-key is that for the app. In other words, ContactLister must handle a call from Flickr of the following form:

callback-URL?frob={frob}

A frob is akin to a session ID. It lets ContactLister know that some form of authorization has been granted to ContactLister. To actually get the token that ContactLister needs to use the requested read permission, ContactLister needs to use flickr.auth.getToken to exchange the frob for the token. Frobs aren’t meant to be the permanent representation of an authorization act. Frobs expire after 60 minutes or after flickr.auth.getToken is used to redeem the frob for a token. This exchange ensures that ContactLister receives a token and that Flickr knows that ContactLister has received the token. Note that flickr.auth.getToken is also a signed call with two mandatory arguments: api_key and frob—in addition to api_sig, of course. The returned token is expressed in the following form (quoting from http://www.flickr.com/services/?api/?flickr.auth.getToken.html):

<auth>
 <token>976598454353455</token>
 <perms>write</perms>
 <user nsid="12037949754@N01" username="Bees" fullname="Cal H" />
</auth>
            

Note that it’s the token that tells ContactLister the details of what is being authorized: the Flickr user and the permission granted. Now, ContactLister knows the Flickr identity of Lois—without ever needing Lois to tell ContactLister directly.

Step 4: ContactLister Can Now Make an Authorized and Signed Call

ContactLister can now actually make the call to flickr.contacts.getList. How so? In addition to signing a call to flickr.contacts.getList, ContactLister adds the appropriate authorization information by adding the following argument to the call and signing it appropriately:

auth-token={token}

We should note moreover that Lois, like all users, can revoke any permission she had previously granted here:

http://flickr.com/services/auth/list.gne

It’s nice for Lois to know that she doesn’t have to convince ContactLister to stop accessing her account. She just tells Flickr.

Implementation of Authorization in PHP

That’s the narrative of how to do Flickr authorization for web applications. Now let’s look at it implemented in PHP. There are two pieces of code. The first generates the authorization URL. (To use it, use your own API key and secret.)

<?php
 $api_key = "";
 $secret = "f0fc085289c7677a";
 $perms = "read";

 function login_link($api_key,$secret,$perms) {
   # calculate API SIG
   # sig string = secret + [arguments listed alphabetically name/value --
   # including api_key and perms]

   $sig_string = "{$secret}api_key{$api_key}perms{$perms}";
   $api_sig = md5($sig_string);

   $url = "http://flickr.com/services/auth?api_key={$api_key}&perms={$perms}&api_sig={$api_sig}";
   return $url;
 }

 $url = login_link($api_key,$secret,$perms);
?>
<html>
 <body><a href="<?php print($url);?>">Login to Flickr</a></body>
</html>
            

To confirm that you have things set up correctly, if you run the app, you should get a prompt from the Flickr site asking for access (see Figure 6-2).[103]


The second piece of code is the authentication-­handling script whose URL is the callback URL registered to the API key. It reads the frob, gets the token, and then lists the contacts of the user (a type of access that demonstrates that authorization is working, since without authorization, an app will not be able to access a user’s contact list). To try this yourself, you will need to create this file and then enter its URL in the Callback URL field of your app’s key configuration screen at Flickr:[104]

<?php
##insert your own Flickr API KEY here
$api_key = "[API_KEY]";
$secret = "[SECRET]";

$perms = "read";

$frob = $_GET['frob'];

function getResource($url){
 $chandle = curl_init();
 curl_setopt($chandle, CURLOPT_URL, $url);
 curl_setopt($chandle, CURLOPT_RETURNTRANSFER, 1);
 $result = curl_exec($chandle);
 curl_close($chandle);

 return $result;
}

function getContactList($api_key, $secret, $auth_token)  {
 # calculate API SIG
 # sig string = secret + [arguments listed alphabetically name/value --
 # including api_key and perms]; don't forget the method call

 $method = "flickr.contacts.getList";
 $sig_string =
   "{$secret}api_key{$api_key}auth_token{$auth_token}method{$method}";
 $api_sig = md5($sig_string);

 $token_url =
   "http://api.flickr.com/services/rest/?method=flickr.contacts.getList&api_key={$api_key}&auth_token={$auth_token}&api_sig={$api_sig}";
 $feed = getResource($token_url);
 $rsp = simplexml_load_string($feed);

 return $rsp;
}

function getToken($api_key,$secret,$frob) {
 # calculate API SIG
 # sig string = secret + [arguments listed alphabetically name/value --
 # including api_key and perms]; don't forget the method call

 $method = "flickr.auth.getToken";
 $sig_string = "{$secret}api_key{$api_key}frob{$frob}method{$method}";
 $api_sig = md5($sig_string);

 $token_url =
   "http://api.flickr.com/services/rest/?method=flickr.auth.getToken&api_key={$api_key}&frob={$frob}&api_sig={$api_sig}";
 $feed = getResource($token_url);
 $rsp = simplexml_load_string($feed);

 return $rsp;
}

$token_rsp = getToken($api_key,$secret,$frob);
$nsid = $token_rsp->auth->user["nsid"];
$username = $token_rsp->auth->user["username"];
$auth_token = $token_rsp->auth->token;
$perms = $token_rsp->auth->perms;

# display some user info
echo "You are: ", $token_rsp->auth->user["fullname"],"<br>";
echo "Your nsid: ", $nsid, "<br>";
echo "Your username: ", $username,"<br>";
echo "auth token: ", $auth_token, "<br>";
echo "perms: ", $perms, "<br>";

# make a call to getContactList

$contact_rsp = (getContactList($api_key,$secret,$auth_token));
$n_contacts = $contact_rsp->contacts["total"];
$s = "<table>";
foreach ($contact_rsp->contacts->contact as $contact) {
 $nsid = $contact['nsid'];
 $username = $contact['username'];
 $realname = $contact['realname'];
 $s = $s . "<tr><td>{$realname}</td><td>{$username}</td><td>{$nsid}</td></tr>";
}

$s = $s . "</table>";
echo "Your contact list (which requires read permission) <br>";
echo "Number of contacts: {$n_contacts}<br>";
echo $s;
?>
            
[Note]Note

Uploading photos to Flickr is a major part of the Flickr API that is not covered in this book. I suggest reading the documentation (http://www.flickr.com/services/api/upload.api.html) and using one of the API kits.