📕

Rails API の HttpAuthentication/Token の実装を読む

2023/01/12に公開

Leaner Technologies の @corocn です。

Rails の HttpAuthentication/Token 周りを改めて読んだのでメモです。

この記事を読むと分かること

Rails で API の保護にトークンを利用する場合において、次のメソッドの使い分けが理解できる。

  • authenticate_or_request_with_http_token
  • authenticate_with_http_token

結論

トークン検証失敗時にレンダリングされる内容やステータスコードについて、特にこだわりがない場合は、authenticate_or_request_with_http_token を利用する。

レスポンスを詳細にカスタマイズしたい場合は、authenticate_with_http_token を利用し、レスポンスは自前で作成する。

モチベーション

Rails API モードを利用し、API を トークンベースの認可の仕組みで保護するケースを考えた。標準で提供されているメソッドがいくつかあり、どれを使えばいいかわかりにくいので、整理したい。

サンプルには authenticate_or_request_with_http_token を使った例が載っている。この例ではブロック内でトークンの検証を行っているようだ。

https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

HttpAuthentication::Token

Controller から利用できるメソッドは以下で把握できる。

https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token/ControllerMethods.html

  • authenticate_or_request_with_http_token
  • authenticate_with_http_token
  • request_http_token_authentication

同じような単語の長めのメソッドでちょっと混乱する。

authenticate_or_request_with_http_token

基本的にはこのメソッドを利用すれば良い。

https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/actionpack/lib/action_controller/metal/http_authentication.rb#L419-L445

authenticate_or_request_with_http_token の中で、authenticate_with_http_token または request_http_token_authentication を呼び出すようになっている。

authenticate_or_request_with_http_token は block を受け取ることができ、authenticate_with_http_token が渡されて実行される。

authenticate_with_http_token の返り値が true で判定されれば処理はそこで終わり、request_http_token_authentication は呼ばれない。

authenticate_with_http_token

https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/actionpack/lib/action_controller/metal/http_authentication.rb#L447-L489

次のようなリクエストヘッダーを前提とする。

Authorization: Bearer <Token>

ここから 実際にリクエストされたトークンを抜き出す処理を token_and_options が担当している。Bearer の部分は Authorization: Bearer または Authorization: Token に標準で対応している。

トークンが存在する場合、そのトークンを第1引数として、渡された block が評価される。つまり authenticate_or_request_with_http_token の block 内でトークンの検証を行い、OKであれば true を返せば良い。

ここまでで、サンプルで把握した挙動と一致することは確認できた。

request_http_token_authentication

authenticate_with_http_token の返り値が false のとき、つまり トークンの検証に失敗した時に呼び出される。

https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/actionpack/lib/action_controller/metal/http_authentication.rb#L440-L444

Token.authentication_request を呼び出している。

https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/actionpack/lib/action_controller/metal/http_authentication.rb#L533-L545

authentication_request では、message が引数で渡ってこない場合は、固定の "HTTP Token: Access denied." を ステータスコード 401: unauthorized で返す。

また、レスポンスヘッダーに WWW-Authenticate を追加し、認証方式に関する情報を提供する。

参考

https://api.rubyonrails.org/classes/ActionController/HttpAuthentication.html
https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

本記事とは切り口が異なるが、以下の記事でも似たようなまとめになっている。
https://qiita.com/Yarimizu14/items/c81a8cf1859f954b953e

執筆時点のバージョン: Ruby on Rails 7.0.4 であり、挙動は変更される可能性がある。

リーナーテックブログ

Discussion