Ruby で OAuth を使ってみた (2) (また失敗 → ちょっと成功)
2007-11-02
昨日の日記の続き。 「諦めたらそこで試合終了ですよ」という声が聞こえたので、騙し騙し動かしてみることにした。 適当に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の電源がなくなったので、今日はここまで。