Rails 6で認証認可入り掲示板APIを構築する #10 devise_token_auth導入

←[Rails 6で認証認可入り掲示板APIを構築する #9 serializer導入](https://qiita.com/rf_p/items/be2c97c85253f4a1a914)

# 構成

現在は誰でもAPIを叩けばpostできますが、これをログインしたユーザーに紐付ける構成に変えたいと思います。

# devise, devise_token_authの導入

deviseはRailsにおける認証周りのデファクトスタンダードです。
これを入れて設定するだけで、ユーザー作成からログイン・ログアウト、パスワード再発行やログイン回数記録、ログイン失敗ブロック等の多岐にわたる機能が使えます。
多機能ゆえにカスタマイズが逆に難しいのが難点ではありますが…

そのdeviseのトークン認証版がdevise_token_authです。
deviseの派生のため、devise_token_authにはdeviseが必要です。

```diff:Gemfile

...
+ # 認証
+ gem "devise"
+ gem "devise_token_auth"
```

deviseとdevist_token_authの両方をインストールします。

```powershell
$ rails g devise:install
$ rails g devise_token_auth:install User auth
```

参考：[[Rails] devise token auth を使う](https://qiita.com/Masahiro_T/items/6bc49a625b437a7c2f45)

# 各種ファイルの変更

実行時にconfig/routes.rbが書き換えられますが、今回はv1のnamespace内に入れたいので直します。

```diff:config/routes.rb
 # frozen_string_literal: true

 Rails.application.routes.draw do
-  mount_devise_token_auth_for 'User', at: 'auth'
   namespace "v1" do
     resources :posts
+    mount_devise_token_auth_for 'User', at: 'auth'
   end
 end
```

また、自動生成したファイルがrubocopに引っかかりまくっているので修正します。
ちょっと雑ですが、migrate系やconfig系の除外設定をします。
それ以外は手動で対応していきます。

```diff:.rubocop.yml

...
+ # メソッドの長さ
+ Metrics/MethodLength:
+   Exclude:
+     - "db/migrate/**/*"
+
+ # AbcSize
+ Metrics/AbcSize:
+   Exclude:
+     - "db/migrate/**/*"
+
+ # 行の長さ
+ Layout/LineLength:
+   Exclude:
+     - "config/initializers/**/*"
```

# model, migrationの変更

今回は必要最低限の機能のみ使うので、不要な初期値を消します。

```diff:app/models/user.rb
   devise :database_authenticatable, :registerable,
-         :recoverable, :rememberable, :trackable, :validatable
+         :rememberable, :validatable
   include DeviseTokenAuth::Concerns::User
```

```diff:db/migrate/xxxxxxxxxxxxxx_devise_token_auth_create_users.rb
       ## Database authenticatable
       t.string :encrypted_password, null: false, default: ""

 
-      ## Recoverable
-      t.string   :reset_password_token
-      t.datetime :reset_password_sent_at
-      t.boolean  :allow_password_change, default: false
-
       ## Rememberable
       t.datetime :remember_created_at
 

-      ## Confirmable
-      t.string   :confirmation_token
-      t.datetime :confirmed_at
-      t.datetime :confirmation_sent_at
-      t.string   :unconfirmed_email # Only if using reconfirmable
-
-      ## Lockable
-      # t.integer  :failed_attempts, :default => 0, :null => false # Only if 
lock strategy is :failed_attempts
-      # t.string   :unlock_token # Only if unlock strategy is :email or :both
-      # t.datetime :locked_at
-
       ## User Info
       t.string :name
-      t.string :nickname
-      t.string :image
       t.string :email
 
...
     add_index :users, :email, unique: true
     add_index :users, %i[uid provider], unique: true
-    add_index :users, :reset_password_token, unique: true
-    add_index :users, :confirmation_token,   unique: true
-    # add_index :users, :unlock_token,       unique: true
   end
```

ここまで変更を終えたらmigrateします。

```powershell
$ rails db:migrate
```

# email, password以外のカラム許可

curlで試してみます。

```powershell
$ curl localhost:8080/v1/auth -X POST -H 'Content-Type: application/json' -d '{"email": "test@example.com", "password": "password", "name": "hoge"}'
{"status":"success","data":{"uid":"test@example.com","id":1,"email":"test@example.com","provider":"email","name":null,"created_at":"2020-09-08T04:40:44.659Z","updated_at":"2020-09-08T04:40:44.827Z"}}
```

無事に登録できましたね。
しかしよく見ると、nameがhogeで指定したにも関わらずnullになっています。

この原因の推測ができると、Rails慣れしてきた証拠です。

他のcontroller同様、ストロングパラメータで登録可能なカラムが限られているからです。
そのため以下対応を行います。

```diff:app/controllers/application_controller.rb
 class ApplicationController < ActionController::API
   include DeviseTokenAuth::Concerns::SetUserByToken
   rescue_from ActiveRecord::RecordNotFound, with: :render_404
+  before_action :configure_permitted_parameters, if: :devise_controller?
 
   def render_404
     render status: 404, json: { message: "record not found." }
   end
+
+  def configure_permitted_parameters
+    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
+  end
 end
```

これにより、nameカラムを含めた登録ができるようになります。

```powershell
$ rails db:reset
$ curl localhost:8080/v1/auth -X POST -H 'Content-Type: application/json' -d '{"email": "test@example.com", "password": "password", "name": "hoge"}'                                                  
{"status":"success","data":{"uid":"test@example.com","id":1,"email":"test@example.com","provider":"email","name":"hoge","created_at":"2020-09-08T04:51:42.527Z","updated_at":"2020-09-08T04:51:42.698Z"}}
```

参考：[超基本的なdeviseの使い方](https://qiita.com/yoshimitsu41/items/ba5487e18278822828a3)

# sign_inの確認

登録はできたので、ログインの確認をしていきます。

```powershell
$ curl localhost:8080/v1/auth/sign_in -X POST -H 'Content-Type: application/json' -d '{"email": "test@example.com", "password": "password"}' -i
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Content-Type: application/json; charset=utf-8
access-token: T4ZeomARybw3_o5nIHQAfw
token-type: Bearer
client: Fj772-EYBPnvJdETYhObyQ
expiry: 1600751367
uid: test@example.com
ETag: W/"8c41022d2e42ca28df0cb958a84ab2f4"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: d4ff732c-f6b5-4213-8984-5d9457b39cbd
X-Runtime: 0.510436
Transfer-Encoding: chunked

{"data":{"id":1,"email":"test@example.com","provider":"email","uid":"test@example.com","name":"hoge"}}
```

`-i`オプションを付けたことでヘッダ情報も一緒に返ってきます。
そして末尾に先程登録したuserの情報が載っており、ヘッダでも200 OKで返ってきていることから、正常にログインできていることが分かります。

注目すべきヘッダは
`access-token: T4ZeomARybw3_o5nIHQAfw
client: Fj772-EYBPnvJdETYhObyQ
uid: test@example.com`この3つです。
この3つをリクエスト時のヘッダに含めることで、認証されたアカウントのアクセスであると判別されます。

余談ですが、誤った認証情報だとどうなるのか。
試しにemailやpasswordを誤った状態でログインを試行してみましょう。

```powershell
$ curl localhost:8OST -H 'Content-Type: application/json' -d '{"email": "test@example.com", "password": "PASSWORD"}' -i                                                       
HTTP/1.1 401 Unauthorized
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache
X-Request-Id: 12832212-9797-465b-a5b1-ecaa7e88a977
X-Runtime: 0.308726
Transfer-Encoding: chunked

{"success":false,"errors":["Invalid login credentials. Please try again."]}
```

401で返ってきますね。

# 続き

→[Rails 6で認証認可入り掲示板APIを構築する #11 userモデルのテストとバリデーション追加](https://qiita.com/rf_p/items/59b83f2f799d5a0c537c)
【[連載目次へ](https://qiita.com/rf_p/items/a846553c11571f4177eb#%E7%9B%AE%E6%AC%A1)】