Ruby
Rails

doorkeeperでアクセストークン生成時のレスポンスを変更する

More than 3 years have passed since last update.

RailsでOAuth2認証をdoorkeeperというgemを使って実装しているのですが、クライアント側の要望でレスポンスで返すjsonの値を変更する場合にどうすればいいか調べてみました。

ちなみにdoorkeeperをRailsに導入する方法は省略しますが、もし必要であれば以前まとめた記事があるのでそちらを参照して下さい。

Doorkeeper::TokensControllerを継承

doorkeeperでアクセストークンを生成するにはデフォルトでは/oauth/tokenにPOSTリクエストを送ります。rake routeしてどのControllerにルーティングされているか調べるとdoorkeeper/tokens#createになっていることがわかります。

そのアクションの処理に変更を加えるため、新規にControllerを作成し、Doorkeeper::TokensControllerを継承して各メソッドをoverrideしていきます。

app/controller/api/v1/doorkeeper/tokens_controller.rb
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にルーティングを向けます。

config/routes.rb
Rails.application.routes.draw do
  use_doorkeeper do
    controllers :tokens => 'api/v1/doorkeeper/tokens'
  end

createアクション内でレスポンスのボディを変更する

今回はresponseにresultというパラメータを追加するように変更を加えます。

ぱっとコードを見て真っ先に加えた変更は以下です。
が、上手く行きません^^;

app/controller/api/v1/doorkeeper/tokens_controller.rb
    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じゃなくてメソッドだということがわかります。

そりゃ代入できないわ・・・

doorkeeper/lib/doorkeeper/oauth/token_response.rb
     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の返り値を変数に代入してから必要な変更を行うようにしました。

app/controller/api/v1/doorkeeper/tokens_controller.rb
    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して動かすとかなり開発しやすくなると思いました。