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


# 準備

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 FOO`か`Authorization: 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