Twitter の OAuth 1.0 認証 – 解説。


みむらです。

ひとまず、おべんきょーということで、後からも分かるように、
変数を多く使って文字列出力をふんだんに出しながら。

 

ソースコードが、海から来た侵略者に侵略されていますが、
そんなもの全く気にしない、むしろ、侵略されていないでゲソ!

 

サンプルコードは前の記事に。
Twitter の OAuth 1.0 認証を自分でコード書いてやってみた。

 

—-

参考資料:

* http://oauth.net/core/1.0
* http://hueniverse.com/oauth/guide/authentication/
* https://dev.twitter.com/docs/auth/authorizing-request/

—-

・・・あほみたいに長くなることが判明したので、この辺で記事を分割しておきます。
見たい方は「続きを読む」から。

 

下記の部分で触れる各変数、およびパラメータは、特に断りがなければ以下のようになっています。

変数名

説明

consumer_key
(oauth_consumer_key)
Consumer Key (Client Identifier)

アプリケーションを一意に示すのに用いられる値。
consumer_secret
Consumer Secret (Client Secret)

Consumer Key に紐づけられた、検証目的で利用される値。
access_token
(oauth_token)
Access Token

リクエストをする際に用いる、
リクエストとユーザを紐づける為に使われる値。

(認証時に一意な値が発行される。)
access_secret
(oauth_token_secret)
Access Secret

Access Token に紐づけられた、検証目的で利用される値。
request_token
(oauth_token)
Request Token

Access Token / Secret を取得するために一時的に発行される値。
request_secret
(oauth_token_secret)
Request Secret

Request Token に紐づけられた、検証目的で利用される値。
nonce
(oauth_nonce)
Nonce

処理ごとに違う、任意の値を指定する。
unixtime
(oauth_timestamp)
Time stamp

UNIX 時間 (1979-01-01 UTC からの秒数)
signature
(oauth_signature)
Signature

整合性チェックのために、
リクエストを Signature Method (Twitter だと HMAC-SHA1) で
指定した方式でハッシュをとった値。
(oauth_signature_method) Signature Method

Signature を作るためのハッシュ関数を指定。
HMAC-SHA1 / RSA-SHA1 / PLAINTEXT が指定できる。

(Twitter の場合は HMAC-SHA1 のみ)
(oauth_version) Version

今回は 1.0 を利用します。
tweet_data
(status)
Status

つぶやく内容。
hwr HttpWebRequest 型のインタフェース。
これを使って通信します。
param パラメータ文字列を格納。

 


1.Token を取得する。

つぶやくために必要なアクセストークンを取得するために、
Twitter 側に「取得するための一時的なトークン」を発行してもらうプロセスです。

 

このステップでは、 Twitter の 「 https://api.twitter.com/oauth/request_token 」 に、

https://api.twitter.com/oauth/request_token?oauth_consumer_key=oDcUp76alv6VctYvRx2g&oauth_nonce=2179941686152750588&oauth_signature=dC61tOolE2kkHbQilqY7zvrnoRA=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1322307481&oauth_version=1.0

というようなリクエストを POST で行います。

参考: https://dev.twitter.com/docs/api/1/post/oauth/request_token

 

Random rand = new Random();
byte[] nonce_b = new byte[64];

rand.NextBytes(nonce_b);

string nonce = Math.Abs(BitConverter.ToInt64(nonce_b, 0)).ToString();
long unixtime = GetUNIXTime();

//
// OAuth Signature 生成のために、それ以外で仮のリクエスト文字列を作成する。
// この際、各パラメータは ABC 順にソートされている必要がある。
//
string signature_request = "POST&" + UrlEncode("https://api.twitter.com/oauth/request_token");
signature_request += "&" + UrlEncode(MakeParamString(consumer_key, nonce, null, unixtime, null, null, null));

//
// ちゃんとしたリクエスト文字列を作る。
//
string signature = GetSignature(consumer_secret, null, signature_request);
string param = MakeParamString(consumer_key, nonce, signature, unixtime, null, null, null);

//
// 問い合わせ。
//
HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create("https://api.twitter.com/oauth/request_token?" + param);
hwr.Method = "POST";
WebResponse ret = hwr.GetResponse();
verify_token = new StreamReader(ret.GetResponseStream()).ReadToEnd().Split('&');

ret.Close();

 

先頭5行で、 UNIX 時間の取得と、ランダムな数字列を生成しています。

Random rand = new Random();
byte[] nonce_b = new byte[64];

rand.NextBytes(nonce_b);

string nonce = Math.Abs(BitConverter.ToInt64(nonce_b, 0)).ToString();
long unixtime = GetUNIXTime();

nonce_b に生成された 64byte 分の数字列を Int64 型に変換し、 abs で絶対値に変換した後、
文字列に変換しています。

static long GetUNIXTime()
{
    return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}

GetUNIXTime() 関数はこのような感じ。 UNIX Time の定義そのままです。

 

次の4行でリクエスト文字列の作成をしています。

//
// OAuth Signature 生成のために、それ以外で仮のリクエスト文字列を作成する。
// この際、各パラメータは ABC 順にソートされている必要がある。
//
string signature_request = "POST&" + UrlEncode("https://api.twitter.com/oauth/request_token");
signature_request += "&" + UrlEncode(MakeParamString(consumer_key, nonce, null, unixtime, null, null, null));

//
// ちゃんとしたリクエスト文字列を作る。
//
string signature = GetSignature(consumer_secret, null, signature_request);
string param = MakeParamString(consumer_key, nonce, signature, unixtime, null, null, null);

 

MakeParamString 関数は次のような感じ:

// パラメータ文字列を生成
static string MakeParamString(string oauth_consumer_key, string oauth_nonce, string oauth_signature, long oauth_timestamp, string oauth_token, string oauth_verifier, string status)
{
    string param = "oauth_consumer_key=" + oauth_consumer_key + "&oauth_nonce=" + oauth_nonce;

    if (oauth_signature != null)
        param += "&oauth_signature=" + oauth_signature;

    param += "&oauth_signature_method=HMAC-SHA1" + "&oauth_timestamp=" + oauth_timestamp;

    if (oauth_token != null)
        param += "&oauth_token=" + oauth_token;

    if (oauth_verifier != null)
        param += "&oauth_verifier=" + oauth_verifier;
            
    param += "&oauth_version=1.0";

    if (status != null)
        param += "&status=" + status;

    return param;
}

つまり、

oauth_consumer_key=oDcUp76alv6VctYvRx2g&oauth_nonce=1155890576800496532&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1322307152&oauth_version=1.0

みたいな文字列を生成しています。

ただこの際、各パラメータは ABC 順にソートされている必要があります。
(されていないと受理されません。)

 

UrlEncode 関数は次のような感じ:

static string UrlEncode(string str)
{
    string s = HttpUtility.UrlEncode(str);
    return _UrlEncodeUpper(s);
}

static string _UrlEncodeUpper(string str)
{
    int p = str.IndexOf("%");
    if (p != -1)
    {
        str = str.Substring(0, p) + str.Substring(p, 3).ToUpper() + _UrlEncodeUpper(str.Substring(p + 3));
    }
    return str;
}

 

要は、.net の標準ライブラリを用いて URL エンコードを行った後、
再起を利用して %以降の2文字を大文字に置換しています。

 

ここまでで、 signature_request 変数( Signature を生成するための文字列 )には、

POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3DoDcUp76alv6VctYvRx2g%26oauth_nonce%3D3863326961439002923%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1322307289%26oauth_version%3D1.0

というような文字列が入ります。

 

GetSignature 関数は次のような感じ:

static string GetSignature(string consumer_secret, string access_secret, string param)
 {
    HMACSHA1 hmacsha1 = new HMACSHA1();
    hmacsha1.Key = Encoding.ASCII.GetBytes(consumer_secret + "&" + access_secret);
    byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(param));
            
    return Convert.ToBase64String(hash);
}

HMACSHA1クラス
( http://msdn.microsoft.com/ja-jp/library/system.security.cryptography.hmacsha1(v=vs.80).aspx )

これを使って、ハッシュをとります。

 

この際の、生成キーは次の文字列になります。

[consumer_secret]&[access_token_secret]

このキーを用いて先ほど signature_request 変数に格納されている文字列についてハッシュを生成します。

 

ここまで作成が終わったら、パラメータ文字列を生成し直して、

oauth_consumer_key=oDcUp76alv6VctYvRx2g&oauth_nonce=2336380648701352887&oauth_signature=YVFCKdmCaljBmurjzFeJv5XCeRQ=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1322308083&oauth_version=1.0

https://api.twitter.com/oauth/request_token に投げると、

oauth_token=BIYmRMZKhdeZ4hale29OplkF0GjA881suM4g0wQ&oauth_token_secret=cKmEjN45oulQd6mJ3j57T1hDeUL1npV5Zjzgxe7s4js&oauth_callback_confirmed=true

という文字列が帰ってきますので、
このうち oauth_token と oauth_token_secret が
それぞれ Access Token / Secret を取得するために必要になる、Request Token / Secret になります。


2. Access Token を取得する。

1で取得した Request Token / Secret を元に、
つぶやくのに必要な、 Access token / Request を取得するプロセスです。

 

このステップでは、 ブラウザでユーザ認証を行い、 PIN を入力してもらった後、
これらのデータを https://api.twitter.com/oauth/access_token に、

oauth_consumer_key=oDcUp76alv6VctYvRx2g&oauth_nonce=3892287381380482420&oauth_signature=tslyAJpnMxhZNfmusKxzH8YKfsY=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1322308659&oauth_token=H5Awad7SVzUHGkA4tNCDEAi3YFBOvtXZ7z1WNNVCY&oauth_verifier=0712398&oauth_version=1.0

このような文字列を送信します。

参考 : https://dev.twitter.com/docs/api/1/post/oauth/access_token

 

 

string request_token = "";
string request_secret = "";

//
// ブラウザ開いて認証。
//
foreach (string s in verify_token)
{
    int p = s.IndexOf("oauth_token=");
    if (p != -1)
    {
        request_token = s.Substring(p + "oauth_token=".Length);
        break;
    }
}
Process.Start("https://twitter.com/oauth/authorize?oauth_token=" + request_token);

//
// PIN を入力させる。
//
Console.Write("\nINPUT PIN : ");
string pin = Console.ReadLine();

foreach (string s in verify_token)
{
    int p = s.IndexOf("oauth_token_secret=");
    if (p != -1)
    {
        request_secret = s.Substring(p + "oauth_token_secret=".Length);
        break;
    }
}

//
// 以下 Twitter ともくもく通信。
//
Random rand = new Random();
byte[] nonce_b = new byte[64];

rand.NextBytes(nonce_b);

string nonce = Math.Abs(BitConverter.ToInt64(nonce_b, 0)).ToString();
long unixtime = GetUNIXTime();

//
// Signature 生成のための一時的なリクエスト文字列を作成。
//
string signature_request = "POST&" + UrlEncode("https://api.twitter.com/oauth/access_token");
signature_request += "&" + UrlEncode(MakeParamString(consumer_key, nonce, null, unixtime, request_token, pin, null));

string signature = GetSignature(consumer_secret, request_secret, signature_request);
string param = MakeParamString(consumer_key, nonce, signature, unixtime, request_token, pin, null);

//
// 問い合わせ。
//
HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create("https://api.twitter.com/oauth/access_token?" + param);
hwr.Method = "POST";
hwr.Headers.Add("Authorization", "OAuth");

WebResponse ret = hwr.GetResponse();
verify_token = new StreamReader(ret.GetResponseStream()).ReadToEnd().Split('&');

ret.Close();

foreach (string s in verify_token)
{
    if (s.IndexOf("oauth_token=") != -1)
        access_token = s.Substring(s.IndexOf("=") + 1);

    if (s.IndexOf("oauth_token_secret=") != -1)
        access_secret = s.Substring(s.IndexOf("=") + 1);
}

 

まず始めに、
先ほど取得した Request Token を元に、ブラウザで

https://twitter.com/oauth/authorize?oauth_token=[request_token]

にアクセスさせます。

image

そうしますと、今回の場合こんなのが出てきますので、「認証」

image

出てきた文字列を打ってもらう。

 

あとはそれを元に、pin を oauth_verifier=pin として付け加えて、次のような文字列を作り、

POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Faccess_token&oauth_consumer_key%3DoDcUp76alv6VctYvRx2g%26oauth_nonce%3D7576447428639206328%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1322309766%26oauth_token%3D9WSdHNafGF0uRHbkgW7i1jDaMRyVRggwg1T6wzElTI%26oauth_verifier%3D9525882%26oauth_version%3D1.0
上記の文字列から Signature を生成して、次のようにパラメータ文字列を作り直し、
oauth_consumer_key=oDcUp76alv6VctYvRx2g&oauth_nonce=7576447428639206328&oauth_signature=Ft4AgiIaRONzOkHmJ1P9IAE3N7s=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1322309766&oauth_token=9WSdHNafGF0uRHbkgW7i1jDaMRyVRggwg1T6wzElTI&oauth_verifier=9525882&oauth_version=1.0

https://api.twitter.com/oauth/access_token に投げると、

oauth_token=77219798-*************&oauth_token_secret=*****************************&user_id=77219798&screen_name=mimura1133

というような文字列が帰ってきます。

この際の oauth_token および oauth_token_secret は、
Access Token および Access Secret になります。

(この値は表に出ると大変衛生上よろしくないので、伏せ字にしています。)

 


3.つぶやく。

つぶやきます。

 

このステップでは、 https://api.twitter.com/1/status/update.xml に、

Authorization: OAuth oauth_consumer_key="oDcUp76alv6VctYvRx2g",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1322310097",oauth_nonce="1334809891491345235",oauth_version="1.0",oauth_token="77219798-********************",oauth_signature="**********************",

というようなヘッダを付けて、

status=%E3%82%B2%E3%82%BD%E3%83%BC%E3%82%B2%E3%82%BD%E3%83%BC&include_entities=true

というようなデータを送信します。

 

ただ今回はURL にくっつけるのではなく、

POST https://api.twitter.com/1/statuses/update.xml HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: api.twitter.com
Authorization: OAuth oauth_consumer_key="iOQHfiCUsyOyamW8JJ8jg",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1322297289",oauth_nonce="5194364",oauth_version="1.0",oauth_token="77219798-**********************",oauth_signature="***********************",
Content-Length: 110

status=%E3%81%A7%E3%82%93%E3%81%A7%E3%82%89%E3%82%8A%E3%82%85%E3%83%BC%E3%81%B0%E3%83%BC&include_entities=true

こんな感じのリクエストになります。

 

Random rand = new Random();
byte[] nonce_b = new byte[64];

rand.NextBytes(nonce_b);

string nonce = Math.Abs(BitConverter.ToInt64(nonce_b, 0)).ToString();
long unixtime = GetUNIXTime();

string tweet_data = UrlEncode(tweet_text);

string signature_request = "POST&" + UrlEncode("https://api.twitter.com/1/statuses/update.xml");
signature_request += "&include_entities%3Dtrue%26" + UrlEncode(MakeParamString(consumer_key, nonce, null, unixtime, access_token, null, tweet_data));
string signature = GetSignature(consumer_secret, access_secret, signature_request);

string header_param = "OAuth oauth_consumer_key=\"" + UrlEncode(consumer_key) + "\",oauth_signature_method=\"HMAC-SHA1\"," +
                        "oauth_timestamp=\"" + unixtime + "\",oauth_nonce=\"" + UrlEncode(nonce) + "\"," +
                        "oauth_version=\"1.0\",oauth_token=\"" + UrlEncode(access_token) + "\"," +
                        "oauth_signature=\"" + UrlEncode(signature) + "\",";

HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create("https://api.twitter.com/1/statuses/update.xml");
hwr.Method = "POST";
hwr.ServicePoint.Expect100Continue = false;
hwr.Headers.Add(HttpRequestHeader.Authorization, header_param);
hwr.ContentType = "application/x-www-form-urlencoded";
var s = new StreamWriter(hwr.GetRequestStream());
s.Write("status=" + tweet_data + "&include_entities=true");
s.Close();

WebResponse ret = hwr.GetResponse();

 

まずは、つぶやく内容を URLEncode して、送信するデータを作成します。

次に、 “ status=[つぶやく中身] “として追加し、パラメータ文字列を作り、 Signature を計算します。

POST&https%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.xml&include_entities%3Dtrue%26oauth_consumer_key%3DoDcUp76alv6VctYvRx2g%26oauth_nonce%3D1334809891491345235%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1322310097%26oauth_token%3D77219798-****************************%26oauth_version%3D1.0%26status%3D%25E3%2582%25B2%25E3%2582%25BD%25E3%2583%25BC%25E3%2582%25B2%25E3%2582%25BD%25E3%2583%25BC

次に、HTTP リクエストのヘッダにくっつける、認証情報を作成します。

                string header_param = "OAuth oauth_consumer_key=\"" + UrlEncode(consumer_key) + "\",oauth_signature_method=\"HMAC-SHA1\"," +
                                      "oauth_timestamp=\"" + unixtime + "\",oauth_nonce=\"" + UrlEncode(nonce) + "\"," +
                                      "oauth_version=\"1.0\",oauth_token=\"" + UrlEncode(access_token) + "\"," +
                                      "oauth_signature=\"" + UrlEncode(signature) + "\",";

文字列としては、

OAuth oauth_consumer_key="oDcUp76alv6VctYvRx2g",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1322310097",oauth_nonce="1334809891491345235",oauth_version="1.0",oauth_token="77219798-*****************",oauth_signature="****************",

このように、

hogehoge="gesogeso"

みたいな文字列を、カンマで区切った形の文となります。

 

この際、 Signature を URLEncode してあげる必要があります。

エンコードを忘れたり、ヘッダに付ける文字列が正しく無かったりすると、
Invalid … という文字列が帰ってきたり、
Could not authenticase with OAuth.

というエラーが、送信したときに Twitter 側から帰ってきます。

 

全部生成が完了したら、

https://api.twitter.com/1/status/update.xml に対して送信する際、
Header について以下の要素を指定します。

ContentType に application/x-www-form-urlencoded を指定し、
Authorization に 先ほど header_param に生成した文字列を指定します。

hwr.Headers.Add(HttpRequestHeader.Authorization, header_param);
hwr.ContentType = "application/x-www-form-urlencoded";

そして、 Message 部分に

status=[つぶやく内容を URL エンコードしたもの]&include_entities=true

を付けて送信します。

 

全部完了すると、ちゃんとつぶやかれます。

 

ということで、サンプルコードは、前の記事を参照してください。
Twitter の OAuth 1.0 認証を自分でコード書いてやってみた。

 

ではではー。


 

関連記事