自作TwitterクライアントをOAuth認証に対応させる。の続き。
- クライアントがTwitterにRequest Tokenを要求する。
- TwitterがクライアントにRequest Tokenを返す。
- ユーザにAuthorizeページでクライアントアプリケーションを「許可」してもらう。
- Twitterが暗証番号を表示する。
- ユーザがクライアントに暗証番号を入力する。
- クライアントがTwitterにAccess Tokenを要求する。
- TwitterがクライアントにAccess Tokenを返す。
- クライアントにAccess TokenとAccess TokenSecretを保存する。
- 以降、クライアントはAccess Tokenを含めてAPIの各リクエストをする。
各手順では細かいパラメータがあります。このうちクライアントに関連づける必須のパラメータConsumer keyと対になるConsumer Secretを取得するためにクライアント開発者はいかのページアクセスしてフォームを埋める必要があります。
「Twitter / Applications: Register」 http://twitter.com/apps/new
Application Name:にアプリケーション名(twitterという文字列を含むことができません!)を入力、Description:に説明(10文字以上)を入力します。Application Website:は必須の項目になっていますので、今回は自分のホームページを設定します。公開クライアントの場合はクライアントのダウンロードサイトやサポートサイト等を指定します。Application Type:はデスクトップアプリケーションの場合Clientを選択します。
これらはあとからでも修正できます。
入力を終え[Save]をクリックすると、Consumer key と Consumer secret の値が表示されます。アプリケーションの端末に保存して使用します。
自分のアプリは複数登録できて、以下のページより入力した情報の確認と修正が行えます。
[Link]「Twitter / Applications」 http://twitter.com/apps
あとは実装です。私の自作クライアントはC#を使用していましたので、「Twitter API Wiki / OAuth Examples」 のリンク先の.NETのさらにリンク先などを参考にしました。それ以外にもすでにいろいろなところでライブラリやサンプルも提供されているようですが、勉強も兼ねてそれらを参考にあえて自分でライブラリ部分もコーディングしました。アプリケーションは公開しませんが、改めて作成して、ライブラリ部分のコードの主要部分をこちらに公開しようかと思います。
Const.cs(定数クラス、IntelliSenseを使いたいだけです(笑))
namespace TwitterUtils
{
/// <summary>
/// constant
/// </summary>
public static class Const
{
//URL
public const string URL_TWITTER = "http://twitter.com/";
public const string URL_API_TWITTER = "http://api.twitter.com/";
public const string URL_API_TWITTER_1 = "http://api.twitter.com/1/";
//API
public const string API_REQUEST_TOKEN = URL_API_TWITTER + "oauth/request_token";
public const string API_AUTHORIZE = URL_API_TWITTER + "oauth/authorize";
public const string API_ACCESS_TOKEN = URL_API_TWITTER + "oauth/access_token";
public const string API_STATUSES = URL_API_TWITTER_1 + "statuses/";
//API_METHOD
public const string API_METHOD_PUBLIC_TIMELINE = "public_timeline";
public const string API_METHOD_HOME_TIMELINE = "home_timeline";
public const string API_METHOD_FRIENDS_TIMELINE = "friends_timeline";
public const string API_METHOD_USER_TIMELINE = "user_timeline ";
public const string API_METHOD_MENTIONS = "mentions";
public const string API_METHOD_FRIENDS = "friends";
public const string API_METHOD_FOLLOWERS = "followers";
public const string API_METHOD_SHOW = "show";
public const string API_METHOD_UPDATE = "update";
public const string API_METHOD_DESTROY = "destroy ";
public const string API_METHOD_RETWEET = "retweet";
public const string API_METHOD_RETWEETS = "retweets";
//FORMAT
public const string FORMAT_XML = ".xml";
public const string FORMAT_JSON = ".json";
public const string FORMAT_ATOM = ".atom";
public const string FORMAT_RSS = ".rss";
//URL query parameter
public const string QUERY_OAUTH_CONSUMER_KEY = "oauth_consumer_key";
public const string QUERY_OAUTH_NONCE = "oauth_nonce";
public const string QUERY_OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
public const string QUERY_OAUTH_TOKEN = "oauth_token";
public const string QUERY_OAUTH_TOKEN_SECRET = "oauth_token_secret";
public const string QUERY_OAUTH_TIMESTAMP = "oauth_timestamp";
public const string QUERY_OAUTH_VERSION = "oauth_version";
public const string QUERY_OAUTH_SIGNATURE = "oauth_signature";
public const string QUERY_OAUTH_VERIFIER = "oauth_verifier";
public const string QUERY_STATUS = "status";
public const string QUERY_IN_REPLY_TO_STATUS_ID = "in_reply_to_status_id";
//constant values
public const string VALUE_OAUTH_VERSION = "1.0";
public const string VALUE_HMAC_SHA1 = "HMAC-SHA1";
public const string HTTPMETHOD_GET = "GET";
public const string HTTPMETHOD_POST = "POST";
}
}
BasicUtil.cs (名前をBasicUtilという名前にしましたが、Basic認証とは何の関係もありません。単なる文字列操作のメソッドです。)
namespace TwitterUtils
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Util methods
/// </summary>
public static class BasicUtil {
/// <summary>
/// UrlEncode
/// </summary>
/// <param name="src">string</param>
/// <returns>encoded string</returns>
public static string UrlEncode(string src)
{
string escaped = Uri.EscapeDataString(src);
StringBuilder sb = new StringBuilder(escaped.Length);
foreach (var c in escaped)
{
if (c <= 0x2A && "!&'()*".IndexOf(c) > -1)
{
sb.AppendFormat("%{0:X2}", (int)c);
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
/// <summary>
/// IDictionary<string,string> object => Url query string
/// </summary>
/// <param name="dic">IDictionary<string,string> object</param>
/// <returns>Url query string</returns>
public static string BuildQueryString(IDictionary<string, string> dic)
{
StringBuilder sb = new StringBuilder();
foreach (var item in dic)
{
sb.Append(item.Key).Append('=').Append(item.Value);
sb.Append('&');
}
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
/// <summary>
/// Url query string => IDictionary<string,string> object
/// </summary>
/// <param name="queryString">Url query string</param>
/// <returns>IDictionary<string,string>IDictionary object</returns>
public static IDictionary<string, string> QueryStringToDictionary(string queryString)
{
queryString = queryString.Trim('?');
var dic = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(queryString))
{
string[] qs = queryString.Split('&');
foreach (var item in qs)
{
string[] nv = item.Split('=');
dic.Add(nv[0], nv[1]);
}
}
return dic;
}
}
}
OAuthUtil.cs (OAuth認証の各手順で使用する値の計算メソッド群です。)
namespace TwitterUtils
{
using System;
using System.Text;
using System.Security.Cryptography;
/// <summary>
/// Utility methods for OAuth
/// </summary>
public static class OAuthUtil
{
static Random random = new Random(Environment.TickCount);
/// <summary>
/// Hash method using HMACSHA1
/// </summary>
/// <param name="keyBytes">key</param>
/// <param name="dataBytes">data</param>
/// <returns>result</returns>
public static byte[] ComputeHash(byte[] keyBytes, byte[] dataBytes)
{
HMACSHA1 hmacsha1 = new HMACSHA1();
hmacsha1.Key = keyBytes;
byte[] resultBytes = hmacsha1.ComputeHash(dataBytes);
return resultBytes;
}
/// <summary>
/// Hash method using HMACSHA1
/// </summary>
/// <param name="keyString">key string</param>
/// <param name="dataString">data string</param>
/// <param name="enc">encoding (from string to byte)</param>
/// <returns>result string</returns>
public static string ComputeHash(string keyString, string dataString, Encoding enc)
{
byte[] keyBytes = enc.GetBytes(keyString);
byte[] dataBytes = enc.GetBytes(dataString);
return Convert.ToBase64String(ComputeHash(keyBytes, dataBytes), Base64FormattingOptions.None);
}
/// <summary>
/// TIME Stamp
/// </summary>
/// <returns>TIME Stamp</returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// random integer between 123400 - 9999999
/// </summary>
/// <returns>random integer string</returns>
public static string GetNonce()
{
return random.Next(123400, 9999999).ToString();
}
}
}
OAuthURLBuilder.cs(OAuth認証で使用するURLを組み立てるメソッドがあります。token、token_secret等を保持させるのでインスタンス化が可能なクラスにしました。)
namespace TwitterUtils
{
using System.Collections.Generic;
using System.Text;
/// <summary>
/// OAuth URL methods
/// </summary>
public class OAuthURLBuilder
{
string oauth_consumer_key;
string oauth_consumer_secret;
string oauth_token;
string oauth_token_secret;
/// <summary>
/// constractor
/// </summary>
/// <param name="oauthConsumerKey">oauthConsumerKey</param>
/// <param name="oauthConsumerSecret">oauthConsumerSecret</param>
public OAuthURLBuilder(string oauthConsumerKey, string oauthConsumerSecret)
{
oauth_consumer_key = oauthConsumerKey;
oauth_consumer_secret = oauthConsumerSecret;
oauth_token = "";
oauth_token_secret = "";
}
/// <summary>
/// sets OAuth token
/// </summary>
/// <param name="token">OAuth token</param>
/// <returns>OAuthURLBuilder object</returns>
public OAuthURLBuilder SetOAuthToken(string token)
{
oauth_token = token;
return this;
}
/// <summary>
/// sets OAuth tokenSecret
/// </summary>
/// <param name="tokenSecret">OAuth tokenSecret</param>
/// <returns>OAuthURLBuilder object</returns>
public OAuthURLBuilder SetOAuthTokenSecret(string tokenSecret)
{
oauth_token_secret = tokenSecret;
return this;
}
/// <summary>
/// Gets Signature
/// </summary>
private static string GetSignature(string httpMethod, string url, string queries, string consumerSecret, string tokenSecret)
{
string sigData = string.Format("{0}&{1}&{2}", httpMethod, BasicUtil.UrlEncode(url), BasicUtil.UrlEncode(queries));
string sigKey = string.Format("{0}&{1}", BasicUtil.UrlEncode(consumerSecret), BasicUtil.UrlEncode(tokenSecret));
return OAuthUtil.ComputeHash(sigKey, sigData, Encoding.UTF8);
}
/// <summary>
/// get twitter API Url with OAuth
/// </summary>
/// <param name="httpMethod">Http method (GET/POST)</param>
/// <param name="originalUrl">Original Url</param>
/// <param name="queries">url queries</param>
/// <returns>twitter API Url with OAuth</returns>
public string GetURL(string httpMethod, string originalUrl, IDictionary<string, string> queries)
{
var paramsDic = GetDictionaryWithOAuthParams();
foreach (var en in queries)
{
paramsDic.Add(en.Key, en.Value);
}
if (!string.IsNullOrEmpty(oauth_token))
{
paramsDic.Add(Const.QUERY_OAUTH_TOKEN, oauth_token);
}
string queryString = BasicUtil.BuildQueryString(paramsDic);
string signature = GetSignature(httpMethod, originalUrl, queryString, oauth_consumer_secret, oauth_token_secret);
paramsDic.Add(Const.QUERY_OAUTH_SIGNATURE, BasicUtil.UrlEncode(signature));
return string.Format("{0}?{1}", originalUrl, BasicUtil.BuildQueryString(paramsDic));
}
/// <summary>
/// Gets DictionaryWithOAuthParams
/// </summary>
private SortedDictionary<string, string> GetDictionaryWithOAuthParams()
{
return new SortedDictionary<string, string> {
{ Const.QUERY_OAUTH_CONSUMER_KEY, oauth_consumer_key },
{ Const.QUERY_OAUTH_NONCE, OAuthUtil.GetNonce()},
{ Const.QUERY_OAUTH_SIGNATURE_METHOD, Const.VALUE_HMAC_SHA1 },
{ Const.QUERY_OAUTH_TIMESTAMP, OAuthUtil.GetTimeStamp() },
{ Const.QUERY_OAUTH_VERSION, Const.VALUE_OAUTH_VERSION },
};
}
/// <summary>
/// gets RequestTokenURL
/// </summary>
/// <returns>RequestTokenURL</returns>
public string GetRequestTokenURL()
{
return GetURL(
Const.HTTPMETHOD_GET,
Const.API_REQUEST_TOKEN,
new Dictionary<string, string>());
}
/// <summary>
/// gets AccessTokenURL
/// </summary>
/// <param name="pin">pin number</param>
/// <returns>AccessTokenURL</returns>
public string GetAccessTokenURL(string pin)
{
return GetURL(
Const.HTTPMETHOD_GET,
Const.API_ACCESS_TOKEN,
new Dictionary<string, string>() { { Const.QUERY_OAUTH_VERIFIER, pin } });
}
/// <summary>
/// Gets AuthorizeURL
/// </summary>
/// <returns>Authorize page URL</returns>
public string GetAuthorizeURL()
{
var queries = new Dictionary<string, string>() { { Const.QUERY_OAUTH_TOKEN, oauth_token } };
return string.Format("{0}?{1}", Const.API_AUTHORIZE, BasicUtil.BuildQueryString(queries)); ;
}
}
}
で、これらを使用するコードMain()の含まれるコードなのですが、長くなりましたのでまた後のエントリにします。