RubyでTwitterのOAuth認証をしてみる その2

[Ruby][Twitter][API]RubyでTwitterのOAuth認証をしてみる その2

前回のエントリ では、リクエストトークンまで発行してもらえました。だもんで今回は前回に続いてアクセストークンを発行してもらいます。アクセストークン発行の流れとしては、

  1. 今もらったリクエストトークンを付加して http://twitter.com/oauth/authorize へアクセスする
  2. 画面に表示されるPINコード(oauth_verifier)を控える
  3. リクエストトークンとPINコードをを付加して http://twitter.com/oauth/access_token へアクセスする

行きます。

PINコードを発行してもらう

リクエストトークンを発行してもらうと、

oauth_token=XXXXXXXXXX&oauth_token_secret=YYYYYYYYYYYYYYYYYYYYYYYYYYYY&oauth_callback_confirmed=true

という文字列がbodyに埋まってきたはず。[1]

この中から oauth_tokenを抜き出し、 http://twitter.com/oauth/authorize にパラメータとしてくっつけます。こんな感じ。

http://twitter.com/oauth/authorize?oauth_token=XXXXXXXXXX

アドレスが正しければ、以下のような画面が出るはず。[2]アプリケーションからのアクセスを許可しますか? と尋ねられるので許可するを選択。

[f:id:kk_Ataka:20101130220734j:image]

許可すると、PINコードが画面に表示されるのでこれを控えておく。

[f:id:kk_Ataka:20101130220731j:image]

再度signatureを作成する

このPINコードを”oauth_verify”として。先ほどURLに貼っつけたoauth_tokenを”oauth_token”としてパラメータとして持たせます。

そして、ここでもう一度signatureを作成を作成します。注意点としては、

  1. 今まで使っていたoauth_signatureは一旦消して、以下のパラメータで新たにつくり直す
    1. oauth_consumer_key
    2. oauth_nonce
    3. oauth_signature_method
    4. oauth_timestamp
    5. oauth_version
    6. oauth_token New!
    7. oauth_verifier New!
  2. URLは”http://twitter.com/oauth/access_token”をエスケープしたもの
  3. 暗号化用のキーは”consumer_secret&oauth_token_secret”[3]

この3点でしょうか。

signatureが作成できたら、前回と同様にアルファベット順に並べて連結し、URLのおしりにくっつけます。URLはこんな感じになります。

[f:id:kk_Ataka:20101130223243j:image]

成功したら、oauth_token, oauth_token_secret, user_id, screen_nameが返ってきます。これ俺や!!

[f:id:kk_Ataka:20101130220728j:image]

ここで返してもらったoauth_tokenとoauth_token_secretは本物なので大切にとっておく! というわけで、次は自分のTimelineを取得します!

ソースはこんな感じ。

require 'openssl'
require 'uri'
require 'net/http'

# signature作成
def signature(method, consumer_secret, oauth_token_secret, url, oauth_header)
    # signature_keyの作成
    # リクエストトークン時は"CONSUMER_SECRET&"(アンドが入っている)
    # アクセストークン時は"CONSUMER_SECRET&OAUTH_TOKEN_SECRET"として使用
    signature_key = consumer_secret + "&"
    if !oauth_token_secret.nil? then
        signature_key += oauth_token_secret
    end

    # oauth_headerのパラメータをソートして連結
    param = sort_and_concat(oauth_header)

    # httpメソッドとURLとパラメータを&で連結する
    value = method + "&" + escape(url) + "&" + escape(param)
    # hmac_sha1
    sha1 = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, signature_key, value)
    # base64
    base64 = [sha1].pack('m').gsub(/\n/, '')
    return base64
end

# 文字列のエスケープ(: / = %をエスケープする。. _ -はそのまま)
def escape(value)
    URI.escape(value, Regexp.new("[^a-zA-Z0-9._-]"))
end

# oauth_headerの情報をアルファベット順に並べ替え & で結合
def sort_and_concat(oauth_header)
    oauth_header_array = oauth_header.sort
    param = ""
    oauth_header_array.each do |params|
        for i in 1..params.length
            param += params[i-1]
            if i % params.length  == 0
                param += "&"
            else
                param += "="
            end
        end
    end
    param = param.slice(0, param.length-1)
end

# リクエストトークン取得用のURL
request_token_url = "http://twitter.com/oauth/request_token"
# PINコード取得用URL
authorize_url = "http://twitter.com/oauth/authorize"
# アクセストークン取得用のURL
access_token_url = "http://twitter.com/oauth/access_token"

# Twitterで登録したらもらえる
consumer_key = "XXXXXXXXXXXXXXXXXXXXX"
consumer_secret = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"

# Twitterからもらえるアクセストークン(最初は使わない)
oauth_token = ""
oauth_token_secret = ""

# oauthパラメータたち
oauth_header = {
    # Consumer Key
    "oauth_consumer_key" => consumer_key,
    # 一意な値(今回は適当に実装)
    "oauth_nonce" => "AAAAAAAA",
    # 署名方式(HMAC-SHA1)
    "oauth_signature_method" => "HMAC-SHA1",
    # リクエスト生成時のタイムスタンプ(ミリ秒)
    "oauth_timestamp" => Time.now.to_i.to_s,
    # バージョン(1.0)
    "oauth_version" => "1.0",
}

# signature作成
oauth_header["oauth_signature"] = signature("GET", 
                    consumer_secret, 
                    nil, 
                    request_token_url, 
                    oauth_header)

# GETする
uri = URI.parse(request_token_url)
proxy_class = Net::HTTP::Proxy(ARGV[0], 8080)
http = proxy_class.new(uri.host)
http.start do |http|
    # oauth_headerのパラメータをソートして連結
    param = sort_and_concat(oauth_header)

    res = http.get(uri.path + "?#{param}")

    if res.code == "200" then
        # 返ってきた値を分割
        params = res.body.split("&")
        params.each do |param|
            # さらに=で分割し前部分をkey、後方部分をvalueに格納
            key,value = param.split("=")

            # リクエストトークンを格納
            if ("oauth_token" == key) then
                oauth_token = value
            elsif ("oauth_token_secret" == key) then
                oauth_token_secret = value
            end
        end

        # プロンプトにPINコード取得用URL表示
        print "#{authorize_url}?oauth_token=#{oauth_token}\n"
        print "Input PIN Code. Input...\n"

        # PINコード入力待ち
        oauth_verifier = STDIN.gets
        # 改行コード(\n)取り除き
        oauth_verifier = oauth_verifier.slice(0, oauth_verifier.length-1)

        # ヘッダにアクセストークンとPINコード追加
        oauth_header["oauth_token"] = oauth_token
        oauth_header["oauth_verifier"] = oauth_verifier
        
        # いったんoauth_signature削除
        oauth_header.delete("oauth_signature")

        # 再びsignature作成
        oauth_header["oauth_signature"] = signature("GET", 
                            consumer_secret, 
                            oauth_token_secret, 
                            access_token_url, 
                            oauth_header)

        # oauth_headerのパラメータをソートして連結
        param = sort_and_concat(oauth_header)

        # GETする
        uri = URI.parse(access_token_url)
        proxy_class = Net::HTTP::Proxy(ARGV[0], 8080)
        http = proxy_class.new(uri.host)
        http.start do |http|
            res = http.get(uri.path + "?#{param}")
            if res.code =="200" then
                print "#{res.code}\n"
                print "#{res.body}\n"
            else
                print "ERROR: #{res.code}\n"
            end
        end
    else
        print "ERROR: #{res.code}\n"
    end
end

処理の流れとしては、oauth_tokenを貼りつけたhttp://twitter.com/oauth/authorizeのアドレスを出力するので、(お手数ですが)ブラウザからアクセスしてもらう。

[f:id:kk_Ataka:20101130220736j:image]

ブラウザに表示されたPINコードを貼っつけてもらって、これをoauth_verifierとし、アクセストークンを発行してもらう。

[f:id:kk_Ataka:20101130220728j:image]

PINコードの所はいちいちブラウザから行かないようにすることもできそうなんですが……。

[1] この時のoauth_tokenとoauth_token_secretは一時的なものらしいので、アクセストークンがもらえれば忘れても良いようです。

[2] Twitterにログインしていなければ、ログイン画面がまず出たはず。

[3] リクエストトークンを作るときは”consumer_secret&“でした

関連記事(この記事の初版より古い記事はリンクがグレーで表示されます)

  1. 2010/12/07 [Ruby] [Twitter] [API] RubyでTwitterのタイムラインを取得してみる
  2. 2010/11/21 [Ruby] [Twitter] [API] RubyでTwitterのOAuth認証をしてみる
  3. 2011/01/23 [Ruby] [Twitter] [API] RubyでTwitterにツイートをキメてみる
  4. 2012/05/21 [SlideShare] [Heroku] [Ruby] [API] SlideShareのAPIを叩いてスライドをDLするRubyスクリプトをHerokuにデプロイした
  5. 2011/12/27 [Evernote] [Ruby] [API] EvernoteのAPIをRubyから叩きたい
  6. 2011/03/08 [Ruby] [API] RubyではてなのWSSE認証をしてはてブにブクマをポストする
  7. 2011/03/02 [Ruby] [API] Read it LaterをRubyで取得する
  8. 2017/03/30 [Ruby] rbenvを最新にして新しいバージョンのRubyをインストールできるようにする
  9. 2016/08/31 [Ruby] Rubyの変数DATABASE_URLでハマった話
  10. 2016/06/05 [Ruby] Rubyで自然言語っぽくcrontab管理できるwheneverを使う
  11. 2016/02/17 [Ruby] [Mac] El Capitan上に古いpumaをインストールすると失敗する
  12. 2015/02/28 [Ruby] rubyXLを使ってExcelを操作したい
  13. 2014/08/28 [Sphinx] [Ruby] [イベント] kawasaki.rb #015 でSphinx導入事例の発表をしてきました #kwskrb #sphinxjp
  14. 2014/07/13 [Sphinx] [Ruby] [イベント] kawasaki.rb #013 でSphinx導入事例の発表をしてきました と #011 #012 参加記録 #kwskrb #sphinxjp
  15. 2014/04/01 [Ruby] [Jekyll] [イベント] kawasaki.rb #010 で発表してきました #kwskrb