at posts/single.html

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の電源がなくなったので、今日はここまで。

関連する日記