/ OAuth

[PHP] Flickr OAuth PHP 認證小筆記

最近因為 Flickr 明年說要把舊的認證方式拿掉,所以就找時間研究一下他的 OAuth 的運作方式。官方雖然是有文件說明,但是,他的簽名方式我永遠都沒辦法簽過。而且更詭譎的地方是,我都已經按照官方說明來作簽章了,但是只要送去給 flickr 他永遠都說我的簽名錯誤(眼神死)。

有鑑於此,所以我就開始找尋 PHP 關於 OAuth 的解決方案。


雖然我本身是使用 CakePHP framework,不過我還是傾向於更簡便的使用。既然自己刻老是被 flickr 婊到說簽名不正確,那我用別人做好的函式庫總是可以了吧。所以,我就挑了 oauth-php 這一套來用。也許你會問說,為什麼不用 PECL::OAuth 就好了?其實,這種需要安裝在系統中的東西,如果遇上環境不允許的話就哭哭了!

oauth-php 的運作方式很簡單,他也提供了很多種儲存方法。在他的 wiki 上面有簡易的說明了一下(ConsumerHowTo),所以在使用上應該不會有太大的問題。不過,由於他的官方文件相對少,所以很多地方還是去看原始碼會比較快一點(喂

認證方式可以跑這兩種:

  • 2-Legged OAuth 就是只有兩個參與者的 OAuth。
  • 3-Legged OAuth 有第三方參與者的 OAuth。一般來說 OAuth 都泛指使用 3-Legged 的方式。詳細關於 OAuth 的資料可以參考維基百科的說明。

不要問我 flickr 是屬於哪一種。

我從 Flickr 來實驗,使用 2-Legged 可以做到 RequestToken 的部份,但是繼續往下取得 AccessToken 的地方,就會出現oauth_problem=signature_invalid的錯誤訊息。這個錯誤對於這一套工具來說是無解的,你必須使用 3-Legged 才能解開(對我來說目前是這樣)。

所以我做了一套 flickMDr 用來把 flickr 的相片轉換成 markdown 的語法,方便我在 Octopress 上面貼圖。這裡提供認證的原始碼給大家參考一下。

define("FLICKR_CONSUMER_KEY", "/* Fill your flickr api key. */");
define("FLICKR_CONSUMER_SECRET", "/* Fill your flickr api secret. */");
define("FLICKR_REST_API", "http://api.flickr.com/services/rest");

define("FLICKR_OAUTH_HOST", "http://www.flickr.com/services");
define("FLICKR_REQUEST_TOKEN_URL", FLICKR_OAUTH_HOST . "/oauth/request_token");
define("FLICKR_AUTHORIZE_URL", FLICKR_OAUTH_HOST . "/oauth/authorize");
define("FLICKR_ACCESS_TOKEN_URL", FLICKR_OAUTH_HOST . "/oauth/access_token");

define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"]));

define('ROOT', dirname(dirname(__FILE__)));
define('DS', DIRECTORY_SEPARATOR);
define('HOST', 'http://'.$_SERVER['HTTP_HOST']);

if(session_id() == "") session_start();

require_once ROOT . DS . "lib" . DS . "OAuthStore.php";
require_once ROOT . DS . "lib" . DS . "OAuthRequester.php";

$options = array(
    'consumer_key' => FLICKR_CONSUMER_KEY,
    'consumer_secret' => FLICKR_CONSUMER_SECRET,
    'server_uri' => FLICKR_OAUTH_HOST,
    'signature_methods' => array('HMAC-SHA1'),
    'request_token_uri' => FLICKR_REQUEST_TOKEN_URL,
    'authorize_uri' => FLICKR_AUTHORIZE_URL,
    'access_token_uri' => FLICKR_ACCESS_TOKEN_URL
);

$store = OAuthStore::instance("Session", $options);

try {

    if (!isset($_SESSION['flickr_oauth_token']) || empty($_SESSION['flickr_oauth_token'])) {

        $params = array(
            'oauth_nonce' => time(),
            'oauth_timestamp' => time(),
            'oauth_signature_method' => 'HMAC-SHA1',
            'oauth_version' => '1.0',
            'oauth_callback' => HOST.'/auth/'
        );

        $tokenResultParams = OAuthRequester::requestRequestToken(FLICKR_CONSUMER_KEY, 0, $params);
        header("Location: " . FLICKR_AUTHORIZE_URL . "?oauth_token=". $tokenResultParams['token']."&perms=read");
    } else {
        $oauthToken = isset($_GET['oauth_token']) ? $_GET['oauth_token'] : $_SESSION['flickr_oauth_token'];
        try {
            OAuthRequester::requestAccessToken(FLICKR_CONSUMER_KEY, $oauthToken, 0, 'POST', $_GET);
        } catch(OAuthException2 $e) {
            header("Location: ".HOST);
        }

        $_SESSION['flickr_oauth_token'] = $oauthToken;

        header("Location: ".HOST);
    }
} catch(OAuthException2 $e) {
    header("Location: ".HOST);
}

程式碼寫得不是很乾淨,不過這算是簡易的範例給大家參考一下。OAuth 的簽名方式真的很奇怪,我雖然都依照 flickr 官方的作法去產生簽名,但是,不管我怎麼送永遠都是拿到 401 實在很令人無奈啊(眼神死

其實,由於我每次都沒有儲存來用這個東西的人的 token,所以照理說使用 2-Legged 應該可以了。但是,我使用 2-Legged 的方式實做,卻還是遇到了oauth_problem=signature_invalid的問題,我把送出的完整 URL 拆出來看,也是完全依照 flickr 官方的範例規格,至於為什麼不能傳送,我想應該 flickr 的文件還是有什麼地方沒有交代清楚吧。

或者說,我還是回頭去把 OAuth 的完整認證方式給搞清楚再說囉(飄走