RailsでOAuth2認証をdoorkeeperというgemを使って実装しているのですが、クライアント側の要望でレスポンスで返すjsonの値を変更する場合にどうすればいいか調べてみました。
ちなみにdoorkeeperをRailsに導入する方法は省略しますが、もし必要であれば以前まとめた記事があるのでそちらを参照して下さい。
Doorkeeper::TokensControllerを継承
doorkeeperでアクセストークンを生成するにはデフォルトでは/oauth/tokenにPOSTリクエストを送ります。rake routeしてどのControllerにルーティングされているか調べるとdoorkeeper/tokens#createになっていることがわかります。
そのアクションの処理に変更を加えるため、新規にControllerを作成し、Doorkeeper::TokensControllerを継承して各メソッドをoverrideしていきます。
class Api::V1::Doorkeeper::TokensController < Doorkeeper::TokensController
def create
response = strategy.authorize
self.headers.merge! response.headers
self.response_body = response.body.to_json
self.status = response.status
rescue Errors::DoorkeeperError => e
handle_token_exception e
end
# OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
def revoke
super
end
private
def revoke_token(token)
super
end
def strategy
super
end
end
各メソッドのoverrideはsuperを呼び出すだけにしておきますが、今回変更を加えるcreateアクションは動作を確認しやすくするために継承元のコードをそのままコピペしています。
ルーティングを作成したControllerに変更
いまのままだと元のControllerにルーティングされたままなので、先ほど作成したControllerにルーティングを向けます。
Rails.application.routes.draw do
use_doorkeeper do
controllers :tokens => 'api/v1/doorkeeper/tokens'
end
createアクション内でレスポンスのボディを変更する
今回はresponseにresultというパラメータを追加するように変更を加えます。
ぱっとコードを見て真っ先に加えた変更は以下です。
が、上手く行きません^^;
def create
response = strategy.authorize
response.body[:result] = true
self.headers.merge! response.headers
self.response_body = response.body.to_json
self.status = response.status
rescue Errors::DoorkeeperError => e
handle_token_exception e
end
response.bodyを p で出力してみるとDoorkeeper::OAuth::TokenResponseのインスタンスだとわかりました。
このDoorkeeper::OAuth::TokenResponseのコードを見てみると、bodyはattributeじゃなくてメソッドだということがわかります。
そりゃ代入できないわ・・・
def body
{
'access_token' => token.token,
'token_type' => token.token_type,
'expires_in' => token.expires_in_seconds,
'refresh_token' => token.refresh_token,
'scope' => token.scopes_string
}.reject { |_, value| value.blank? }
end
ということで、一度responce.bodyの返り値を変数に代入してから必要な変更を行うようにしました。
def create
response = strategy.authorize
# responseの変更
tmp_res_body = response.body
tmp_res_body[:result] = true
self.headers.merge! response.headers
self.response_body = response.body.to_json
self.status = response.status
rescue Errors::DoorkeeperError => e
handle_token_exception e
end
こうすることでアクセストークン生成時に任意のパラメータを付けてレスポンスを返せます。ちなみに失敗した時のレスポンスの変更はdoorkeeperのwikiにのっていたのでそこを確認してみて下さい。
おわりに
Railsで開発するといろんなgemを使うので、それらの処理がブラックボックスになってしまって上手くコントロール出来なくってしまいがちだが、今回みたいに動作の変更やデバックを行いたい場合もgemのソースをoverrideして動かすとかなり開発しやすくなると思いました。