Railsでdevise_token_authを使ってAPIの認証機能を作っているときに、User以外のモデル(例えばAdminなど)を追加したらレスポンスヘッダーにアクセストークン等がなくてハマった話。
環境
- rails (5.2.1)
- devise_token_auth (0.2.0)
- active_model_serializers (0.10.8)
最初の認証モデルを作成
まず最初にactive_model_serializersをインストールして使える状態にした。
続いて、devise_token_authをインストールしてUserモデルを作成した。
$ rails g devise_token_auth:install User user
$ rails db:migrate
mount_devise_token_auth_for 'User', at: 'user'
だいだいこんな感じでUserモデルの認証機能ができるので、適当にUserを作成してアクセストークンを取得してみる。
User.create!(email: "hoge@hoge.com", password: "password")
$ curl -D - -X "POST" "http://localhost:3000/user/sign_in" -H "Content-Type: application/json" -d $'{"email": "hoge@hoge.com", "password": "password"}'
HTTP/1.1 200 OK
X-Frame-Options: xxxxxxxxxxxxxxxxxx
X-XSS-Protection: xxxxxxxxxxxxxxxxxx
X-Content-Type-Options: xxxxxxxxxxxxxxxxxx
X-Download-Options: xxxxxxxxxxxxxxxxxx
X-Permitted-Cross-Domain-Policies: xxxxxxxxxxxxxxxxxx
Referrer-Policy: xxxxxxxxxxxxxxxxxx
Content-Type: application/json; charset=utf-8
access-token: xxxxxxxxxxxxxxxxxx
token-type: xxxxxxxxxxxxxxxxxx
client: xxxxxxxxxxxxxxxxxxx
expiry: xxxxxxxx
uid: hoge@hoge.com
Vary: xxxxxxxxxxxxxxxxxx
ETag: xxxxxxxxxxxxxxxxxx
Cache-Control: xxxxxxxxxxxxxxxxxx
X-Request-Id: xxxxxxxxxxxxxxxxxx
X-Runtime: xxxxxxxxxxxxxxxxxx
Transfer-Encoding: xxxxxxxxxxxxxxxxxx
ログインが成功して作成したUserのaccess-token
、token-type
、client
、expiry
、uid
をレスポンスヘッダーに含まれるので、以降はこれらをリクエストヘッダーに入れてアクセスする感じ。
続いて管理者用のモデルを追加してみる
devise_token_authは複数の認証モデルもサポートしているので、新しく管理者用のモデルとしてAdminモデルを追加みてみる。
$ rails g devise_token_auth:install Admin admin
$ rails db:migrate
mount_devise_token_auth_for 'User', at: 'user'
mount_devise_token_auth_for 'Admin', at: 'admin'
だいだいこんな感じでAdminモデルの認証機能ができるので、適当にAdminを作成してアクセストークンを取得してみる。
Admin.create!(email: "admin@hoge.com", password: "password")
$ curl -D - -X "POST" "http://localhost:3000/admin/sign_in" -H "Content-Type: application/json" -d $'{"email": "admin@hoge.com", "password": "password"}'
HTTP/1.1 200 OK
X-Frame-Options: xxxxxxxxxxxxxxxxxx
X-XSS-Protection: xxxxxxxxxxxxxxxxxx
X-Content-Type-Options: xxxxxxxxxxxxxxxxxx
X-Download-Options: xxxxxxxxxxxxxxxxxx
X-Permitted-Cross-Domain-Policies: xxxxxxxxxxxxxxxxxx
Referrer-Policy: xxxxxxxxxxxxxxxxxx
Content-Type: application/json; charset=utf-8
Vary: xxxxxxxxxxxxxxxxxx
ETag: xxxxxxxxxxxxxxxxxx
Cache-Control: xxxxxxxxxxxxxxxxxx
X-Request-Id: xxxxxxxxxxxxxxxxxx
X-Runtime: xxxxxxxxxxxxxxxxxx
Transfer-Encoding: xxxxxxxxxxxxxxxxxx
なんてこった、access-token
、token-type
、client
、expiry
、uid
がない。
さっきのUserではあったのに、いったい何故・・・
解決策
stackoverflowに同じような事例があった。
どうもactive_model_serializerの影響っぽい。
ソースコードあんまり読んでないけど、ここで指定しているscopeがcurrent_userになってるから?
とりあえず回答にあるように、devise_token_authのSessionsControllerをoverrideしたらちゃんとアクセストークンが返ってきた。
def render_create_success
render json: {
data: resource_data(resource_json: @resource.token_validation_response)
}, scope: :current_admin
end
active_model_serializerが影響してるとは思わなかったのでハマってしまったが、もっと良い解決策がないかもう少し調査が必要かも・・・
追記
overrideするよりapplication_controllerに以下を追加する方法のほうが良さそう。
serialization_scope :view_context