Rails: Deviseを徹底理解する(1)基礎編(翻訳)
原注
本記事は、Rubyの隠れたgem: Deviseシリーズ記事の一部です。
- Rails: Deviseを徹底理解する(1)基礎編(翻訳) -- 本記事
- Rails: Deviseを徹底理解する(2)応用編
Devise gemは、Rubyの世界で広く用いられているgemの1つで、GitHubの★が20,000個を超え、多くの統合も行われています。今になってDeviseをRubyの「隠れた」gemと呼ぶ理由は何なのでしょうか?実は、Deviseの人気は非常に高いにもかかわらず、多くの開発者がこのライブラリの機能を十分に活用していないからです。
2部構成の本シリーズでは、Deviseを深く掘り下げていきます。
このパート1記事では、以下を含む基礎をある程度学びます。
- Deviseとは何か、Deviseを最初に使うべき理由、Deviseを使わない方がよい場合
- Deviseのインストール方法、およびプロジェクトでの利用方法
- プロジェクトに合わせてDeviseライブラリをカスタマイズする方法
次回パート2記事では、以下を含むDeviseの高度な利用法を見ていきます。
- OmniAuth、およびAPI専用アプリケーションでのDeviseの使い方
- Deviseを認証ライブラリと統合する
それでは始めましょう!
🔗 前提条件
このチュートリアルでは、ユーザー機能やタスク機能を備えたシンプルなRuby on Rails 7アプリケーションを使います。
ユーザーは登録、ログイン、ログアウトが可能で、ユーザーに割り当てられたロール(役割)に応じてタスクのcreate
、read
、update
、delete
操作を実行できます。
このアプリを使って、複雑な機能を徐々に構築しながら、Deviseの強力な機能を実際に活用する様子をお見せします。
まずはDeviseを手短に紹介します。
🔗 Devise gemとは
Deviseは、Rackベースの認証フレームワークであるWarden gemを基にした認証(authentication)ライブラリです。
Wardenは、ログイン済みユーザーの身元をセキュアなセッション文字列で確認するために、ユーザーセッションを処理します。
また、ログインしていないユーザーが、制限されたリソースにアクセスできないようにする処理も行います。
Wardenは純粋にRackベースのライブラリなので、コントローラアクションや、ビュー、ヘルパーなど、適切なユーザー認証ソリューションを構築するために必要な設定オプションを追加しません。一方、Deviseはそうしたものを追加します。
Deviseのもう1つの大きな特徴は、モジュール性です。
このライブラリにはモジュールが10個ほど付属しており、アプリケーションで認証をどのように扱いたいかを正確に指定できます。
必ずしも10個のモジュールをすべて使う必要はなく、アプリケーションに必要なものだけを有効化して利用します。
これらのモジュール(Registerable
モジュール、Omniauthable
、Trackable
など)について詳しくは後述します。
以上を念頭に置いて、Tasksアプリの構築とDeviseのインストールを始めることにしましょう。
🔗 Deviseをインストールする
bundle exec rails new tasks_app
を実行して、Rails 7アプリを新規に生成します1。または以下のサンプルアプリのコードをリポジトリから取得します2。
アプリのrootディレクトリにいることを確認してから、bundle add devise
を実行します。これにより、Devise gem がアプリの Gemfile に追加されます。
続いて、bundle exec rails g devise:install
を実行して Devise をインストールし、初期化ファイル(config/initializers/devise.rb)を生成します(Devise のモジュールを詳しく見るにはこのファイルを見てみましょう)。
なお、このコマンドを実行すると、Deviseが正常に動作するためのいくつかのセットアップの提案がジェネレータによって表示されます。この提案に沿って進めておけばOKです。
▶参考: Deviseインストールメッセージ(クリックすると表示されます)
Depending on your application's configuration some manual setup may be required:
1. Ensure you have defined default url options in your environments files. Here is an example of
default_url_options
appropriate for a development environment in config/environments/development.rb:config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production,
:host
should be set to the actual host of your application.Required for all applications.
2. Ensure you have defined
root_url
to something in your config/routes.rb.For example:
root to: "home#index"
Not required for API-only Applications
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p>
Not required for API-only Applications
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
Not required
次に、Deviseが連携するユーザーモデルを生成しましょう。
🔗 Deviseのユーザーモデルを生成する
Deviseでは何らかのユーザーモデルが必要です。このモデルにどんな名前を付けるかは、完全に自分の好みとアプリケーションの利用方法次第ですが、User
またはAdmin
にするのが普通です。
bundle exec rails g devise User
を実行して、Deviseのユーザーモデルを生成します。これにより、app/models/user.rb
の下にUser
モデルが生成され、users
テーブル作成用のマイグレーションファイルと、ユーザーリソースへアクセスするためのルーティングも生成されます。
# app/config/routes.rb
Rails.application.routes.draw do
devise_for :users
root "home#index"
end
bundle exec rails db:migrate
を実行して、マイグレーションを実行し、usersテーブルをセットアップします。これにより、認証に関するすべての操作で利用可能なUserモデルが作成されます。
次に、今後利用するTaskモデルをセットアップします。
🔗 Taskモデルを生成する
私たちのシンプルなtasks_appでDeviseを使う場合の要件は以下になります。
- ユーザーが登録を行えるようにする
- ユーザーがアプリにサインイン/サインアウトできるようにする
- ユーザーが、自分が属しているTaskを作成できるようにする
- ユーザーのロールに応じて、Taksにアクセスを許可(または禁止)する
以上を念頭に置いて、ユーザーがやりとりするTaskモデルを生成します。
bundle exec rails g scaffold Task user:references title body:text status:integer
これで、作成したユーザーにTaskモデルを関連付けできるようになり、title
、body
、およびstatus
(タスクが完了しているかどうかを示す)属性もTaskモデルに追加されたはずです。
終わったら、bundle exec rails db:migrate
を実行してtasksテーブルを作成します。このとき、以下のようにTaskモデルとUserモデルの関連付けも行ってください。
# app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :tasks, dependent: :destroy #この行を追加
end
scaffoldジェネレータを実行すれば、belongs_to :users
関連付けが自動的にTaskモデルに追加されます。そのうえで、以下のようにTaskモデルを編集してstatus
でenum
を使うようにします。
# app/models/task.rb
class Task < ApplicationRecord
belongs_to :user
enum :status, { draft: 0, underway: 1, done: 2, archived: 3 }
end
それでは、いよいよDeviseについて詳しく掘り下げていきましょう。最初に、Deviseのモジュールの概要を手短に押さえておきます。
🔗 Deviseのモジュール
本記事冒頭で述べたように、Deviseの主な特徴の1つはモジュール性です。しかし、この「モジュール性」は実際には何を意味するのでしょうか?簡単に言えば、認証プロセスを分割して独立して管理するという意味です。
本記事執筆時点では、Deviseに以下の10個のモジュールがあります。
DatabaseAuthenticatable
- このモジュールは、ユーザーが提供したパスワードを安全なハッシュに変換してデータベースに保存します。また、ユーザーがサインインする際のバリデーションも行います。
Omniauthable
- OmniAuth認証を有効にします。
Lockable
- このモジュールは、ログイン失敗の回数に応じてアカウントをロックします。アカウントは、一定の時間経過後またはメール経由で再び利用可能となります。
Trackable
- アカウントのログイン回数、使用されたIPアドレス、ログインのタイムスタンプをトラッキングします。
Confirmable
- アカウントが登録されると、確認手順をメールで送信します。ユーザーのログイン時にもアカウントが確認済みかどうかをチェックします。
Registerable
- ユーザーがアプリにアカウントを登録できるようにし、ユーザーによるアカウントの編集や削除も処理します。
Recoverable
- パスワードのリセットとアカウントの復旧を処理します。
Timeoutable
- 指定の時間が経過した後、ユーザーのセッションを無効にします。
Validatable
- アカウント作成時にユーザーが提供するメールアドレスやパスワードをカスタムバリデーションするルールを定義できます。
Rememberable
- ユーザーの認証中に、ユーザーをcookieで記憶します(ログインを保存)。
モジュールについて詳しくは、Deviseモジュールのドキュメントが有用です。
次は、Deviseのヘルパーやフィルタについてです。
🔗 Deviseのヘルパーとフィルタ
Deviseなどの認証ライブラリを使う理由の1つは、コントローラのリソースや、それに関連付けられるビューへのアクセスを管理するためです。Deviseには以下のような便利なヘルパーが用意されています3。
user_signed_in?
- 現在ログイン中のユーザーがいるかどうかを確認します。
current_user
- 現在ログイン中のユーザーを参照できます。たとえば、
current_user.email
のようなコードスニペットを使って、現在ログイン中のユーザーのメールアドレスを取得できます。 user_session
- 現在ログイン中のユーザーセッションです。
destroy_user_session_path
- ログイン中のユーザーセッションを破棄し、指定されたパスまたはrootパスにリダイレクトします。
new_user_session_path
- ユーザーログイン画面を表示します。
edit_user_registration_path
- 現在ログイン中のユーザーに、登録の詳細を編集するビューへのアクセス権限を与えます。
new_user_registration_path
- 新規ユーザーの登録フォームを持つビューを表示します。
ヘルパーについては以上です。
Deviseは、コントローラのアクセスを管理する便利なbefore_action
フィルタを提供しています。フィルタは以下のように利用可能です。
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user! # モデル名がUserの場合
before_action :authenticate_member! # モデル名がMemberの場合
...
end
次は、DeviseがRailsのstrong parametersとどのように統合されるかについて解説します。
🔗 DeviseとRailsの"Strong Parameters"について
strong parametersは、Ruby on Railsのよく知られた機能であり、リクエストパラメータをオブジェクトに一括で代入する「マスアサインメント(mass assignment)」を防止します。strong parametersでは、通常、コントローラレベルで明示的にリクエストパラメータを宣言しなければなりません。
参考: §4.5 Strong Parameters -- Action Controller の概要 - Railsガイド
Deviseも、Devise固有の適切なコントローラアクションの下で明示的なパラメータ定義を必須にすることで、strong parametersの機能を反映しています。
sign_up
- このメソッドは、デフォルトではDeviseコントローラ
Devise::RegistrationsController#create
で見つかります。デフォルトで許可されるキーは、email
、password
、password_confirmation
です。 sign_in
- このメソッドは、コントローラ
Devise::SessionsController#new
で見つかります。デフォルトで許可される認証キーはemail
とpassword
です。 account_update
- このメソッドは、コントローラ
Devise::Registrations#update
内で見つかります。許可される認証キーは、email
、current_password
、password
、password_confirmation
です。
独自の認証キーを追加する場合に最も便利なのは、ApplicationController
内でbefore_action
フィルタを使う方法です。
以下の例では、ユーザーがサインアップする際に必要なusername
キーを追加しています。
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
end
Deviseのstrong parametersの興味深いユースケースは、Deviseサニタイザにキーの配列を渡す必要がある場合です。
たとえば、フリーランスのマーケットプレイスアプリがあるとします。このアプリにおけるユーザーは、以下のいずれかだとします。
- 雇用可能な「契約者(contractor)」
- 他の契約者を雇用できる「雇用主(employer)」
- 契約者であり、雇用主でもある
ここでは技術的な詳細には触れませんが、この機能は、ユーザーが自分のロールをセレクトボックスの「契約者」と「雇用主」の2つから選んでアカウントを更新可能にすることで実現できます。こうすることで、ユーザーは「契約者」と「雇用主」のいずれか一方、または両方を選択できるようになります。
これを手軽に実現する方法は、ロールをキーとした配列を利用することですが、Railsのstrong parametersで許可されるのは以下のスカラー値のみです。
String
Symbol
NilClass
Numeric
TrueClass
FalseClass
Date
Time
DateTime
StringIO
IO
ActionDispatch::Http::UploadedFile
Rack::Test::UploadedFile
見ての通り、デフォルトでは配列やハッシュなどのオブジェクトは許可されていません。
ユーザーに複数のロールを許可する配列を使えるようにするには、コードを以下のように修正します。
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update) do |user_params|
user_params.permit({ roles: [] },
:email, :password, :password_confirmation, :current_password)
end
end
end
このトピックについて詳しくは、Deviseヘルパーのドキュメントで解説されています。
次は、Deviseのビューやコントローラのカスタマイズ方法を学びます。
🔗 Deviseのビューをカスタマイズする
DeviseはRailsエンジンとして構築されているので、ほとんどのコンポーネントが事前にパッケージ化されています。ログイン用ビュー、サインアップ用ビュー、アカウント詳細の更新用ビュー、パスワードリセット用ビューなどはその良い例です。
アプリを構築するときに、これらの組み込みビューをカスタマイズする必要が生じることがあります。
最初のステップは、bundle exec rails g devise:views
コマンドを実行してビューを生成します。これで、対応するビューが生成されてapp/views/devise
フォルダに配置されます。
Deviseのビューをカスタマイズする実用的な例を使ってみましょう。前のセクションでは、Deviseサニタイザに追加の認証キーを追加する方法を学びました。この例を拡張して、ユーザー登録ビューにusername
フィールドを追加しましょう。
username
フィールドはデフォルトのUserモデルでは利用できないので、bundle exec rails g migration add_column_username_to_user username
マイグレーションを実行してusername
フィールドを追加し、bundle exec rails db:migrate
コマンドを実行してマイグレーションを開始し、usersテーブルにカラムを追加します。
次に、新しいユーザー登録用ビューファイルを開いて、以下のように編集します。
# app/devise/registrations/new.html.erb
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
...
<!-- usernameフィールドを追加する -->
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username, autofocus: true %>
</div>
...
<% end %>
この方法で生成したDeviseのビューは、すべてのビューで使う分には問題ありません。しかし、特定のビューだけをカスタマイズしたい場合はどうすればよいでしょうか?
これは、bundle exec rails g devise:views -v sessions registrations
のような形式で、カスタマイズしたいビューのリストをジェネレータコマンドの-v
フラグに渡すだけでできます。この場合、ログイン/ログアウトプロセスに関連するビューと、サインアップ処理を担当するビューが生成されます。
Deviseのカスタマイズについて先に進みましょう。今度は、Deviseのコントローラやルーティングのカスタマイズ方法を学びます。
🔗 Deviseのコントローラやルーティングをカスタマイズする
Deviseのビューのカスタマイズでできることには限界があります。本格的にカスタマイズをしたい場合は、Deviseのコントローラとルーティングに手をつける必要があります。
アプリで新しいユーザー登録が発生したときに管理者にメールで通知することを考えましょう(この方法は理想的とは言えませんが、そういう要望があった場合を説明する例として進めることにしましょう)。
まず、Devise::RegistrationsController
のsignup
アクションを変更する必要があります。通常、Deviseのコントローラは直接編集できませんが、ビューと同様にジェネレータで生成できます。
bundle exec rails generate devise:controllers users
上のコマンドの末尾はスコープ(この場合users
)です。別のスコープにしたい場合は、それに応じて変更可能です。
コマンド実行が完了すると、生成されたコントローラは次のような構造になっているはずです。
次には、routes.rbファイルを開き、Deviseのルーティングをコントローラ構造の変更に合わせて以下のように修正します。
# config/routes.rb
Rails.application.routes.draw do
resources :tasks
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
root "tasks#index"
end
新しく生成したUsers::RegistrationsController
を開き、新規ユーザーがサインアップするたびに管理者にメールを送信するように変更します。
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
...
def create
super do |resource|
# 管理者へのメール送信に何らかのカスタムメーラーを使う
AdminNotifierMailer.user_signup_notification(resource).deliver_later
end
...
end
このような方法でDeviseのコントローラとアクションにアクセスできるので、アプリケーションの認証フローを自由に変更できるようになります。
🔗 次回予告: Rails: Deviseを徹底理解する(2)応用編
この2回シリーズの記事のパート1基礎編では、Devise gemの基本について解説し、Deviseのさまざまなモジュールやビューやコントローラをカスタマイズする方法を学びました。
次回のパート2では、API認証や、OmniAuthをDeviseと併用する方法など、さらに高度な使い方について詳しく解説します。
それでは次回お会いするまで、happy coding!
P.S. Ruby Magicの記事をいち早くお読みになりたいのであれば、ぜひRuby Magicニュースレターに登録して、記事を見逃さずに読めるようにしましょう!
関連記事
- 訳注: 本記事のDeviseインストール手順では、Railsセットアップの一部が省略されています。詳しくは§3.1 Railsのインストール -- Rails をはじめよう - Railsガイドなどをご覧ください。 ↩
- 訳注: このサンプルアプリは、UserモデルやTaskモデルのセットアップまで完了しています。 ↩
-
訳注: これらのヘルパーは、モデル名が
User
の場合に動的に生成されるメソッドです(Helpers
、UrlHelpers
)。モデル名が異なるとメソッド名も変わります。 ↩
概要
原著者の許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。