Help us understand the problem. What is going on with this article?

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": "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同様、ストロングパラメータで登録可能なカラムが限られているからです。
そのため以下対応を行います。

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": "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の使い方

sign_inの確認

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

$ 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を誤った状態でログインを試行してみましょう。

$ 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モデルのテストとバリデーション追加
連載目次へ

brides-a-tm
『一組でも多くのカップルに “理想の結婚式”のきっかけを』の使命の元、花嫁の理想(ユメ)を叶えるサービス「ハナユメ」「HIMARI」「ハナユメウエディングデスク」を運営しています。
http://brides.a-tm.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした