#2 初学者がrailsとkotlinでTODOアプリを作成する記録 ~認証・認可~
前回の続き
認証・認可の設計
-
Railsでの認証機能には、Devise gemを使用する
- Deviseは、Railsでよく使われる認証機能を提供するgemで、シンプルでセキュアな認証機能を簡単に実装できます。
-
認可には、Pundit gemを使用する
- Punditは、Railsアプリケーションでの認可機能を提供するgemです。アクセス権限を制御するポリシーを定義し、簡潔なコードで認可を実現できます。
処理の流れ
- クライアント側(Kotlin)でユーザーIDとパスワードを入力し、ログインボタンを押下する。
- 入力値のバリデーションを行い、問題がなければサーバー側のログインAPIを呼び出す。
- サーバー側(Rails)で入力された値とデータベースの値を照合し、一致した場合はJWTトークンを生成してレスポンスとして返す。一致しない場合はエラーメッセージを返す。
- クライアント側でレスポンスを受け取り、成功の場合はトークンを保存し、TOP画面に遷移する。失敗の場合はエラーメッセージを表示する。
サーバー側(Rails)
- Devise gemをインストールして、Userモデルを生成する
gem 'devise'
bundle install
rails generate devise:install
rails generate devise User
- Pundit gemをインストールして、ポリシーを生成する
gem 'pundit'
bundle install
rails generate pundit:install
- JWTを扱うために、jwt gemを追加してインストールする
gem 'jwt'
bundle install
-
Deviseの設定ファイルを編集し、JWTを使ったトークン認証ができるようにする
-
API用のコントローラーを作成し、ログイン用のエンドポイントを実装する
例: sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user && user.valid_password?(params[:password])
token = generate_jwt(user)
render json: { token: token }, status: :ok
else
render json: { error: 'Invalid email or password' }, status: :unauthorized
end
end
private
def generate_jwt(user)
JWT.encode({ user_id: user.id, exp: Time.now.to_i + 3600 }, Rails.application.secret_key_base, 'HS256')
end
end
ここでは、入力されたメールアドレスとパスワードを使ってユーザーを検索し、認証が成功した場合にはJWTトークンを生成して返します。
- ルーティング設定を追加して、ログイン用のエンドポイントを有効にする
例: config/routes.rb
Rails.application.routes.draw do
# ...
post 'login', to: 'sessions#create'
end
- ApplicationControllerに認証・認可機能を追加する
例: application_controller.rb
class ApplicationController < ActionController::Base
include Devise::Controllers::Helpers
include Pundit
before_action :authenticate_user!
private
def authenticate_user!
token = request.headers['Authorization']
return head :unauthorized unless token.present?
payload = decode_jwt(token)
return head :unauthorized unless payload
user_id = payload['user_id']
@current_user = User.find_by(id: user_id)
head :unauthorized unless @current_user
end
def decode_jwt(token)
JWT.decode(token, Rails.application.secret_key_base, true, { algorithm: 'HS256' })[0]
rescue JWT::DecodeError
nil
end
end
ここでは、リクエストヘッダーに含まれるJWTトークンを検証して、認証を行います。
アクセス制御の実装(Pundit)
- 各リソースに対応するポリシーを生成する
例:タスクリソースに対するポリシーを生成する場合
rails generate pundit:policy Task
- 生成されたポリシーにアクセス制御のルールを記述する
例:タスクリソースに対するアクセス制御のルールを定義する
app/policies/task_policy.rb
class TaskPolicy < ApplicationPolicy
def create?
user.present?
end
def update?
user.present? && record.user == user
end
def destroy?
update?
end
end
この例では、タスクの作成はログインユーザーに限定し、更新・削除はタスクの作成者にのみ許可します。
- 各コントローラーでアクセス制御を行う
例:タスクコントローラーにアクセス制御を実装する
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
before_action :set_task, only: [:update, :destroy]
def create
@task = current_user.tasks.build(task_params)
authorize @task
# 以降、タスクの保存処理
end
def update
authorize @task
# 以降、タスクの更新処理
end
def destroy
authorize @task
# 以降、タスクの削除処理
end
private
def set_task
@task = Task.find(params[:id])
end
def task_params
params.require(:task).permit(:title, :description, :due_date, :priority, :category)
end
end
ここでは、authorizeメソッドを使ってアクセス制御を実行します。対象となるリソースに対応するポリシーのメソッドが実行され、アクセスが許可されない場合はPundit::NotAuthorizedErrorが発生します。
- Pundit::NotAuthorizedErrorに対する処理を追加する
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
# ...
def user_not_authorized
render json: { error: 'You are not authorized to perform this action.' }, status: :forbidden
end
end
これでサーバー側(Rails)の認証・認可機能が実装できました。
次に、正しく動作するかを確認する手順を説明します。
動作確認手順
- ユーザー登録機能を追加する
Deviseが提供するユーザー登録機能を使って、テスト用のユーザーを作成できるようにします。
config/routes.rb
Rails.application.routes.draw do
devise_for :users, only: :registrations
# ...
end
これにより、/users/sign_upエンドポイントでユーザー登録ができるようになります。
- Postmanやcurlなどのツールを使って、APIの動作確認を行う
まず、ユーザー登録APIを呼び出してユーザーを作成します。次に、ログインAPIを使って認証トークンを取得し、そのトークンを使って認証が必要なAPIを呼び出して動作を確認します。
例:
- ユーザー登録API: POST /users/sign_up
- ログインAPI: POST /login
- タスク作成API: POST /tasks
-
Railsのコントローラーでアクションが実行される前に、認証・認可が正しく行われているかを確認する
デバッガ(例:binding.pry)やRails.loggerを使って、認証・認可処理が正しく行われているかを確認します。 -
Railsのテスト(RSpecなど)を使って、認証・認可機能のテストを書く
自動化されたテストを書くことで、今後の開発で認証・認可機能に影響が出る変更があった場合でも、機能が正しく動作するかを確認できます。
以上の手順で、実装した認証・認可機能が正しく動作するかを確認できます。問題があった場合は、エラーメッセージやログを参考にしてデバッグし、実装を修正してください。
Discussion