🍈
【難あり】Rails × Knock でJWT認証をしてみました
環境
- rails 7.2.1
- sqlite3 2.1.0-aarch64-linux-musl
- ユーザー認証には、deviseを利用
JWT認証
- Sessionを利用した認証とは異なり、ステートレスな認証方法
- JWT認証の流れを理解するがわかりやすかったです
Knock
- https://github.com/nsarno/knock
- JWT認証をやってくれるgem
- ユーザー認証して、トークン発行して、返してくれるというシンプルさがよいです
- 現在はアーカイブされており、メンテはされていない模様です
- Knockを使わず、参考にして実装をすると良さそうです!とりあえず今回はどんな感じなのか知るために使ってみます
- (代替となるようなgemを誰か作ってくれないかな、、)
1. 導入
1-1. gem追加
- Gemfileに追加して、
bundle install
gem 'knock', '~> 2.1.1'
# bundle install
1-2. 初期設定ファイル作成
-
以下コマンド実行
rails generate knock:install
-
config/initializers/knock.rb
が作成されます。各種設定値が変更できます。
ruby:config/initializers/knock.rb Knock.setup do |config| config.token_lifetime = 7.day config.token_secret_signature_key = -> { Rails.application.credentials.secret_key_base } # 以下略... end
2. 実装
2-1. token発行用のController作成
- 以下コマンド実行terminal
rails generate knock:token_controller user
- token発行リクエスト先として、以下のrouteが追加されます
config/routes.rbRails.application.routes.draw do post 'user_token' => 'user_token#create' # 以下略 end
- routeに対応する、以下のcontrollerが追加されます
app/controllers/user_token_controller.rbclass UserTokenController < Knock::AuthTokenController end
2-2. Userモデル用意
- Userモデル作成
- authenticateメソッド必要です
- Requirements
-
has_secure_passwordの利用が例として挙げられています。今回はユーザー認証はdeviseを利用しているので、deviseのユーザー認証用メソッドである
valid_password?
にエイリアスを設定しました。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable alias_method :authenticate, :valid_password? end
2-3. ユーザー認証処理の追加
-
Knockモジュールのインクルード
app/controllers/application_controller.rbclass ApplicationController < ActionController::API include Knock::Authenticable end
-
ユーザー認証用before_actionの追加
app/controllers/application_controller.rbclass ApplicationController < ActionController::API include Knock::Authenticable # ユーザー認証 before_action :authenticate_user end
3. Knockのロードエラーへの対応
-
Rails6以降からオートローダー(必要に応じてクラスやモジュールを開発者の代わりに自動読み込みしてくれる機能)がClassicモードからZeitwerkモードに変更された影響で、Knock::Authenticableが読み込めません。
#<NameError: uninitialized constant Knock::Authenticable>
- 詳細は、Classic から Zeitwerk への移行を参照してください
-
Rails6でknockを導入してload関連のエラーが発生したときの対処法 を参照し、明示的にKnockライブラリーをロードするようにします
config/initializers/knock.rbrequire 'knock/version' require 'knock/authenticable' ... 以下略 ...
4. 認証を試してみる
4-1. User作成
- rails console内でユーザーを作成
app(dev)> User.create!(email: "test@example.com", password: "testtesttest")
4-2. token発行
-
/user_tokenへPOSTすると、tokenが返ります
POST /user_token {"auth": {"email": "foo@bar.com", "password": "secret"}}
4-3. tokenでの認証
-
Userの情報を取得するAPIの作成(テキトー)
config/routes.rbresources :users, only: [:show]
app/controllers/users_controller.rbclass UsersController < ApplicationController def show # okを返すだけです render status: :ok end end
-
tokenなしでAPIへアクセスすると、
401 unauthorized
が返ります
-
tokenありでAPIへアクセスすると、
200 ok
が返ります
Discussion