Rails: 認証gem 'rodauth-rails' README(翻訳)
rodauth-railsは、Rodauth認証フレームワークのRails統合を提供します。
関連リソース
🔗 リンク:
🎥 Screencasts:
📚 記事:
- Rodauth: A Refreshing Authentication Solution for Ruby
- Rails Authentication with Rodauth
- Multifactor Authentication in Rails with Rodauth
- How to build an OIDC provider using rodauth-oauth on Rails
- Rails: 認証gem ‘Rodauth’を統合するrodauth-railsを開発しました
- Social Login in Rails with Rodauth
- Passkey Authentication with Rodauth
🔗 Rodauthを使う理由
Railsには、既に有名な認証ソリューションがいくつもあります(Devise、Sorcery、Clearance、Authlogic)が、Rodauthを選ぶ理由は何でしょうか?特に大きな理由を以下に示します。
- 多要素認証が使える
- 標準化されたJSON APIをあらゆる機能でサポート(JWTなど)
- エンタープライズ向けセキュリティ機能
- パスワードレス認証(メール認証、パスキー)
- あらゆる操作の監査ログ出力
- SQLインジェクション攻撃時でもパスワードハッシュを保護 (詳細)
- 統一された設定用DSL(あらゆるものに実行前・実行後、実行前後のフックをかけられる)
🔗 Sequelについて
Railsの他の認証フレームワークを使っていた方がよく気にするのは、RodauthがデータベースとのやりとりにActive RecordではなくSequelを使っている点ですが、Sequelは強力なAPIを備えており、それによって「高度なクエリのビルド」「複雑なSQL式のサポート」「データベースの種類を問わないデータ演算」「SQL関数呼び出し」といった操作を生SQLを使わずに行えるのです。
rodauth-railsは、Active Recordを用いるRailsアプリ向けに、SequelがActive Recordのデータベースコネクションを再利用する形でSequelを設定します。これによってrodauth-railsはActive Recordとスムーズに連携し、Rodauthの設定内からActive Recordのコードを呼び出すことも可能になっています。Sequelは、単にRodauthの実装詳細として扱えます。
🔗 インストール
プロジェクトにrodauth-rails gemを追加します。
$ bundle add rodauth-rails
続いてインストールジェネレータを実行します。
$ rails generate rodauth:install
テーブル名をaccounts
以外のものにしたい場合は、以下を実行します。
$ rails generate rodauth:install users
RodauthのエンドポイントをJSON APIで公開したい場合は、以下を実行します。
# Railsセッションを用いる通常の認証
$ rails generate rodauth:install --json
# または
# "Authentication"ヘッダー経由のトークン認証
$ rails generate rodauth:install --jwt
$ bundle add jwt
パスワードのハッシュにbcryptではなくArgon2を使う場合は以下を実行します。
$ rails generate rodauth:install --argon2
$ bundle add argon2
このジェネレータは以下を作成します。
- 一般的な認証機能を有効にしたRodauthアプリと設定ファイル
- これらの機能で必要なテーブルを含むデータベースマイグレーション
- デフォルトのテンプレートを含むメーラー
- その他のファイル数個
使わない機能は、対応するテーブルとともに自由に削除できます。
続いてマイグレーションを実行します。
$ rails db:migrate
メーラーでメールのリンクを生成可能にするために、環境ごとにデフォルトのURLオプションを指定する必要があります。以下はconfig/environments/development.rbで利用可能な設定です。
# config/environments/development.rb
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
訳注
rails generate rodauth:install
でインストールジェネレータを実行すると、以下のようなメッセージが表示されます(rodauth-rails 1.6.2の場合)。
(大意)
アプリケーションの設定によってはいくつかの手動セットアップが必要になる場合があります。
- 1: config/environments/のファイルにデフォルトURLオプションを設定すること。以下はconfig/environments/development.rbにおけるdevelopment環境向けの適切な
default_url_options
の例です。
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
production環境では、:host
にアプリケーションの実際のホストを設定すること。
(1はすべてのアプリケーションで必須)
- 2: config/routes.rbの
root_url
に以下のような「何らかの」ルーティングを設定すること。
root to: "home#index"
(2はAPI専用アプリケーションでは不要)
- 3: app/views/layouts/application.html.erbで、たとえば以下のようにflashメッセージを表示できるようにしておくこと。
<% if notice %>
<div class="alert alert-success"><%= notice %></div>
<% end %>
<% if alert %>
<div class="alert alert-danger"><%= alert %></div>
<% end %>
(3はAPI専用アプリケーションでは不要)
- 4: Rodauthページのタイトルはデフォルトで
@page_title
インスタンス変数で利用できるので、レイアウトファイルで以下のようにタイトルを表示できます。
<head>
<title><%= @page_title || "Default title" %></title>
...
</head>
(4は必須ではありません)
- 5: 以下を実行すると、Rodauthのビューをコピーしてカスタマイズできるようになります。
rails g rodauth:views
(5は必須ではありません)
🔗 利用法
Rodauthアプリは、Railsルーターに到達する前にリクエストごとに呼び出されます。これによってRodauthのエンドポイントへのリクエストを処理し、メインルートの前に追加のコードを呼び出せるようになります。
$ rails middleware
# ...
# use Rodauth::Rails::Middleware (calls your Rodauth app)
# run YourApp::Application.routes
🔗 ルーティング
RodauthのエンドポイントへのリクエストはRodaによって処理されるため、rails routes
ではRodauthのルートが表示されない点にご注意ください。
現在読み込まれている機能に基づいてエンドポイントのリストを表示するには、rodauth:routes
というrakeタスクを利用できます。
$ rails rodauth:routes
Routes handled by RodauthApp:
GET/POST /login rodauth.login_path
GET/POST /create-account rodauth.create_account_path
GET/POST /verify-account-resend rodauth.verify_account_resend_path
GET/POST /verify-account rodauth.verify_account_path
GET/POST /change-password rodauth.change_password_path
GET/POST /change-login rodauth.change_login_path
GET/POST /logout rodauth.logout_path
GET/POST /remember rodauth.remember_path
GET/POST /reset-password-request rodauth.reset_password_request_path
GET/POST /reset-password rodauth.reset_password_path
GET/POST /verify-login-change rodauth.verify_login_change_path
GET/POST /close-account rodauth.close_account_path
この情報を元に、アプリのナビゲーションで以下のように基本的な認証をいくつか追加できます。
<% if rodauth.logged_in? %>
<%= link_to "Sign out", rodauth.logout_path, method: :post %>
<% else %>
<%= link_to "Sign in", rodauth.login_path %>
<%= link_to "Sign up", rodauth.create_account_path %>
<% end %>
これらのルーティングは完全に機能するので、自由にアクセスしてページを移動してみてください。Rodauthに付属するテンプレートは完全な認証を体験可能にすることを目的としており、フォームではBootstrapのマークアップが使われています。
🔗 現在のアカウント
Rodauthオブジェクトは#rails_account
メソッドを定義します。このメソッドはアカウントに現在ログインしているアカウントのモデルインスタンスを返します。コントローラやビューから手軽にアクセスできるヘルパーメソッドを作成できます。
class ApplicationController < ActionController::Base
private
def current_account
rodauth.rails_account
end
helper_method :current_account # ActionController::APIから継承する場合はスキップ
end
current_account #=> #<Account id=123 email="user@example.com">
current_account.email #=> "user@example.com"
🔗 認証を必須にする
アプリの特定の部分で認証を要求し、ユーザーがログインしていない場合はログインページにリダイレクトしたいことがあります。これはRodauthアプリのルーティングブロックで実現でき、認証ロジックをカプセル化するうえで有用です。
# app/misc/rodauth_app.rb
class RodauthApp < Rodauth::Rails::App
# ...
route do |r|
# ...
r.rodauth # rodauthリクエストのルーティング
# /dashboard/*ルーティングで認証を要求する
if r.path.start_with?("/dashboard")
rodauth.require_account # 認証されていない場合はログインページにリダイレクトする
end
end
end
認証は以下のようにコントローラ層でも要求できます。
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
private
def authenticate
rodauth.require_account # 認証されていない場合はログインページにリダイレクトする
end
end
# app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
before_action :authenticate
end
🔗 ルーティングのconstraints
で認証する
場合によってはRailsルーターレベルで認証を要求する方が理にかなっていることもあります。これは組み込みのauthenticated
ルーティング制約でできます。
# config/routes.rb
Rails.application.routes.draw do
constraints Rodauth::Rails.authenticated do
# ... 認証済みのルーティング ...
end
end
条件を追加したい場合は、以下のようにRodauthインスタンスで呼び出されるブロックを渡せます。
# config/routes.rb
Rails.application.routes.draw do
# 多要素認証のセットアップを要求する
constraints Rodauth::Rails.authenticated { |rodauth| rodauth.uses_two_factor_authentication? } do
# ...
end
end
以下のようにコンフィグ名を指定することで、別のRodauthコンフィグを指定できます。
# config/routes.rb
Rails.application.routes.draw do
constraints Rodauth::Rails.authenticated(:admin) do
# ...
end
end
さらにカスタマイズが必要な場合は、いつでも以下のように手動でルーティング制約を作成できます。
# config/routes.rb
Rails.application.routes.draw do
constraints -> (r) { !r.env["rodauth"].logged_in? } do # またはenv["rodauth.admin"]
# ユーザーがログインしていない場合のルーティング
end
end
🔗 コントローラ
RodauthのコンフィグはRailsコントローラー(デフォルトではRodauthController
)に接続されており、自動的にそのエンドポイントの前後で定義された任意のコールバックやrescueハンドラを実行します。
class RodauthController < ApplicationController
before_action :set_locale # executes before Rodauth endpoints
rescue_from("MyApp::SomeError") { |exception| ... } # Rodauthエンドポイント前後をrescueする
end
🔗 コントローラのメソッドを呼び出す
rails_controller_eval
を使うと、Rodauthコンフィグから以下のように任意のコントローラーメソッドを呼び出すことが可能です。
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
private
def setup_tracking(account_id)
# ... 何か実装する ...
end
end
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
after_create_account do
rails_controller_eval { setup_tracking(account_id) }
end
end
end
🔗 RailsのURLヘルパー
Rodauthコンフィグやroute
ブロック内では、以下のように#rails_routes
を介してRailsのルーティングヘルパーにアクセスできます。
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
login_redirect { rails_routes.activity_path }
change_password_redirect { rails_routes.profile_path }
change_login_redirect { rails_routes.profile_path }
end
end
🔗 ビュー
Rodauthに組み込まれているビューテンプレートは使い始めのうちは便利ですが、そのうちマークアップを編集したくなるでしょう。以下のコマンドを実行すればRodauthのビューテンプレートをRailsアプリにコピーできます。
$ rails generate rodauth:views # bootstrapのビュー
# または
$ rails generate rodauth:views --css=tailwind # tailwindのビュー(@tailwindcss/formsプラグインが必要)
メインの設定ファイルでRodauthController
が設定されていれば、上のコマンドによって現在有効なRodauth機能のビューテンプレートがapp/views/rodauth/ディレクトリに生成されます。
Rodauthで利用したい機能のリストをジェネレータに渡せば、それらの機能が使えるビューを作成できます(既存のビューは削除されません)。
$ rails generate rodauth:views login create_account lockout otp
以下を実行すれば、すべての機能を含むビューを生成できます。
$ rails generate rodauth:views --all
別のRodauth設定で用いるビューを生成するには--name
オプションを指定します。
$ rails generate rodauth:views webauthn two_factor_base --name admin
🔗 ページタイトル
生成した設定ファイルにはtitle_instance_variable
が設定され、ビューの@page_title
インスタンス変数でページタイトルにアクセス可能になります。これはレイアウトでも利用できます。
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
title_instance_variable :@page_title
end
end
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title><%= @page_title || "Default title" %></title>
<!-- ... -->
</head>
<!-- ... -->
</html>
🔗 レイアウト
Rodauthのビューごとに異なるレイアウトを使い分けたい場合は、layout
メソッドでリクエストパスを比較できます。
# app/controllers/rodauth_controller.rb
class RodauthController < ApplicationController
layout :rodauth_layout
private
def rodauth_layout
case request.path
when rodauth.login_path,
rodauth.create_account_path,
rodauth.verify_account_path,
rodauth.verify_account_resend_path,
rodauth.reset_password_path,
rodauth.reset_password_request_path
"authentication"
else
"dashboard"
end
end
end
🔗 Turbo
Turboはデフォルトですべての組み込みおよび生成ビューテンプレートで無効になっています。理由は、一部のRodauth操作(multi-phaseログイン、リカバリーコードの追加)がTurboと互換性がなく、POSTリクエストで200レスポンスを返すためです。
しかしRodauthのほとんどはTurboと互換性があるので、Turboを使いたい操作では自由に有効にしてください。
🔗 メーラー
インストールジェネレータは、メールテンプレートを持つRodauthMailer
をデフォルトで作成し、認証フローの一環としてメールを送信するRodauthの機能を利用する設定を行います。
# app/mailers/rodauth_mailer.rb
class RodauthMailer < ApplicationMailer
def verify_account(account_id, key) ... end
def reset_password(account_id, key) ... end
def verify_login_change(account_id, key) ... end
def password_changed(account_id) ... end
# def email_auth(account_id, key) ... end
# def unlock_account(account_id, key) ... end
end
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
create_reset_password_email { RodauthMailer.reset_password(account_id, reset_password_key_value) }
create_verify_account_email { RodauthMailer.verify_account(account_id, verify_account_key_value) }
create_verify_login_change_email { |_login| RodauthMailer.verify_login_change(account_id, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_key_value) }
create_password_changed_email { RodauthMailer.password_changed(account_id) }
# create_email_auth_email { RodauthMailer.email_auth(account_id, email_auth_key_value) }
# create_unlock_account_email { RodauthMailer.unlock_account(account_id, unlock_account_key_value) }
send_email do |email|
# queue email delivery on the mailer after the transaction commits
db.after_commit { email.deliver_later }
end
end
end
この設定が呼び出す#deliver_later
は、Active Jobを用いてメールをバックグラウンドジョブで配信します。メールを同期的に送信したい場合は、代わりに設定を変更して#deliver_now
を呼び出せます。
Active Jobアダプタを使わないバックグラウンド処理ライブラリを使う場合や、サードパーティのトランザクショナルなメール送信サービスを使う場合は、このWikiページでセットアップ方法を参照してください。
🔗 マイグレーション
インストールジェネレータは、デフォルトで有効になっているRodauth機能が利用するテーブルのマイグレーションを作成します。Rodauthの追加機能については、必要なテーブルを以下のようにマイグレーションジェネレータで作成できます。
$ rails generate rodauth:migration otp sms_codes recovery_codes
# db/migration/*_create_rodauth_otp_sms_codes_recovery_codes.rb
class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
def change
create_table :account_otp_keys do |t| ... end
create_table :account_sms_codes do |t| ... end
create_table :account_recovery_codes do |t| ... end
end
end
🔗 テーブル名のプレフィックス
アカウントのレコードをaccounts
以外のテーブルに保存している場合は、新しいマイグレーションを生成するときに、以下のように正しいテーブルプレフィックスを指定する必要があります。
$ rails generate rodauth:migration base active_sessions --prefix user
# Add the following to your Rodauth configuration:
#
# accounts_table :users
# active_sessions_table :user_active_session_keys
# active_sessions_account_id_column :user_id
# db/migration/*_create_rodauth_user_base_active_sessions.rb
class CreateRodauthUserBaseActiveSessions < ActiveRecord::Migration
def change
create_table :users do |t| ... end
create_table :user_active_session_keys do |t| ... end
end
end
🔗 マイグレーション名をカスタマイズする
デフォルトのマイグレーション名は以下のオプションでカスタマイズできます。
$ rails generate rodauth:migration email_auth --name create_account_email_auth_keys
# db/migration/*_create_account_email_auth_keys
class CreateAccountEmailAuthKeys < ActiveRecord::Migration
def change
create_table :account_email_auth_keys do |t| ... end
end
end
🔗 モデル
rodauth-model gemは、アカウントモデルにinclude
できるRodauth::Model
ミックスインを提供します。このミックスインは、password
属性と、有効な認証機能で使われるテーブルの関連付けを定義します。
class Account < ActiveRecord::Base # Sequel::Model
include Rodauth::Rails.model # またはRodauth::Rails.model(:admin)
end
# パスワードハッシュを設定
account = Account.create!(email: "user@example.com", password: "secret123")
account.password_hash #=> "$2a$12$k/Ub1I2iomi84RacqY89Hu4.M0vK7klRnRtzorDyvOkVI.hKhkNw."
# パスワードハッシュをクリアする
account.password = nil
account.password_hash #=> nil
# 関連付け
account.remember_key #=> #<Account::RememberKey> (record from `account_remember_keys` table)
account.active_session_keys #=> [#<Account::ActiveSessionKey>,...] (records from `account_active_session_keys` table)
🔗 複数の設定を使い分ける
複数のアカウント種別ごとに異なる認証ロジックを使い分ける必要がある場合は、そのための新しい設定を作成できます。これは、Rodauth::Rails::Auth
のサブクラスを新たに作成し、名前をつけて登録することで行なえます。
# app/misc/rodauth_app.rb
class RodauthApp < Rodauth::Rails::App
configure RodauthMain # プライマリ設定
configure RodauthAdmin, :admin # セカンダリ設定
route do |r|
r.rodauth # プライマリRodauthリクエストへのルーティング
r.rodauth(:admin) # セカンダリRodauthリクエストへのルーティング
end
end
# app/misc/rodauth_admin.rb
class RodauthAdmin < Rodauth::Rails::Auth
configure do
# ... 機能を有効にする ...
prefix "/admin"
session_key_prefix "admin_"
remember_cookie_key "_admin_remember" # パスワード保存機能を使う場合
# `app/views/admin/rodauth`ディレクトリのビューを探索する
rails_controller { Admin::RodauthController }
end
end
# app/controllers/admin/rodauth_controller.rb
class Admin::RodauthController < ApplicationController
end
これで、アプリケーション内でセカンダリのRodauthインスタンスを参照できるようになります。
rodauth(:admin).login_path #=> "/admin/login"
原注
どのアカウントがどのコンフィグに属しているかという情報もデータベースに保存したいことがよくあります。詳しくはこのガイドを参照してください。なお、コンフィグを継承で共有することも可能です。
🔗 リクエスト外部での利用
🔗 アクションを呼び出す
Rodauthをよりプログラム的に使う必要が生じることがあるかもしれません。認証操作をリクエストのコンテキストの外で行いたい場合は、そのためのinternal_request機能を利用できます。
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
enable :internal_request
end
end
# プライマリ設定
RodauthApp.rodauth.create_account(login: "user@example.com", password: "secret123")
RodauthApp.rodauth.verify_account(account_login: "user@example.com")
# セカンダリ設定
RodauthApp.rodauth(:admin).close_account(account_login: "user@example.com")
🔗 URLを生成する
リクエストの外で認証用URLを生成するには、path_class_methodsプラグインを使います。
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
enable :path_class_methods
create_account_route "register"
end
end
# プライマリ設定
RodauthApp.rodauth.create_account_path # => "/register"
RodauthApp.rodauth.verify_account_url(key: "abc123") #=> "https://example.com/verify-account?key=abc123"
# セカンダリ設定
RodauthApp.rodauth(:admin).close_account_path(foo: "bar") #=> "/admin/close-account?foo=bar"
🔗 インスタンスメソッドを呼び出す
内部リクエストとして公開されていないRodauthメソッドにアクセスする必要がある場合は、Rodauth::Rails.rodauth
を利用すればinternal_request
機能で使われるRodauthインスタンスを取得できます。
# app/misc/rodauth_main.rb
class RodauthMain < Rodauth::Rails::Auth
configure do
enable :internal_request # これは必須
end
end
account = Account.find_by!(email: "user@example.com")
rodauth = Rodauth::Rails.rodauth(account: account) #=> #<RodauthMain::InternalRequest ...>
rodauth.compute_hmac("token") #=> "TpEJTKfKwqYvIDKWsuZhkhKlhaBXtR1aodskBAflD8U"
rodauth.open_account? #=> true
rodauth.two_factor_authentication_setup? #=> true
rodauth.password_meets_requirements?("foo") #=> false
rodauth.locked_out? #=> false
Rodauth::Rails.rodauth
メソッドは、:account
オプションの他に、internal_request
機能でサポートされている任意のオプションを受け取れます。
# プライマリ設定
Rodauth::Rails.rodauth(env: { "HTTP_USER_AGENT" => "programmatic" })
Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
# セカンダリ設定
Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
🔗 ライブラリとして利用する
Rodauthは、ルーティングリクエストの代わりにライブラリとして使用可能にするRodauth.lib
メソッドも提供しています(internal_requestを参照)。このgemは、Rodauth::Rails.lib
相当の機能に加えてRailsの統合も提供します。
# app/misc/rodauth_main.rb
require "rodauth/rails"
require "sequel/core"
RodauthMain = Rodauth::Rails.lib do
enable :create_account, :login, :close_account
db Sequel.postgres(extensions: :activerecord_connection, keep_reference: false)
# ...
end
RodauthMain.create_account(login: "email@example.com", password: "secret123")
RodauthMain.login(login: "email@example.com", password: "secret123")
RodauthMain.close_account(account_login: "email@example.com")
Railsの起動時にミドルウェアを自動的に挿入しないようにするには、rodauth-rails
のrequire
をスキップして、イニシャライザを削除します。
# Gemfile
gem "rodauth-rails", require: false
🔗 テスト
ミドルウェアスタック全体を実行するシステムテストや統合テストでは、HTTPエンドポイントで通常通り認証をテストできます。
たとえば以下のコントローラがあるとします。
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action -> { rodauth.require_account }
def index
# ...
end
end
以下のようにActionDispatch::IntegrationTest
のテストヘルパーを作成することで、Rodauthエンドポイントへのリクエストを行う形でlogin
やlogout
を行えるようになります。
# test/controllers/articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
def login(email, password)
post "/login", params: { email: email, password: password }
assert_redirected_to "/"
end
def logout
post "/logout"
assert_redirected_to "/"
end
test "required authentication" do
get :index
assert_response 302
assert_redirected_to "/login"
assert_equal "Please login to continue", flash[:alert]
account = Account.create!(email: "user@example.com", password: "secret123", status: "verified")
login(account.email, "secret123")
get :index
assert_response 200
logout
get :index
assert_response 302
assert_equal "Please login to continue", flash[:alert]
end
end
Rodauthによるテストの情報やこの他のサンプルコードについては、このWikiページを参照してください。
🔗 設定方法
🔗 設定用メソッド
rodauth-railsが読み込むrails
機能では、以下の設定メソッドが提供されます。
メソッド名 | 説明 |
---|---|
rails_render(**options) |
指定のレンダリングオプションでテンプレートをレンダリングする |
rails_csrf_tag |
CSRFトークンを含む隠しフィールドをRodauthテンプレートに追加する |
rails_csrf_param |
CSRFタグのname 属性の値 |
rails_csrf_token |
CSRFタグのvalue 属性の値 |
rails_check_csrf! |
現在のリクエストで認証トークンを検証する |
rails_controller_instance |
リクエストのenvコンテキストを持つコントローラのインスタンス |
rails_controller |
レンダリングやCSRF保護に用いるコントローラのクラス |
rails_account_model |
accountsテーブルに接続するモデルのクラス |
class RodauthMain < Rodauth::Rails::Auth
configure do
rails_controller { Authentication::RodauthController }
rails_account_model { Authentication::Account }
end
end
Rodauthが提供する設定メソッドのリストについては、Rodauthの機能ドキュメントを参照してください。
🔗 カスタムメソッドを定義する
Rodauthのすべての設定メソッドは、認証クラスの単なるシンタックスシュガーです。以下のようにカスタムメソッドを独自に定義することも可能です。
class RodauthMain < Rodauth::Rails::Auth
configure do
password_match? { |password| ldap_valid?(password) }
end
def admin?
rails_account.admin?
end
private
def ldap_valid?(password)
SimpleLdapAuthenticator.valid?(account[:email], password)
end
end
rodauth.admin? #=> true
🔗 単一の設定ファイルにまとめる
以下のように、RodauthのロジックをすべてRodauthアプリクラス内に配置することも可能です。
# app/misc/rodauth_app.rb
class RodauthApp < Rodauth::Rails::App
# プライマリ設定
configure do
enable :login, :logout, :create_account, :verify_account
# ...
end
# セカンダリ設定
configure(:admin) do
enable :email_auth, :single_session
# ...
end
route do |r|
# ...
end
end
🔗 ミドルウェアを手動で挿入する
Rodauthミドルウェアは、以下のようにRailsルーターより前の位置にも挿入できます。
Rodauth::Rails.configure do |config|
config.middleware = false # 自動挿入を無効にする
end
Rails.application.config.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware
🔗 rodauth-railsのしくみ
🔗 Rackミドルウェア
RailsのrailtieはRackミドルウェアスタックの末尾にRodauth::Rails::Middleware
を挿入し、これがリクエストごとにRodauthアプリを呼び出します。
$ rails middleware
# ...
# use Rodauth::Rails::Middleware
# run MyApp::Application.routes
このミドルウェアは、Rodauth::Rails.app
を介してRodauthアプリを取得します。このクラスをdevelopmentモードでオートロード可能・リロード可能にするために、クラス名を文字列で指定します。
Rodauth::Rails.configure do |config|
config.app = "RodauthApp"
end
これによってZeitwerkとの互換性が保たれるとともに、コントローラレベルで発生するRodauthのリダイレクト(例: before_action
フィルタでrodauth.require_account
を呼ぶ)をこの追加の層がキャッチするようになります。
🔗 Rodaアプリ
Rodauth::Rails::App
クラスはRodaのサブクラスで、以下を行う有用な層をRodauthに提供します。
- Action DispatchのFlashメッセージを利用する
- Rodauthプラグイン読み込み用のシンタックスシュガーを提供する
- RodauthオブジェクトをRackのenvハッシュに保存する
- 編集されたヘッダーをRailsのレスポンスに展開する
🔗 ブロックを渡して設定する
configure
呼び出しはRodauthプラグインを読み込みます。規約としては、この呼び出しは認証クラスと設定名(それぞれプラグインの:auth_class
オプションと:name
オプションに転送されます)を位置引数として受け取ります。また、匿名の認証クラス用のブロックを受け取り、プラグインの任意の追加オプションも受け取ります。
class RodauthApp < Rodauth::Rails::App
# 名前を持つ認証クラス
configure(RodauthMain)
configure(RodauthAdmin, :admin)
# 匿名の認証クラス
configure { ... }
configure(:admin) { ... }
# プラグインのオプション
configure(RodauthMain, json: :only)
end
🔗 ブロックを渡してルーティングする
route
に渡したブロックは、Railsルーターに到達する前にリクエストごとに呼び出されてリクエストオブジェクトをyield
します。
class RodauthApp < Rodauth::Rails::App
route do |r|
# 各リクエストの前に呼び出される
end
end
🔗 ルーティングのprefix
ルーティングでprefix
を指定すると、r.rodauth
は自動的にプレフィックスにルーティングされるように変更されるので、素のRodauthのようなr.on
呼び出しの追加が不要になります。
class RodauthApp < Rodauth::Rails::App
configure do
prefix "/user"
end
route do |r|
r.rodauth # `r.on("user") { ... }`で囲む必要はない
end
end
🔗 Auth
クラス
Rodauth::Rails::Auth
クラスはRodauth::Auth
のサブクラスで、Rodauthのrails
機能をプリロードしてHMACのsecretをRailsのsecretキーベースに設定し、いくつかのデフォルト設定を変更します。
class RodauthMain < Rodauth::Rails::Auth
configure do
# 認証の設定
end
end
🔗 Rodauthの機能
Rodauth::Rails::Auth
で読み込まれるRodauthのrails
機能は、RodauthをRailsに統合するうえで主要な部分を提供します。
- テンプレートのレンダリングにAction Viewを利用する
- CSRF保護にAction Dispatchを利用する
- Action Controllerコールバックと、Rodauthリクエストの前後のブロックからの
rescue
を実行する - メール作成と配信にAction Mailerを利用する
- Rodauthリクエストの前後でAction Controllerのinstrumentationを利用する
- リクエストの外でRodauthを呼ぶときにAction MailerのデフォルトURLオプションを利用する
🔗 コントローラ
Rodauthアプリは、Rodauth::Rails::Auth
のインスタンスをRackのenvハッシュに保存し、Railsアプリで利用できるようにします。
request.env["rodauth"] #=> #<RodauthMain>
request.env["rodauth.admin"] #=> #<RodauthAdmin> (設定を複数使う場合)
利便性のため、ビューやコントローラでも#rodauth
メソッドでこのオブジェクトにアクセスできるようになっています。
class MyController < ApplicationController
def my_action
rodauth #=> #<RodauthMain>
rodauth(:admin) #=> #<RodauthAdmin> (設定を複数使う場合)
end
end
<% rodauth #=> #<RodauthMain> %>
<% rodauth(:admin) #=> #<RodauthAdmin> (設定を複数使う場合) %>
🔗 Rodauthのデフォルト設定
rodauth-railsは、セットアップを容易にするためにRodauth設定の一部を変更します。
🔗 データベース関数
PostgreSQL、MySQL、Microsoft SQL Serverの場合、デフォルトのRodauthはパスワードハッシュにアクセスするときにデータベース関数を利用します。つまり、アプリケーションを実行するユーザーがパスワードハッシュに直接アクセスすることは不可能になります。これにより、攻撃者がパスワードハッシュにアクセスして他のサイトの攻撃に転用するリスクを軽減します。
この機能は追加のセキュリティとして有用ですが、データベースのユーザーが2つ必要で、適切なデータベースユーザーに対してマイグレーションを実行しなければならなくなるため、その分セットアップが複雑になり、理解も難しくなります。
Railsの「設定より規約」という教義を守るために、rodauth-railsではRodauthのこのデータベース関数の利用を無効にしてありますが、以下の設定でいつでも有効にできます。
use_database_authentication_functions? true
データベース関数を作成するには、RodauthのメソッドにSequelのデータベースオブジェクトを渡してください。
# db/migrate/*_create_rodauth_database_functions.rb
require "rodauth/migrations"
class CreateRodauthDatabaseFunctions < ActiveRecord::Migration
def up
Rodauth.create_database_authentication_functions(db)
end
def down
Rodauth.drop_database_authentication_functions(db)
end
private
def db
RodauthMain.allocate.db
end
end
🔗 アカウントのステータス
推奨されているRodauthのマイグレーションでは、可能なアカウントのステータス値を別テーブルに保存し、accountsテーブルに外部キーを作成して、有効なステータス値だけが永続化されるようにしています。
しかし残念ながら、スキーマファイルからデータベースをリストアした場合はこのようにならず、アカウントステータスのテーブルが空になります。testモードではデフォルトでこれが発生しますが、developmentモードで発生することも珍しくありません。
この問題に対処するため、rodauth-railsはstatus
カラムを別テーブルに分けずに利用します。無効なステータス値が混入することが心配な場合は、代わりにenumを使うとよいでしょう。また、Rodauthの推奨セットアップに戻すことはいつでも可能です。
# マイグレーション
create_table :account_statuses do |t|
t.string :name, null: false, unique: true
end
execute "INSERT INTO account_statuses (id, name) VALUES (1, 'Unverified'), (2, 'Verified'), (3, 'Closed')"
create_table :accounts do |t|
# ...
t.references :status, foreign_key: { to_table: :account_statuses }, null: false, default: 1
# ...
end
class RodauthMain < Rodauth::Rails::Auth
configure do
# ...
- account_status_column :status
# ...
end
end
🔗 deadline値
データベーススキーマを変更しやすくするため、rodauth-railsはRodauthが「カラムのデフォルト値設定をデータベースに依存する」のではなく「Rubyのさまざまな機能で用いられるdeadline値を設定する」形でRodauthの設定を変更します。
この設定は簡単に無効にできます。
set_deadline_values? false
ライセンス
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the rodauth-rails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
概要
MITライセンスに基づいて翻訳・公開いたします。
見出しの深さは変えてあります。
rodauth-railsにはRails 7ベースの設定済みデモアプリもあります。
rodauth-rails作者のjanko氏による以下の記事もどうぞ。