読者です 読者をやめる 読者になる 読者になる

肉とビールとパンケーキ by @sotarok

少し大人になった「肉とご飯と甘いもの」

Flickr API の認証とAPI を使った画像アップロードの簡単なまとめ

FlickrAPI経由でアップロードしたりするのには、少々厄介なカンジだったので、まとめてみました。

FlickrAPI は、本当にマッシュアップ(笑)用というか、そのアプリ(ウェブアプリ、デスクトップアプリにかかわらず)の開発者のAPIキーで他人がアップロードできるようになっていて、そのため、そのアプリを利用する人は自分のメールアドレスとパスワードをそのアプリ・開発者に知らせずにAPIを利用したアプリを利用できるようになっています。そのため認証周りが多少面倒になっているので、ちょっとした注意とプロセスが必要です。*1

最低限やることの手順

色々考えると面倒なので、今回は、「APIを使って、自分のアカウントにアップロードしたい」ということに限って説明します。

やる必要がある操作の手順:

  • FlickrAPIキーの取得
  • Frobを取得 (※1)
  • 取得した(※1)のFrobをもって、Authentication (認証) リクエストして、認証リクエスト用ページのURLを取得
  • Flickrにログインした状態で認証リクエスト用ページにアクセスし、アプリケーションに権限を与える(OK!ってする)
  • (※1)のFrobをもってTokenを取得する
  • このTokenを使うと、自分のアカウントにアップロードなどできる


そんな流れです。

まず、認証の流れですが、Flickr API の認証 - まちゅダイアリー(2005-11-01) に詳しい流れが書いてあります。すごくわかりやすいです。
あと、Flickr Services にも具体的に例を踏まえて説明が書いてあります。こちらのドキュメントも結構充実しています。*2

FlickrAPI キーを取得

これは良いですよね。あまり難しく考えないで、http://www.flickr.com/services/api/keys/apply/ からNon-Commercial API Key などを取得します。

Frob を取得

API で認証をおこなう前にまず Frob というアクセスキーのようなものを取得しなければいけません。API でいう、flickr.auth.getFrob というメソッドを叩きます。

PEAR::HTTP_Request2 を使ったPHPコードの例:

<?php
define("FLICKR_REST",   "http://api.flickr.com/services/rest/");
define("FLICKR_AUTH",   "http://flickr.com/services/auth/");
define("FLICKR_UPLOAD", "http://api.flickr.com/services/upload/");

$apikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; # api key
$secret = "xxxxxxxxxxxxxxxx"; # api key と一緒に取得できるsecretキー

$args = array(
    'method' => 'flickr.auth.getFrob',
    'api_key' => $apikey,
);
$args['api_sig'] = getSig($secret, $args);
$url = FLICKR_REST . "?" . http_build_query($args);

$Req = new HTTP_Request2();
$Req->setUrl($url);
$res = $Req->send();
$rsponse = simplexml_load_string($res->getBody());

echo "frob is : " . (string)$rsponse->frob . "\n";


※ Signing について

getSig() って関数ですが、これは シグネチャを生成する関数です。Flickr Services の 8 に書いてありますが、アクセスごとに secret を使って生成したシグネチャを一緒に投げなければいけません。

公式のドキュメントを簡単に説明すると、シグネチャは以下のものです。

  • アクセスするときのgetパラメータを、キーのアルファベット順に並び替え
  • キー値キー値 ... となるように文字列を連結
  • それを API Key と一緒に取得できる secret と連結して、md5

です。

で、それに従った getSig() 関数は以下のようにしています。

<?php
function getSig($secret, $args)
{
    ksort($args);
    $s = "";
    foreach($args as $k => $v){
        $s .= $k . $v;
    }
    return md5($secret . $s);
}
Authentication (認証) リクエストして、認証リクエスト用ページのURLを取得

Webアプリやデスクトップアプリであれば、リクエスト用ページのURLを取得したら、リダイレクトまたはそのページをブラウザで開き、認証させれば良いのですが、プログラム経由では、URLをゲットしつつ、ブラウザ経由でそれを認証しなければいけません。

そんなわけで、上で取得したFrobを用いて、認証用URLにアクセスします。

<?php
// ↑で使ったdefineとgetSigは定義してあるものとします
// $frob には、↑で取得した frob が入っているものとします。
// $Req も↑の使いまわし

$args = array(
    'api_key' => $apikey,
    'frob' => $frob,
    'perms' => 'delete',
    // ほしい権限が read だけでよければ 'read'、read/write なら 'write'
    // read/write/delete なら 'delete'
);
$args['api_sig'] = getSig($secret, $args);
$url = FLICKR_AUTH . "?" . http_build_query($args);
$Req->setUrl($url);
$res = $Req->send();
echo $res->getHeader('location') . "\n";


こんなかんじです。こうすると、以下のような文字列が手に入ります。

/signin/?acf=%2Fservices%2Fauth%2F%3Fapi_key%3Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%26frob%3Dxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx-xxxxx%26perms%3Ddelete%26api_sig%3Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

このURLにアクセスすることで、このAPIキーに対して権限を与えることができます。

多少混乱しやすいのですが、ここの時点では、このAPIキーのこの権限を、誰のどのアカウントに対して取得するのかは決まっていません。つまり、getFrob→authenticationの流れで、このURL(都度、Frobが唯一)をいくつも取得することができます。この唯一のFrobをもって、権限を与えたいアカウントでURLにアクセスすることで(あるいは、アクセス後、ログインすることで)、「APIキー・Frobの組み合わせ」と「権限のほしいユーザアカウント」をひもづけることになります。この紐付けを証明するものが「Token」です。

認証リクエスト用ページにアクセスしてOK

説明が前後してしまいましたが、↑で得た認証要URLにアクセスすると、権限を与えるかどうかの問いが出てきます。(Flickrには現在自分のアカウントでログインしているものとします)。

http://www.flickr.com/signin/?acf=%2Fservices%2Fauth%2F%3Fapi_key%3Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%26frob%3Dxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx-xxxxx%26perms%3Ddelete%26api_sig%3Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

で、OKします。

Tokenを取得する

上の操作によって、API Key、Frobとユーザアカウントを結びつけることができました。
これで、Tokenを取得することができます。

<?php
// 設定は上と同じです

$args = array(
    'method' => 'flickr.auth.getToken',
    'api_key' => $apikey,
    'frob' => $frob,
);
$args['api_sig'] = getSig($secret, $args);
$url = FLICKR_REST . "?" . http_build_query($args);
$Req->setUrl($url);
$res = $Req->send();
echo $res->getBody();

こうすると、以下のようなXMLが得られます。

<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<auth>
        <token>xxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx</token>
        <perms>delete</perms>
        <user nsid="xxxxxxxx@xxx" username="sotarok" fullname="" />
</auth>
</rsp>

ここで取得できたTokenとAPI Keyの組み合わせによって、誰のアカウントに対して操作するのが決められます(今回は、自分のアカウントに対して、です)。
一度このTokenを取得すれば、以後、アップロードのたびにこのTokenを使うだけです。他の人のアカウントに対して権限を求めたりしない限り、これまでの操作は以後必要ありません。

自分のアカウントにアップロードなどできる
<?php
// 設定は上と同じで、かつ $auth_token に取得したTokenが入っているものとします

$Req = new HTTP_Request2();
$Req->setMethod(HTTP_Request2::METHOD_POST);
$Req->setUrl(FLICKR_UPLOAD);
$args = array(
    'api_key' => $apikey,
    'auth_token' => $auth_token,
    'frob' => $frob,
    // その他のパラメータは http://www.flickr.com/services/api/upload.api.html に詳しく書いてある
);
$args['api_sig'] = getSig($secret, $args); // sig の生成に photo は含めません
$Req->addPostParameter($args);
$Req->addUpload("photo", $file);
$res = $Req->send();
echo $res->getBody();

レスポンスはXMLで写真のIDが取得できます

まとめ

本来は、アップロード系APIを使うアプリを利用すれば、アップローダクライアントなんかが実装できるわけですね。で、ユーザがapikeyを取得しなくても、アプリの開発者のapikeyとユーザアカウントとTokenで結びつけることによって、アップロードが可能になる、そんな使い方だと思います。ウェブアプリで使う場合は、ユーザアカウントとFlickr Auth Tokenを結びつけてDBに保存しておいたりするような感じでしょうか。

Flickr Services にあげられている、PEAR::Flickr_APIにはアップロード系処理ができなかったり*3(ちなみにphpFlickrのほうにはあるっぽい。実装が微妙なかんじだけど。。)、かの Zend_Service_Flickr もアップロード系処理はなかったりと、PHPには粒ぞろいの役に立たないライブラリたちがたくさんでお兄さんも嬉しいです。
近いうちServices_Flickrでも作ります。たぶん。*4
そういえばServics_Flickrは以前PEARのProposalに出ていたのですが、開発者の支援やフィードバックが得られないということで、Proposal削除されてしまいました。*5

openpearならそんな心配はありませんね!!!やりましたね!

参考にしたところ

*1:メールアドレス+パスワードでもアップロードできるようですが、まぁ今回はauthを取得するための具体例として。

*2:で、なんでまとめようかと思ったかというと、どこで取得したFrobやTokenがどこで使えるのかが微妙にわかりづらかったからです。

*3:つかPEAR::とかいってるけどあれはどうなんだ?

*4:でも認証周りの解決はそれなりに面倒ですね

*5:参考:http://d.hatena.ne.jp/sotarok/20080507/1210180634

*6:最初IDまちがえちゃってトラバとばしちゃった><