Ruby で OAuth を使ってみた (2) (また失敗 → ちょっと成功)
昨日の日記の続き。 「諦めたらそこで試合終了ですよ」という声が聞こえたので、騙し騙し動かしてみることにした。 適当にRequestProxyを作ったり、動かないメソッドを直したりしたんだけど、こんなのでいいのだろうか。 ちなみに、元のソースは署名生成が明らかに間違ってた(ハッシュをそのままテキストにしてる)。
リクエストを作れるようになったので、 Twitter の APIに登録して Consumer Key と Consumer Secret を取得。 OAuth Request URLsを見て、OAuth Request Token URL を呼んでみた。 …結果はNG。署名生成で失敗しているのかなぁ。
とりあえず正しいリクエストが分からないと比較もできないので、先に PHP の実装を試してみることにする。
追記
PHPだと動いた。 署名生成についての僕の知識がやっぱり間違ってたみたい。 なので、下記のパッチは役に立たないや。あとで直します。
追記2
PHP版の実行結果を見ながらRuby版でも動くようにした。 とりあえず最初のステップの RequestToken だけだけど。
サンプルソースは以下の通り。
require 'oauth' require 'oauth_token' require 'oauth_request' require 'oauth_consumer' require 'oauth_signature' require 'base64' require 'uri' require 'openssl' consumer_key = "Twitterから取得したkey" consumer_secret = "Twitterから取得したsecret" consumer = OAuth::Consumer.new(consumer_key, consumer_secret) endpoint = { :req_token => "http://twitter.com/oauth/request_token", :user_auth => "http://twitter.com/oauth/authorize", :access_token => "http://twitter.com/oauth/access_token" } params = { :method => 'GET', :uri => endpoint[:req_token], :parameters => { :oauth_version => '1.0' } } req = OAuth::Request.new(consumer, nil, params) query = req.parameters.map {|k,v| "#{k}=#{v}" unless v.empty? }.compact.sort.join("&"); puts endpoint[:req_token] + '?' + query
OAuthライブラリへのパッチは以下の通り。
Index: oauth_signature.rb =================================================================== --- oauth_signature.rb (revision 205) +++ oauth_signature.rb (working copy) @@ -20,8 +20,19 @@ class UnknownSignatureMethod < Exception; end class Base + attr_reader :request + + def initialize(request) + @request = request + end + + def escape(text) + # unsafe caractor: http://oauth.googlecode.com/svn/spec/branches/1.0/drafts/4/spec.html#encoding_parameters + URI.escape(text, /[^a-zA-Z\d\-\._~]/) + end + def signature - Base64.encode64(digest).chomp + escape(Base64.encode64(digest).chomp) end def ==(cmp_signature) @@ -103,6 +114,10 @@ class SHA1 < Base private def hmac_class; HMAC::SHA1; end + + def self.digest(secret, message) + OpenSSL::HMAC::digest(OpenSSL::Digest::SHA1.new, secret, message) + end end class SHA2 < Base Index: oauth_request.rb =================================================================== --- oauth_request.rb (revision 205) +++ oauth_request.rb (working copy) @@ -1,9 +1,23 @@ module OAuth + class RequestProxy + attr_reader :parameters, :method, :uri + + def initialize(request = {}) + @parameters = request[:parameters] || {} + @method = request[:method] || 'GET' + @uri = request[:uri] + end + + def self.proxy(request) + OAuth::RequestProxy.new(request) + end + end + class Request attr_accessor :consumer, :token, :request, :realm, :signature_method, :nonce, :timestamp attr_reader :parameters_for_signature - def initialize(consumer, token, request, realm = '', signature_method => 'HMAC-SHA1', nonce = nil, timestamp = nil) + def initialize(consumer, token, request, realm = '', signature_method = 'HMAC-SHA1', nonce = nil, timestamp = nil) @consumer = consumer @token ||= OAuth::Token.new('', '') @request = OAuth::RequestProxy.proxy(request) @@ -23,8 +37,8 @@ "oauth_token=\"#{token.token}\", " + "oauth_signature_method=\"#{signature_method}\", " + "oauth_signature=\"#{signature}\", " + - "oauth_timestamp=\"#{oauth_timestamp}\", " + - "oauth_nonce=\"#{oauth_nonce}\"" + "oauth_timestamp=\"#{timestamp}\", " + + "oauth_nonce=\"#{nonce}\"" end def method @@ -43,6 +57,8 @@ else @parameters_for_signature = request.parameters end + # convert hash to query string + @parameters_for_signature = @parameters_for_signature.map {|k,v| "#{k}=#{v}" unless v.empty? }.compact.sort.join("&") OAuth::Signature.sign(self) end
ノートPCの電源がなくなったので、今日はここまで。