Deviseをマスターしよう:Ruby on Rails認証の基本ガイド
20,000以上のGitHubスターと豊富な統合機能を誇るDevise gemは、Rubyエコシステムで最も人気のあるgemのひとつです。では、なぜこれをRubyの「隠れた」宝石と呼ぶのでしょうか?それは、非常に人気がありながらも、ほとんどの開発者がこのライブラリの能力の表面をなぞっているだけだからです。
本記事は2部構成のシリーズで、Deviseについて深く掘り下げていきます。
第1回目となる今回は、以下のような基本事項を学びます。
- Deviseとは何か、なぜ使うべきなのか。さらに、使用が推奨されない状況についても解説します。
- Deviseのインストール方法と、プロジェクトでの使い方。
- プロジェクトに合わせたライブラリのカスタマイズ方法。
第2回目では、より高度な使い方として以下を取り上げます。
- OmniAuthの活用と、API専用アプリケーションでのDeviseの使い方。
- 認可ライブラリとの統合方法。
それでは始めましょう!
前提条件
本チュートリアルでは、ユーザーとタスクを扱うシンプルなRuby on Rails 7アプリケーションを使用します。ユーザーは登録・ログイン・ログアウトができ、割り当てられたロールに応じてタスクに対するcreate(作成)、read(読み取り)、update(更新)、delete(削除)アクションにアクセスできます。
このアプリを使って段階的に複雑な機能を構築しながら、Deviseの強力な機能を実践的に紹介していきます。
まずはDeviseの簡単な紹介から始めましょう。
Ruby向けDeviseとは
Deviseは、Rackベースの認証フレームワークであるWardenの上に構築された認証ライブラリです。
Wardenは安全なセッション文字列を使用してログイン済みユーザーのIDを検証し、ユーザーセッションを管理します。また、ログインしていないユーザーの処理も担当し、制限されたリソースへのアクセスを防ぎます。
しかし、Wardenは純粋にRackベースであるため、本格的なユーザー認証ソリューションを構築するために必要なコントローラアクション、ビュー、ヘルパーなどの設定オプションは提供されていません。その点、Deviseはこれらすべてを備えています。
Deviseのもうひとつの注目すべき特徴は、そのモジュール性です。このライブラリには約10個のモジュールが用意されており、アプリケーションで認証をどのように処理するかを細かく指定できます。10個すべてのモジュールを使う必要はなく、アプリに必要なものだけを有効化して使用します。これらのモジュール(Registerableモジュール、Omniauthable、Trackableなど)については後ほど詳しく見ていきます。
それを踏まえて、Tasksアプリの構築を開始し、Deviseをインストールしましょう。
はじめの一歩:Deviseのインストール
bundle exec rails new tasks_appコマンドで新しいRails 7アプリを生成します。あるいは、サンプルアプリのコードをリポジトリから取得しても構いません。
アプリのルートディレクトリにいることを確認し、bundle add deviseを実行します。これにより、GemfileにDevise gemが追加されます。その後、bundle exec rails g devise:installを実行してDeviseをインストールし、イニシャライザファイルを生成します(このファイルについては、Deviseのモジュールを解説する際に詳しく見ていきます)。
このコマンドを実行すると、ジェネレータがDeviseを期待通りに動作させるためのセットアップ手順をいくつか提案してくれます。指示に従えば準備完了です。
次に、Deviseが動作するためのユーザーモデルを生成しましょう。
Devise用ユーザーモデルの生成
Deviseにはユーザーモデルが必要です。モデル名は好みやアプリの用途によって自由に決められますが、通常はUserかAdminが使われます。
bundle exec rails g devise UserコマンドでDevise用のユーザーモデルを生成しましょう。これにより、app/models/user.rb配下にUserモデル、ユーザーテーブルを作成するマイグレーションファイル、そしてユーザーリソースにアクセスするためのルートが生成されます。
bundle exec rails db:migrateを実行してマイグレーションを行い、ユーザーテーブルをセットアップします。これで、認証関連のあらゆる処理に使えるユーザーモデルが完成しました。
続いて、今後使用するTaskモデルをセットアップしましょう。
Taskモデルの生成
思い出してください。Deviseを使ったシンプルなアプリには、次の機能が必要です。
- ユーザーが登録できること。
- ユーザーがアプリにサインイン・サインアウトできること。
- ユーザーが自分自身に紐づくタスクを作成できること。
- ロールに応じて、ユーザーがタスクを操作できる(またはできない)ようにすること。
それを踏まえて、ユーザーが操作するTaskモデルを生成しましょう。
これで、作成者であるユーザーと関連付けられたTaskモデルに、タイトル、本文、ステータス(完了したかどうか)が含まれるようになります。
その後、bundle exec rails db:migrateを実行してtasksテーブルを作成します。また、TaskをUserに関連付けておきましょう。
scaffoldジェネレータを実行すると、belongs_to :usersというリレーションが自動的にTaskモデルに追加されます。同時に、Taskモデルを編集して、ステータスに以下のようにenumを使用するようにしておきます。
これで、Deviseをさらに深く掘り下げる準備が整いました。まずはDeviseのモジュールの概要から始めましょう。
Ruby向けDeviseのモジュール
冒頭で述べたように、Deviseの重要な特徴のひとつはモジュール性です。しかし、ここでいう「モジュール性」とは具体的に何を意味するのでしょうか?簡単に言えば、認証プロセスを異なる部分に分割し、それぞれを独立して管理できるようにすることです。
執筆時点での最新版のDeviseには、次の10個のモジュールが含まれています。
- Database authenticatable — ユーザーが入力したパスワードを安全なハッシュに変換してデータベースに保存します。ユーザーのサインイン時の検証もこのモジュールが担当します。
- Omniauthable — OmniAuthによる認証を有効にします。
- Lockable — ログイン失敗回数に応じてアカウントをロックします。一定期間の経過後、またはメール経由でアカウントは再び利用可能になります。
- Trackable — アカウントのログイン回数、使用されたIPアドレス、ログインのタイムスタンプを追跡します。
- Confirmable — アカウント登録時に確認手順を記載したメールを送信し、ログイン時にユーザーのアカウントが確認済みかどうかをチェックします。
- Registerable — ユーザーがアプリにアカウントを登録できるようにし、アカウントの編集と削除も処理します。
- Recoverable — パスワードリセットとアカウント復旧を処理します。
- Timeoutable — 指定した時間が経過するとユーザーセッションを失効させます。
- Validatable — アカウント作成時に、ユーザーが入力したメールアドレスとパスワードに対するカスタムバリデーションルールを定義できます。
- Rememberable — Cookieを使用して、認証時にユーザーを記憶します。
詳細については、Deviseのモジュールに関する公式ドキュメントが役立ちます。ここからは、Deviseのヘルパーとフィルターに移りましょう。
Deviseのヘルパーとフィルター
Deviseのような認証ライブラリを使う理由のひとつは、コントローラのリソースと関連するビューへのアクセス管理です。Deviseには便利なヘルパーが多数用意されています。主なものは以下の通りです。
user_signed_in?— 現在ログインしているユーザーがいるかどうかをチェックします。current_user— 現在ログインしているユーザーを参照できます。例えば、current_user.emailのようなコードでログイン中ユーザーのメールアドレスを取得できます。user_session— 現在ログインしているユーザーのセッションを表します。destroy_user_session_path— ログイン中ユーザーのセッションを破棄し、指定したパスまたはルートパスへリダイレクトします。new_user_session_path— ユーザーログインビューを返します。edit_user_registration_path— 現在ログインしているユーザーが登録情報を編集できるビューへのアクセスを提供します。new_user_registration_path— 新規ユーザー登録フォームを含むビューを返します。
ヘルパーについては以上です。コントローラへのアクセスを管理するために、Deviseは次のように使える便利なbefore_actionフィルターを提供しています。
それでは、DeviseがRailsのストロングパラメータとどのように連携するかに焦点を移しましょう。
DeviseとRuby on Railsのストロングパラメータ
ストロングパラメータは、リクエストパラメータのオブジェクトへの一括代入(mass assignment)を防ぐ、よく知られたRuby on Railsの機能です。ストロングパラメータでは、通常コントローラレベルで、代入を行う前にリクエストパラメータを明示的に宣言することが求められます。
Deviseも同様の仕組みを採用しており、Devise固有のコントローラアクションごとにパラメータを明示的に定義する必要があります。
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キーを追加しています。
Deviseのストロングパラメータの興味深いユースケースのひとつは、キーの配列をDeviseのサニタイザーに渡す必要がある場合です。例えば、フリーランスマーケットプレイスアプリを考えてみましょう。ユーザーは「請負業者(コントラクター)」として受注することも、「発注者(雇用主)」として他のコントラクターを雇うことも、あるいはその両方になることもできます。
技術的な詳細には立ち入りませんが、この機能は、それぞれのロールに対応する2つのセレクトボックス——「コントラクター」と「雇用主」——を使ってユーザーがアカウントを更新できるようにすることで実現できます。ユーザーはどちらか一方、または両方を選択できます。
これを素早く実現するには、ロールをキーとした配列を使いますが、Railsのストロングパラメータで許可されるのは次のスカラー値のみです:String、Symbol、NilClass、Numeric、TrueClass、FalseClass、Date、Time、DateTime、StringIO、IO、ActionDispatch::Http::UploadedFile、Rack::Test::UploadedFile。ご覧の通り、配列、ハッシュ、その他のオブジェクトはデフォルトでは許可されていません。
ユーザーに複数のロールを許可する配列を使うには、コードを以下のように修正します。
ヘルパーに関するDeviseのドキュメントで、このトピックについてより詳しく説明されています。
次に、Deviseのビューとコントローラのカスタマイズ方法を学びましょう。
Deviseビューのカスタマイズ
DeviseはRailsエンジンとして構築されているため、ほぼすべてのコンポーネントが事前にパッケージ化されています。その代表例が、ログイン、サインアップ、アカウント情報の更新、パスワードリセットなどのビューです。アプリ開発を進める中で、これらの組み込みビューを自分のニーズに合わせてカスタマイズしたくなる場面が出てくるでしょう。
最初のステップは、bundle exec rails g devise:viewsコマンドでビューを生成することです。これを実行すると、該当するビューが生成され、app/views/deviseフォルダに配置されます。
Deviseのビューをカスタマイズする実践的な例を見てみましょう。前のセクションでは、Deviseのサニタイザーに追加の認証キーを加える方法を学びました。ここではその例を拡張して、ユーザー登録ビューにusernameフィールドを追加してみます。
usernameフィールドはデフォルトのユーザーモデルには存在しないため、bundle exec rails g migration add_column_username_to_user usernameというマイグレーションで追加します。bundle exec rails db:migrateコマンドを実行してマイグレーションを開始し、ユーザーテーブルにカラムを追加しましょう。
次に、新規ユーザー登録ファイルを開いて、以下のように編集します。
この方法でDeviseのビューを生成するのは、すべてのビューを扱う場合には問題ありません。しかし、一部のビューだけをカスタマイズしたい場合はどうすればよいでしょうか?その場合は、ビューフラグを指定してジェネレータコマンドに必要なビューのリストを渡すだけです:bundle exec rails g devise:views -v sessions registrations。この場合、ログイン/ログアウト処理に関連するビューと、サインアップを処理するビューのみが生成されます。
さらに進んで、ライブラリのコントローラとルートのカスタマイズ方法を学び、Deviseのカスタマイズを深掘りしていきましょう。
Deviseのコントローラとルートのカスタマイズ
Deviseのビューのカスタマイズだけでは、できることに限りがあります。本格的なカスタマイズを行いたいなら、ライブラリのコントローラとルートに踏み込む必要があります。
例えば、新しいユーザーがアプリに登録されるたびに管理者へメール通知を送りたいとしましょう。(これは理想的なやり方ではありませんが、説明のためこの例で進めます)。
まず、Devise::RegistrationsControllerのサインアップアクションを変更する必要があります。通常、Deviseのコントローラは直接編集できません。そこで、ビューの場合と同じように、次のコマンドで生成します。
コマンドの末尾はスコープです。この場合はusersです。別の名前にしたい場合は、適宜変更できます。完了すると、生成されたコントローラは次のような構造になっているはずです。
次に、ルーティングファイルを開き、コントローラ構造の変更を反映するようにDeviseのルートを修正します。
続いて、新しく生成されたUsers::RegistrationsControllerを開き、新しいユーザーがサインアップするたびに管理者へメールを送信するよう修正します。
ご覧の通り、Deviseのコントローラとアクションにアクセスできれば、アプリの認証フローを自由自在に変更できます。
次回予告:Deviseの高度な活用法
この2部構成シリーズの第1回では、Devise gemの基本を解説しました。Deviseのさまざまなモジュールについて学び、ビューとコントローラのカスタマイズ方法も紹介しました。
第2回では、API認証やOmniAuthとDeviseの併用など、より高度な使い方を掘り下げていきます。
それまで、楽しいコーディングを!
P.S. Ruby Magicの記事を公開と同時にお読みになりたい方は、Ruby Magicニュースレターを購読して、記事を見逃さないようにしましょう!
-
Rubyのputs・print・pの違いとは?出力メソッドの使い分けを徹底解説
Rubyには、画面に何かを出力するための方法がいくつかあります。中でも特によく使われるのが、以下の3つのメソッドです。 puts print p しかし、これらは一体どう違うのでしょうか?そしてどんな場面でどれを使えばよいのか?この記事では、それぞれの特徴と使い分けのポイントを詳しく解説します。 改行なしで出力する方法 ユーザーに見せるために画面へ何かを出力したいときは、通常putsを使用します。 例: puts Hello there! putsは、実行するたびにメッセージの末尾へ自動的に改行を追加します。 もし改行したくない場合は、printを使いましょう。 例: print 1
-
JSON Web Token(JWT)認証で Ruby アプリを安全に強化する方法
ユーザーが関わるWebアプリケーションでは、当然ながらユーザーのデータを保護し、安全に管理する必要があります。 Webアプリケーションのセキュリティ確保にはさまざまな側面がありますが、本記事では、その中でも「JSON Web Token(JWT)を使った認証」と、Ruby on Railsフレームワークとの組み合わせに焦点を当てて解説します。 それでは始めましょう! JSON Web Token(JWT)とは? JSON Web Tokenは、Internet Engineering Task Force(IETF)が定義したインターネット標準であり、「2者間で受け渡されるクレーム(主張)を表