【Rails】RailsでAPIの簡単なトークン認証を実装する

  • 46
    Like
  • 0
    Comment

準備

Rails 5のAPI modeでRails newする。

rails new apiTokenAuthentication --api -d postgresql

scaffoldする。てきとうなAPIを作る。

bundle exec rails g scaffold Product name:string age:integer email:string
bundle exec rake db:setup
bundle exec rake db:migrate

動作確認

$ curl -X POST -H 'Content-Type:application/json' -d '{ "name": "test", "price": 500 }' http://0.0.0.0:3000/products
$ curl -X GET -H 'Content-Type:application/json' http://0.0.0.0:3000/products | jq . 

[
  {
    "updated_at": "2016-09-14T17:54:24.717Z",
    "created_at": "2016-09-14T17:54:24.717Z",
    "price": 500,
    "name": "test",
    "id": 1
  }
]

トークン認証

authenticate_or_request_with_http_tokenを使う

authenticate_or_request_with_http_tokenにブロックを渡してトークンをチェックする。

app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::HttpAuthentication::Token::ControllerMethods

  before_action :authenticate

  protected
  def authenticate
    authenticate_or_request_with_http_token do |token, options|
      token == 'FOO'
    end
  end
end

動作確認。AuthorizationヘッダーにAuthorization: Token FOOAuthorization: Bearer FOOがない場合は、HTTP Token: Access denied.を返すようになった。

$ curl -X GET -H 'Authorization: Token BAR' -H 'Content-Type:application/json' http://0.0.0.0:3000/products/1
HTTP Token: Access denied.

$ curl -X GET -H 'Authorization: Token FOO' -H 'Content-Type:application/json' http://0.0.0.0:3000/products/1 | jq .
{
  "updated_at": "2016-09-14T17:54:24.717Z",
  "created_at": "2016-09-14T17:54:24.717Z",
  "price": 500,
  "name": "test",
  "id": 1
}

authenticate_with_http_tokenを使う

authenticate_with_http_tokenにブロックを渡してトークンをチェックする。
authenticate_or_request_with_http_tokenと違ってレスポンスを返す処理は自分で実装する。

app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::HttpAuthentication::Token::ControllerMethods

  before_action :authenticate

  protected
  def authenticate
    authenticate_token || render_unauthorized
  end

  def authenticate_token
    authenticate_with_http_token do |token, options|
      token == 'FOO'
    end
  end

  def render_unauthorized
    # render_errors(:unauthorized, ['invalid token'])
    obj = { message: 'token invalid' }
    render json: obj, status: :unauthorized
  end
end

動作確認。

$ curl -X GET -H 'Authorization: Token BAR' -H 'Content-Type:application/json' http://0.0.0.0:3000/products/1 | jq .                                                                                   
{
  "message": "token invalid"
}

まとめ

method
authenticate_with_http_token トークンとオプションを受け取るブロックを渡してToken認証する。レスポンスは自分で書く。
request_http_token_authentication 中でauthenticate_with_http_tokenを使っていて、ブロックを渡すところまでは一緒。こちらは簡単なテキストのレスポンスを返すところまでやってくれるのでレスポンスの処理を書く必要がない。

注意

Rail5ではAuthorization: Token xxx, Authorization: Bearer xxxのようにTokenでもBearerでもどちらでもいける。
Rail4の場合は、Tokenの方しか使えないので注意。

参考

Rails5 APIモード

http://morizyun.github.io/blog/rails-api-5.0.0.beta.2/

https://www.hommax39.com/archives/170

http://totutotu.hatenablog.com/entry/2015/12/19/093944

トークン認証

http://sourcey.com/building-the-prefect-rails-5-api-only-app/#versioning-your-api

https://www.codeschool.com/blog/2014/02/03/token-based-authentication-rails/

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

http://techblog.thescore.com/2014/06/25/http-basic-authentication-and-http-token-authentication/

http://security.stackexchange.com/questions/108662/why-is-bearer-required-before-the-token-in-authorization-header-in-a-http-re

http://qiita.com/uasi/items/cfb60588daa18c2ec6f5

http://edgeapi.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html

http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token/ControllerMethods.html#method-i-authenticate_with_http_token