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


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

構成

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

devise, devise_token_authの導入

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

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

Gemfile

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

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

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

参考:[Rails] devise token auth を使う

各種ファイルの変更

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

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系の除外設定をします。
それ以外は手動で対応していきます。

.rubocop.yml

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

model, migrationの変更

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

app/models/user.rb
   devise :database_authenticatable, :registerable,
-         :recoverable, :rememberable, :trackable, :validatable
+         :rememberable, :validatable
   include DeviseTokenAuth::Concerns::User
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します。

$ rails db:migrate

email, password以外のカラム許可

curlで試してみます。

$ curl localhost:8080/v1/auth -X POST -H 'Content-Type: application/json' -d '{"email": "[email protected]", "password": "password", "name": "hoge"}'
{"status":"success","data":{"uid":"[email protected]","id":1,"email":"[email protected]","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同様、ストロングパラメータで登録可能なカラムが限られているからです。
そのため以下対応を行います。

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カラムを含めた登録ができるようになります。

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

参考:超基本的なdeviseの使い方

sign_inの確認

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

$ curl localhost:8080/v1/auth/sign_in -X POST -H 'Content-Type: application/json' -d '{"email": "[email protected]", "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":"[email protected]","provider":"email","uid":"[email protected]","name":"hoge"}}

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

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

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

$ curl localhost:8OST -H 'Content-Type: application/json' -d '{"email": "[email protected]", "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モデルのテストとバリデーション追加
連載目次へ