OAuth認証でmultipart/form-dataをPOST
oauth gemでマルチパートのPOSTは単体ではできないようなので、multipart-postと組み合わせる方法と、OAuthプロバイダ実装時の注意点について。
各gemは以下のコマンドでインストールできる。
$ sudo gem install oauth multipart-post
ポイントはマルチパートリクエストにOAuth::AccessToken#sign!を適用すること。
require 'oauth/consumer' require 'net/http/post/multipart' OAuth::VERSION = 1.0 consumer = OAuth::Consumer.new('consumer_key', 'consumer_secret', :site => 'http://provider.example.com', :request_token_path => '/request_token', :authorize_path => '/authorize', :access_token_path => '/access.token') request_token = consumer.get_request_token(:oauth_callback => 'http://consumer.example.com/callback') puts request_token.authorize_url access_token = request_token.get_access_token File.open("./image.jpg") do |image| req = Net::HTTP::Post::Multipart.new(url.path, 'text' => 'hi, there', 'image' => UploadIO.new(image, "image/jpeg", "image.jpg")) access_token.sign! req res = Net::HTTP.start('provider.example.com', 80) do |http| http.request(req) end end
僕はOAuthプロバイダを実装していて、そのテストスクリプトとして上記を実行している。
その際に注意が必要だった点として、マルチパートの各フィールドはSignature Base Stringに含めてはならないことが挙げられる。
上記のスクリプトは正しくtextやimageをSignature Base Stringに含めないが、プロバイダサイドで通常のリクエストとマルチパートリクエストを抽象化して扱っていたため、textを含むSignature Base Stringを生成していた。
そのため、マルチパートリクエストのSignatureが一致しないため401が返るという問題が起きる。
この問題を含むSignatureに関連したバグの調査にはコンシューマとプロバイダのSignature Base Stringを比較する必要があるが、oauth gem側では簡単にSignature Base Stringを取り出す方法がないかもしれない。
(以下の方法を試したが、出力内容は純粋なSignature Base Stringではない)
res = Net::HTTP.start('provider.example.com', 80) do |http| puts req.signature_base_string(http, consumer, access_token) http.request(req) end
また、oauth gemはRFC 5849 - The OAuth 1.0 Protocolで定義されないoauth_body_hashというパラメタを付加する。これはOAuth Request Body Hashという拡張のようだ。http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
これもプロバイダ側でSignature Base Stringに含める必要がある。
まとめ1:oauth gem + multipart-post = うごく
まとめ2:OAuthプロバイダはSignature Base Stringにマルチパートのフィールドを含めるな&コンシューマの拡張に寛容に