OAuth Consumerサンプルを動かす
第1回 では実際にOAuthを利用したサービスを触り、ユーザから見たOAuthを理解しました。またOAuthの大まかな処理フローについても触れました。
第2回と第3回では、OAuth Consumerの実装を通じてより深くOAuthを理解します。とは言っても、ゼロからアプリケーションを実装していくのには限界があるので、ここではあらかじめ(Ruby on Railsで)実装したサンプルアプリケーションを使います。なお、ConsumerとService Providerの実装は、すべてrailsを用いて行います。
Ruby on Railsの構築に関しては、技術評論社『WEB+DB PRESS 』やRubyist Magazineの記事 などをご覧ください。
まずはgithubに公開されているoauth_sample を動かしてみてください。gitをお使いの方は以下のコマンドで。gitをお使いでない方は、oauth_sample からzipかtar形式でダウンロードしてください。
git clone git://github.com/nov/oauth_sample.git
その後railsアプリを起動します。筆者のMacBook上では以下で問題なく起動しますが、皆さんの環境でもそうであることを祈ります(サンプルが動かない場合は、githubのwikiページにあるコメント欄へコメントください) 。
gem install sqlite3-ruby
gem install oauth
cd oauth_sample/oauth_consumer
mkdir log
rake db:migrate
./script/server
http://localhost:3000にアクセスし、適当なユーザ名でログインすると、dashboardが表示されます。
smart.fmにOAuth Consumer登録
サンプルアプリケーションのdashboardには「new consumer」と「new access token」というリンクが存在しますが、Access Tokenの取得にはConsumerの情報が必要なので、最初は「new consumer」からConsumer情報を登録します。
図1 Consumer情報の登録
このアプリは、デフォルトの状態ではsmart.fmとGoogleの2つのOAuth Service Providerに対応しています。もちろんどちらも利用できますが、GoogleでのOAuth Consumer登録にはドメイン認証などが必要なので、ここではsmart.fmでConsumer登録を行います。smart.fmのConsumer登録はsmart.fm OAuth Client Applications から行えます。
図2 smart.fm OAuth Client Applications
今回のアプリケーションを利用する場合、登録フォームには以下のURLを入れてください。
Main Application URL : http://localhost:3000
Callback URL : http://localhost:3000/oauth/smartfm/callback
登録後、得られたConsumer KeyとConsumer SecretをサンプルアプリケーションのConsumer登録フォームに入力します。またsmart.fm APIではConsumer Keyとは別にAPI Keyも必要ですので、smart.fm API 日本語ガイド の「APIデベロッパアカウントの登録」からAPI Keyを取得し、そちらもサンプルアプリケーションのConsumer登録フォームに入力して、registerをクリックします。( Scopeは空白で結構です)これでOAuth Consumerの登録は完了です。
OAuth Access Tokenの取得
Consumer登録が完了したので、これでAccess Tokenを取得する準備は整っています。dashboardの「new access token」をクリックすると、smart.fm APIとGoogle Data APIsそれぞれにAccess Tokenの「establish」ボタンがあるので、smart.fm APIのボタンをクリックします。するとsmart.fmにリダイレクトされるので、「 authorize access?」にチェックを入れて「save」をクリックします。
図3 「 authorize access?」にチェックを入れて「save」
これで再びdashboardにリダイレクトされて、Access Tokenが表示されているはずです。
さて、ここからはrailsのコードを見ながらこの動作を追っていきます。いったんDBをクリア(rake db:migrate:reset)してください。
サンプルアプリケーションで実際にAccess Token取得に関わるのは、以下の2つです。
OauthAccessTokensController(oauth_consumer/app/controllers/oauth_access_tokens_controller.rb)
OauthConsumer(oauth_consumer/app/models/oauth_consumer.rb)
Request Tokenの取得
smart.fm APIのestablishボタンをクリックすると、処理はOauthAccessTokensControllerのnewアクションに移ります。以下は実際のコードから重要な部分のみを抜粋したものです。
class OauthAccessTokensController < ApplicationController
def new
redirect_to request_token.authorize_url
end
private
def request_token
request_token = oauth_consumer.get_request_token
session[:request_token] = request_token.token
session[:request_token_secret] = request_token.secret
request_token
end
end
ここではまずrequest_tokenメソッド内でconsumerのget_request_tokenを呼び出してバックグラウンドでRequest Tokenを取得し、authorize_urlにユーザをリダイレクトさせます。ほとんどの処理はoauth gemが行うので、サンプルアプリケーション内には細かい処理は書かれていませんが、実際にどのような通信が行われているかはtcpflow を使って確認することができます。
sudo tcpflow -c
これを見ると、まず最初はRequest Tokenを取得する為に、Authorizationヘッダにconsumer_keyやnonce、signatureなどのOAuthに必要な情報を付与して、http://api.smart.fm/oauth/request_token(smart.fmのRequest Token取得endpoint)にアクセスしていることがわかります。
POST /oauth/request_token HTTP/1.1
Connection: close
Accept: */*
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_nonce="SOME_NONCE_IS_HERE", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1236450076", oauth_consumer_key="SMARTFM_CONSUMER_KEY", oauth_signature="SOME_SIGNATURE_IS_HERE", oauth_version="1.0"
Host: api.smart.fm
Content-Length: 32
そしてsmart.fmからのレスポンスには、以下のようにRequest TokenとRequest Token Secretが含まれています。
oauth_token=SMARTFM_REQUEST_TOKEN&oauth_token_secret=SMARTFM_REQUEST_TOKEN_SECRET
その後、得られたRequest TokenをURLに付与して、http://smart.fm/oauth/authorize?oauth_token=SMARTFM_REQUEST_TOKENというURLにアクセスしています。このリダイレクト先で、ユーザはRequest Tokenに認可を与えるかどうか確認されることになります。
Request TokenとAccess Tokenの交換
ユーザがsmart.fm上でRequest Tokenに認可を与えると、ブラウザはOauthAccessTokensControllerのcreateアクション(http://localhost:3000/oauth/smartfm/callback)にリダイレクトされます。その際、認可済のRequest Tokenがoauth_tokenというパラメータで返されます。
class OauthAccessTokensController < ApplicationController
def create
new_access_token = oauth_consumer.get_access_token(params[:oauth_token], session[:request_token_secret])
store_access_token(new_access_token)
redirect_to_dashboard
end
private
def store_access_token(access_token)
OauthAccessToken.create(
:user => current_user,
:oauth_consumer => oauth_consumer,
:token => access_token.token,
:secret => access_token.secret
)
end
end
createアクションでは、あらかじめセッションに保存しておいたrequest_token_secretと認可済のtoken(params[:oauth_token])を引数として、consumerのget_access_tokenを呼び出しています。実際のtcpflowを見ると、バックグラウンドでAccess Token発行endpointのhttp://api.smart.fm/oauth/access_tokenにアクセスしていることがわかります。このリクエストでは、Authorizationヘッダに認可済のRequest Tokenが含まれていることもわかるでしょう。
POST /oauth/access_token HTTP/1.1
Connection: close
Accept: */*
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_nonce="SOME_NONCE_IS_HERE", oauth_signature_method="HMAC-SHA1", oauth_token="SMARTFM_REQUEST_TOKEN", oauth_timestamp="1236452101", oauth_consumer_key="SMARTFM_CONSUMER_KEY", oauth_signature="SOME_SIGNATURE_IS_HERE", oauth_version="1.0"
Host: api.smart.fm
Content-Length: 32
api.smart.fm上でRequest Tokenの認可が確認されると、以下のようにAccess Tokenが返されます。
oauth_token=SMARTFM_ACCESS_TOKEN&oauth_token_secret=SMARTFM_ACCESS_TOKEN_SECRET
サンプルアプリケーションではstore_access_tokenメソッドでAccess TokenをDBに保存しています。ここではsmart.fm上のユーザ名などは保存していませんが、必要があればService Provider上でのユーザ識別子も一緒に保存するようにした方が良いでしょう。( Service Provider上のユーザ識別子が必要かどうかや、それを得る方法は、各Service ProviderおよびAPI Callによって異なるので、詳しくはService Providerのドキュメントを確認してください。)
Access Tokenを使ってみる
最後に得られたAccess Tokenを利用して、smart.fmのリスト作成APIを呼び出してみましょう。以下のサンプルコードに、既に得られているAPI KeyやConsumer Key、Access Tokenなどをセットして、実行してみてください。正常にリスト作成が完了すれば、response.bodyにリストIDが入っているはずです。http://smart.fm/lists/:list_idにアクセスすれば、実際に作成されたリストが確認できます。
require 'rubygems'
require 'oauth/consumer'
SMARTFM_API_KEY = 'YOUR_API_KEY'
CONSUMER_KEY = 'YOUR_CONSUMER_KEY'
CONSUMER_SECRET = 'YOUR_CONSUMER_SECRET'
ACCESS_TOKEN = 'USER_ACCESS_TOKEN'
ACCESS_TOKEN_SECRET = 'USER_ACCESS_TOKEN_SECRET'
consumer = OAuth::Consumer.new(
CONSUMER_KEY,
CONSUMER_SECRET,
:site => "http://api.smart.fm",
:authorize_url => "http://smart.fm/oauth/authorize"
)
access_token = OAuth::AccessToken.new(consumer, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
response = access_token.post(
'/lists',
{ :api_key => SMARTFM_API_KEY,
'list[name]' => 'OAuth test',
'list[description]' => 'A list for OAuth test',
'list[language]' => 'en',
'list[translation_language]' => 'ja' })
puts response.inspect, response.body
まとめ
今回はOAuth Consumerのサンプルアプリケーションとtcpflowを用いて、Consumerの振る舞いを確認しました。次回はより詳細なoauth gemの利用方法についてご紹介します。