🍈

【難あり】Rails × Knock でJWT認証をしてみました

2024/10/16に公開

環境

  • rails 7.2.1
  • sqlite3 2.1.0-aarch64-linux-musl
  • ユーザー認証には、deviseを利用

JWT認証

Knock

  • https://github.com/nsarno/knock
  • JWT認証をやってくれる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.rb
    Rails.application.routes.draw do
      post 'user_token' => 'user_token#create'
      # 以下略
    end
    
    • routeに対応する、以下のcontrollerが追加されます
    app/controllers/user_token_controller.rb
    class UserTokenController < Knock::AuthTokenController
    end
    

2-2. Userモデル用意

  • Userモデル作成
  • authenticateメソッド必要です
    • Requirements
    • has_secure_passwordの利用が例として挙げられています。今回はユーザー認証はdeviseを利用しているので、deviseのユーザー認証用メソッドであるvalid_password?にエイリアスを設定しました。
      app/models/user.rb
      class 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.rb
    class ApplicationController < ActionController::API
      include Knock::Authenticable
    end
    
  • ユーザー認証用before_actionの追加

    app/controllers/application_controller.rb
    class ApplicationController < ActionController::API
        include Knock::Authenticable
        
        # ユーザー認証
        before_action :authenticate_user
    end
    

3. Knockのロードエラーへの対応

  • Rails6以降からオートローダー(必要に応じてクラスやモジュールを開発者の代わりに自動読み込みしてくれる機能)がClassicモードからZeitwerkモードに変更された影響で、Knock::Authenticableが読み込めません。

  • Rails6でknockを導入してload関連のエラーが発生したときの対処法 を参照し、明示的にKnockライブラリーをロードするようにします

    config/initializers/knock.rb
    require '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.rb
    resources :users, only: [:show]
    
    app/controllers/users_controller.rb
    class UsersController < ApplicationController
        def show
          # okを返すだけです
          render status: :ok
        end
    end
    
  • tokenなしでAPIへアクセスすると、401 unauthorizedが返ります

  • tokenありでAPIへアクセスすると、200 okが返ります

参考リンク

Discussion