xAuthでPOSTするパラメータの意味がやっとわかったのでメモメモ

備忘録です.

Tumblrが突然BASIC認証潰しちゃったので半月でGyamblr+が使えなくなるという涙目展開だったのですが,もはや自分が使えなくなってるC++なんて知るか!とC#でいちから書き直してたりします.

その過程でxAuth対応でOAuth認証のソース弄ってて詰まった際に根本の理屈がやっとわかったのでメモっておきます.(注:認証部,別に自前でどうにかしなくても他の人が頑張ってくれてるので,単なる自己満足です)

 

まず前提として,アクセストークンについて.
普段の認証時にIDとパスワードを通信で流したくないため,そのアプリでそのユーザーのみがアクセスできる番号(文字列)があれば便利です.これをアクセストークンと言います.ではアクセストークンを発行する際に投げているパラメータがなんなのか,そしてどうやってWEBサービス側がアプリおよびユーザーの正当性を判断してるのかって話です.

 

POST時のパラメータ

xAuthのアクセストークン(oauth_tokenおよびoauth_token_secret)を要求する際に,POSTするパラメータがいくつかあります.これを3つに分けてみていきます.

 

まず,公開していい情報.隠すつもりも無い情報.同じアプリ間で一意.

  • oauth_consumer_key – WEBサービス側から与えられたアプリの公開ID
  • oauth_version - 1.0 ← バージョン
  • x_auth_mode - client_auth ←xAuthですよって宣言なのでOAuthでは無い
  • oauth_signature_method - HMAC-SHA1   ←暗号化方法

一方,出来るだけ隠しておきたい情報は当然IDとパスワード.

  • x_auth_password - パスワード
  • x_auth_username - ユーザー名

そこで,

  • oauth_timestamp - 認証時刻
  • oauth_nonce - インスタントランダム値

を加えてアルファベット順でソートすることで,一意に決まる署名の種(OAuth signature base)を生成します.(一例として,問題ない範囲で通った値そのままです)

POST&https%3A%2F%2Fwww.tumblr.com%2Foauth%2Faccess_token&oauth_consumer_key%3DMCGZNG0Pgwxlu59Uj06bSAaVPo3JUsC2170wmSwOf6PbOxqZTF%26oauth_nonce%3D4572380%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1347438371%26oauth_version%3D1.0%26x_auth_mode%3Dclient_auth%26x_auth_password%3Dhogehoge%26x_auth_username%3Dhuga%2540gmail.com

ここに,oauth_consumer_secret というWEBサービス側から与えられたアプリの秘密IDというサービス側とアプリ側の非公開情報を秘密鍵として,oauth_signature という署名をアプリ側で生成します.

よって,oauth_consumer_secretという秘密鍵以外の情報は全部WEBサービス側へPOSTし,末尾に付けたoauth_signatureが一致するかでアクセストークン発行が大丈夫かの判定が行われ,正しければアクセストークンを返してきているわけです.

 

この辺の仕組みがわかっていなかったため,OAuthとxAuthの間でPOST内容およびsignature baseがどう変わるのかわからず,四苦八苦してました.言われてみればなるほど,です.

世の中には変数投げ込めば簡単にアクセストークン返ってくるライブラリはありますが,じっくり書いてみると案外ためになって楽しいですね.

以下,自分で認証部書きたいって物好きのために,フィーリングで書いたC#でのxAuthのトークン取得部を置いておきますね.以前Twitterクライアントで丸コピーしたこれを参考に,ちまちまと書き直してみました.元の泥臭さに引きずられたけど,パラメータ多いんだからもっと他の書き方すべきでしたorz

 

public void GetRequestToken(string email, string pass)
{
    NameValueCollection nvc = GenerateNvc(“”);


    //最初のアクセストークン取得時だけID(メアド)とパスワード使用
    nvc.Add(“x_auth_username”, UrlEncode(email));
    nvc.Add(“x_auth_password”, pass);
    nvc.Add(“x_auth_mode”, “client_auth”);

    string signature = GenerateSignature(“”, “POST”, ACCESS_TOKEN_URL, parameters);
    nvc.Add(“oauth_signature”, UrlEncode(signature));

    string response = HttpPost(ACCESS_TOKEN_URL, nvc);
    Dictionary<string, string> dic = ParseResponse(response);
    RequestToken = dic[“oauth_token”];
    RequestTokenSecret = dic[“oauth_token_secret”];
}

private string HttpPost(string url, NameValueCollection n)
{
    //POST部
    WebClient webClient = new WebClient();

    StringBuilder sb = new StringBuilder();

    //UplaodValuesならいらない気もするがおまじない
    webClient.Headers[“Content-Type”] = “application/x-www-form-urlencoded”;
    byte[] resData = webClient.UploadValues(url, n);

    webClient.Dispose();

    //気分の問題だけど明示的にUTF8
    string returnText = UTF8.GetString(resData);

    return returnText ;
}

private NameValueCollection GenerateNvc(string token)
{
   キー追加
    NameValueCollection para = new NameValueCollection();
    para.Add(“oauth_consumer_key”, ConsumerKey);
    para.Add(“oauth_signature_method”, “HMAC-SHA1”);
    para.Add(“oauth_timestamp”, GenerateTimestamp());
    para.Add(“oauth_nonce”, GenerateNonce());
    para.Add(“oauth_version”, “1.0”); ;
    if (!string.IsNullOrEmpty(token))
        para.Add(“oauth_token”, token);
    return para;
}