←Rails 6で認証認可入り掲示板APIを構築する #9 serializer導入
構成
現在は誰でもAPIを叩けばpostできますが、これをログインしたユーザーに紐付ける構成に変えたいと思います。
devise, devise_token_authの導入
deviseはRailsにおける認証周りのデファクトスタンダードです。
これを入れて設定するだけで、ユーザー作成からログイン・ログアウト、パスワード再発行やログイン回数記録、ログイン失敗ブロック等の多岐にわたる機能が使えます。
多機能ゆえにカスタマイズが逆に難しいのが難点ではありますが…
そのdeviseのトークン認証版がdevise_token_authです。
deviseの派生のため、devise_token_authにはdeviseが必要です。
...
+ # 認証
+ 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内に入れたいので直します。
# 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系の除外設定をします。
それ以外は手動で対応していきます。
...
+ # メソッドの長さ
+ Metrics/MethodLength:
+ Exclude:
+ - "db/migrate/**/*"
+
+ # AbcSize
+ Metrics/AbcSize:
+ Exclude:
+ - "db/migrate/**/*"
+
+ # 行の長さ
+ Layout/LineLength:
+ Exclude:
+ - "config/initializers/**/*"
model, migrationの変更
今回は必要最低限の機能のみ使うので、不要な初期値を消します。
devise :database_authenticatable, :registerable,
- :recoverable, :rememberable, :trackable, :validatable
+ :rememberable, :validatable
include DeviseTokenAuth::Concerns::User
## 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同様、ストロングパラメータで登録可能なカラムが限られているからです。
そのため以下対応を行います。
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"}}
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モデルのテストとバリデーション追加
【連載目次へ】