2013/04/27

【C#でTwitter】OAuth認証を行う[デスクトップクライアント版]

 C#を使ってデスクトップクライアントでOAuth認証を行うプログラムを作りました。
ダウンロードできます → OAuth.cs

OAuth認証の概要は 【C#でTwitter】OAuth認証でTwitterにアクセス[概要] にまとめてます。

処理の大まかな流れは以下のとおりです。
  1. Twitterアプリの登録を行い、ConsumerKeyConsumerSecretを取得する。
  2. ConsumerKeyとConsumerSecretを使って、RequestTokenを取得する。
  3. RequestTokenを使ってTwitterアプリの認証を行う。 
  4. PINコードを取得する。
  5. PINコードを入力し、AccessTokenを取得する。

※追記:2013/06/15
API1.0が廃止され、API1.1に一本化された今でもOAuth認証をすることができました。



0. Twitterアプリの登録を行い、ConsumerKeyとConsumerSecretを取得する。



 Twitter Developers にアクセスし、これから作成するアプリケーションの登録を行います。
Twitterと同じアカウントでログインします。

 ページが表示されたら「Create a new application」 ボタンをクリックし、
これから作るデスクトップクライアントの情報を入力します。
※「My applications」のページが表示されない場合は、右上の自分のTwitterアイコンに
   マウスカーソルを当てると「My applications」という項目が表示されるので、それをクリックしてください。


 次に作成するアプリケーションの詳細情報を入力し、利用規約に同意して
「Create your Twitter application」をクリックします。
  Name       : (必須)アプリケーション名
  Description    : (必須)アプリケーションの説明
  Website      : (必須)なんでも良いので自分のウェブサイト(TwitterURLでもOK)
  Callback URL  : OAuth認証後にリダイレクトするURL(ウェブアプリならそのURL)
              ※今回はデスクトップクライアントのため、未設定

 これでアプリケーションの登録は終了です。
OAuth認証を行うには、以下のConsumer Key、Consumer Secret とそれぞれのURLが必要です。


  
※ConsumerKeyとConsumerSecretは他人に知られないように注意してください。
  他のクライアントやサービスが、自分のアプリケーション名を偽称してスパム行為をすると
  クレームはどこにくるか… あとはわかりますよね?



OAuthBase.csを使う



 OAuth認証は面倒なので、有名?なGoogleのOAuthBase.csを使ってoauth_tokenを取得します。

追記: 2017/03/18 7:30
OAuthBase.csのリンクが切れていたので、 https://gist.github.com/BcRikko/57d3cb7175c46316bca4c675c1c61f32 をお使いください。


 他のサイトでは使いづらいとカスタマイズされてる方が多いのですが、
ここではOAuthBase.csをそのまま使ってコーディングしました。(カスタマイズするスキルがないとか云々)

 ソースをダウンロードしたら、プロジェクトに追加します。
私の環境では、そのままビルドすると「HttpUtility」がエラーになりました。

その場合は、参照設定から「.NET」→「System.Web」 を追加してください。




1.ConsumerKeyとConsumerSecretを使って、RequestTokenを取得する。



Twitter Developersから取得したConsumerKeyとConsumerSecret、その他URLを定数で宣言します。

static readonly string CONSUMER_KEY         = "**********************";
static readonly string CONSUMER_SECRET      = "******************************************";

static readonly string REQUEST_TOKEN_URL    = "https://api.twitter.com/oauth/request_token";
static readonly string AUTHORIZE_URL        = "https://api.twitter.com/oauth/authorize";
static readonly string ACCESS_TOKEN_URL     = "https://api.twitter.com/oauth/access_token";

次にRequestToken取得のための前処理を行います。
ランダム文字列(nonce)やタイムスタンプ(timestamp)、署名(signature)を生成します。

//---------------------------
// 0.リクエストトークン取得の前処理
//---------------------------

OAuthBase oauth = new OAuthBase();

// ランダム文字列の生成
string nonce = oauth.GenerateNonce();
// タイムスタンプ(unix時間)
string timestamp = oauth.GenerateTimeStamp();
string normalizedUrl, normalizedReqParams;

Uri reqUrl = new Uri(REQUEST_TOKEN_URL);            

// Consumer_Secretを暗号鍵とした署名の生成
string signature = oauth.GenerateSignature(reqUrl
                                        , CONSUMER_KEY
                                        , CONSUMER_SECRET
                                        , null
                                        , null
                                        , "GET"
                                        , timestamp
                                        , nonce
                                        , OAuthBase.SignatureTypes.HMACSHA1
                                        , out normalizedUrl
                                        , out normalizedReqParams);

// リクエストトークン取得用URL
string reqTokenUrl = normalizedUrl + "?" 
                    + normalizedReqParams 
                    + "&oauth_signature=" + signature;


 RequestToken取得に必要な情報が揃ったら、以下の形式でアクセスします。
※ 順番が違うと上手くいきませんのでご注意ください。

https://api.twitter.com/oauth/request_token?oauth_consumer_key=CONSUMER_KEY&oauth_nonce=NONCE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=TIMESTAMP&oauth_version=1.0&oauth_signature=SIGNATURE

うまくいくと以下のようにRequestTokenが返ってきます。


oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&oauth_callback_confirmed=true

たまに「リモート サーバーがエラーを返しました: (401) 許可されていません」となる場合がありますが、何度か行うと取得できます。

WebClientやStream、StreamReaderはTwitterが返してくれたRequestTokenを取得するために使用しています。
convertToTokenForOauthは、取得したoauth_tokenなどを<"oauth_token", "********">の形式で
ハッシュテーブルに設定する処理をしています。

//---------------------------
// 1.リクエストトークン取得
//---------------------------

WebClient client = new WebClient();
Stream st = client.OpenRead(reqTokenUrl);
StreamReader sr = new StreamReader(st, Encoding.GetEncoding("Shift_JIS"));

tokens = convertToTokenForOauth(sr.ReadToEnd());

// 取得したリクエストトークン
Console.WriteLine(
      "(request)oauth_token        = {0}\r\n"
    + "(requrst)oauth_token_secret = {1}\r\n"
    , tokens["oauth_token"]
    , tokens["oauth_token_secret"]
     );



2.RequestTokenを使ってTwitterアプリの認証を行う。


 1.で取得したRequestTokenを使用して、アプリの認証を行います。
以下の形式でアクセスするとアプリの認証ページに飛びます。


 https://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET

//---------------------------
// 2.オーサライズ
//---------------------------

string authorizeUrl = AUTHORIZE_URL + "?"
                        + "oauth_token=" + tokens["oauth_token"]
                        + "&oauth_token_secret=" + tokens["oauth_token_secret"];

// ブラウザ起動しPINコードを表示
System.Diagnostics.Process.Start(authorizeUrl);

うまくいくと以下のような認証ページが表示されるので、
「連携アプリを認証」 をクリックします。



3.PINコードを取得する。



 認証を行うと、PINコードが表示されます。




4.PINコードを入力し、AccessTokenを取得する。



取得したPINコードをコマンドプロンプトに入力します。

 RequestTokenとPINコードを元にAccessTokenを取得するため、
以下のような形式でアクセスします。

https://api.twitter.com/oauth/access_token?oauth_consumer_key=CONSUMER_KEY&oauth_nonce=NONCE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=TIMESTAMP&oauth_token=REQUEST_TOKEN&oauth_version=1.0&oauth_signature=SIGNATUEW&oauth_verifier=PINhttps://api.twitter.com/oauth/request_token?oauth_consumer_key=CONSUMER_KEY&oauth_nonce=NONCE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=TIMESTAMP&oauth_version=1.0&oauth_signature=SIGNATURE

ConsumerKeyと取得したRequestToken、RequestTokenSecretを使い、
signatureを再生成します。

//---------------------------
// 4.アクセストークン取得
//---------------------------

// リクエストトークンを加えsignatureを再生成
signature = oauth.GenerateSignature(reqUrl
                                    , CONSUMER_KEY
                                    , CONSUMER_SECRET
                                    , tokens["oauth_token"]
                                    , tokens["oauth_token_secret"]
                                    , "GET"
                                    , timestamp
                                    , nonce
                                    , OAuthBase.SignatureTypes.HMACSHA1
                                    , out normalizedUrl
                                    , out normalizedReqParams);


// アクセストークン取得用URL
string accessTokenUrl = ACCESS_TOKEN_URL + "?"
                            + normalizedReqParams 
                            + "&oauth_signature=" + signature
                            + "&oauth_verifier=" + pin;

st = client.OpenRead(accessTokenUrl);
sr = new StreamReader(st, Encoding.GetEncoding("Shift_JIS"));

tokens = convertToTokenForOauth(sr.ReadToEnd());

// 取得したアクセストークン
Console.WriteLine(
      "(access)oauth_token         = {0}\r\n"
    + "(access)oauth_token_secret  = {1}\r\n"
    + "user_id                     = {2}\r\n"
    + "screen_name                 = {3}\r\n"
    , tokens["oauth_token"]
    , tokens["oauth_token_secret"]
    , tokens["user_id"]
    , tokens["screen_name"]
     );

うまく行くと以下のようにAccessTokenが返ってきます。

oauth_token=TOKEN&oauth_token_secret=TOKEN_SECREET&user_id=USER_ID&screen_name=SCREEN_NAME



 取得したAccessTokenを用いることで、TwitterのAPIを操作できるようになります。
また、AccessTokenは一度取得してしまえば無制限?に使えるので、
今回のような作業は必要ありません。



あとがき



 自分専用アプリの場合は、TwitterDevelopersで簡単にAccessTokenを取得することができます。
Detailsタブの「Your access token」の下にある「Create my access token」というボタンをクリックすれば、前述のようなことをしなくてもOK





参考サイト




OAuthBase.csの基本的な使い方
GenerateSignatureの使い方が現行とちょっと違います。





oauth_tokenの取得方法について
軽くググッた中では一番読みやすくて、わかりやすかった。






以上

4 件のコメント :

  1. 通りすがり2017年3月18日 4:13

    有益な情報ありがとうございます。大変参考になりました。
    「0.リクエストトークン取得の前処理」でリクエストURLにシグネチャを追加していますが、URLEncodeしなくて良いのでしょうか?というか、上記の通りやったらうまくいかず、URLEncodeして行ったらうまくいったのですが…

    返信削除
  2. そう言っていただけると幸いですw
    こちらこそ、コメントにて共有ありがとうございます。

    (記事公開当時は)URLEncodeしなくても大丈夫でした。
    このコードを書いたのが4年前なので、もしかしたらOAuthBase.csがアップデートされているかもです。
    ※ googlecodeのリンクが切れていたので、2013年にダウンロードしたコードをgistにアップしました。


    OAuthBase.csを見ると、ライブラリ側でURLEncodeしてくれているようです。
    https://gist.github.com/BcRikko/57d3cb7175c46316bca4c675c1c61f32#file-oauthbase-cs-L229-L230

    さらにハッシュ化までしているのでだいじょうぶだと思うのですが…。
    https://gist.github.com/BcRikko/57d3cb7175c46316bca4c675c1c61f32#file-oauthbase-cs-L277-L283


    ただ、これでうまくいかないのでしたら、ご指摘どおりURLEncodeが必要だと思います。


    ちゃんとした回答ができず申し訳ございません。

    返信削除
  3. 通りすがり2017年3月18日 23:13

    早速返信ありがとうございます。ライブラリの中身ではなく、

    string reqTokenUrl = normalizedUrl + "?"
    + normalizedReqParams
    + "&oauth_signature=" + signature;

    の最後のsignatureの事です。signatureの桁数が足らないときは最後が=で埋められる仕様になっていますが、この=がURLEncodeされていないとまずいのではないかと思います。
    もしかして、「たまに「リモート サーバーがエラーを返しました: (401) 許可されていません」となる場合がありますが、何度か行うと取得できます。」とありますが、signatureの桁数が足らず=が入った時に起こっていたのではないでしょうか?

    あと、ソースが違うのかもしれませんが、Gitから取ったソースでは
    GenerateSignatureの第9引数(OAuthBase.SignatureTypes.HMACSHA1)がエラーが出ます(オーバーロードが無い)。ここを省略した場合自動的にHMAC_SHA1になるようなのですが...

    いずれにしてもこの記事、大変助かりました。ありがとうございました。

    返信削除
    返信
    1. なるほどー!!!
      そういうことだったんですね!

      いま再現させる環境がないので試せないのですが、signatureの桁数とエンコードの関係でエラーになっていたんですね。


      有益なコメントありがとうございました!

      削除