EpisodeOmniAuthは最近バージョンが1.0になり、いくつかの便利な機能が追加されました。Identityを紹介します。これによって、ユーザに外部プロバイダを介してログインさせるのではなく、ユーザ名とパスワードを与えてアカウントを作成できるようになりました。現状のアプリケーションのしくみ下の画面が今回の対象のアプリケーションです。3つの外部プロバイダでOmniAuthが設定されています。これらのプロバイダのいずれか、例えばTwitterからログインをおこなうと、Twitterのウェブサイトにリダイレクトされ、アプリケーションがTwitterのアカウント情報にアクセスすることを許可するかどうかを聞かれます。許可をすると、アプリケーションはTwitterからプロファイル情報を取得して、ユーザはログインされます。一度このアプリケーションのアクセスを許可すると、その後確認を求められることはなく、ページ上のアイコンをクリックするだけでTwitterを介してアプリケーションにログインできるようになります。これはユーザのログインを許可する便利な方法ですが、これらの外部サービスを使いたくないユーザを排除していることになります。それらのユーザに対しても、直接サイト内でパスワード付きのアカウントを作成するという選択肢を与えるべきです。Identityが登場します。この作業に入る前に、アプリケーションのいくつかのコードを見て、しくみの概要を理解しておきましょう。ソースコードはエピソード241で使用したアプリケーションに基づいています。OmniAuthについてよく知らないという方はまずその回のエピソードを見ることをお勧めします。エピソード241が書かれてからOmniAuthにはいくつか変更点がありましたが、もっとも重要な点のひとつはgemfileです。各プロバイダは、OmniAuth用の個別のgemを持つようになったので、サポートしたいプロバイダ用の正しいgemをインクルードする必要があります。'omniauth-google-oauth2'OmniAuthの初期化ファイルは以前とほとんど一緒です。そこでOmniAuthをミドルウェアとして追加し、使用したいプロバイダ用のproviderエントリーを追加します。ENV['FACEBOOK_SECRET']endアプリケーションのSessionsControllerのcreateアクションはOmniAuthコールバックとして起動されます。その中でOmniAuthハッシュにもとづいてユーザを作成し、その新規ユーザのidをセッション変数に保存します。endendユーザは、Userモデルのfrom_omniauthメソッドで、OmniAuthから取得されます。endendこのメソッドはまず選択されたプロバイダのユーザとユーザidが存在するかどうかをチェックします。存在すればそのユーザが返され、存在しなければcreate_with_omniauthメソッドが呼び出されて、渡されたOmniAuthハッシュからの情報にもとづいて新規ユーザが作成されます。ハッシュには一つ変更があります。1.0を使用するために既存のアプリケーションをアップグレードしている場合は、コードは正しく動作しません。Identityを追加するOmniAuthを介して認証を処理するために必要なコードは以上です。Identityを追加して、ユーザが外部認証サービスを使用しなくてもアカウントを作成できるようにします。gemを追加することです。'omniauth-identity'このgemはbcrypt-rubyを利用してパスワードハッシュを行いますが、依存関係が設定されていないため、手動でbcrypt-rubyもgemfileに追加する必要があります。gemfileにコメント行があるはずなので、コメントを外してこのgemを追加します。いつもと同じようにbundleコマンドを実行して、すべてのgemがインストールされていることを確認します。providerを追加します。今のところはこのproviderにパラメータを追加する必要はありません。:identityendログイン情報を保存するためにはモデルが必要です。デフォルトではこのモデルはidentityという名前にするべきですが、必要であればカスタマイズすることも可能です。そのモデルにname、email、password_digestの各フィールドを設定します。password_digest:stringテーブルを追加するためにデータベースのマイグレーションをおこないます。IdentityのREADMEには、IdentityモデルをActiveRecordを含む各種のORMで動作させる方法が示されています。モデルを変更して、ActiveRecord::Baseではなく、OmniAuth::Identity::Models::ActiveRecordを継承するよう修正します。OmniAuth::Identity::Models::ActiveRecordendほぼ完成に近づきました。providerへのリンクを、外部プロバイダを介したログインのリンクの下に作成します。password.</p>OmniAuthの設定変更を有効化するためにサーバを再起動します。その後ログインページにアクセスすると、登録とログインのための新しいリンクが表示されます。新規アカウントを登録します。Identityはすっきりしたインターフェースのシンプルな登録フォームを提供します。そこに正しい情報を入力するとログインが完了しプロファイルページが表示されます。ログインページは似ていますが、フィールドが2つしかありません。loginフィールドには、画面からはわかりづらいですが、登録時に入力したメールアドレスを入力します。カスタムの登録・ログイン用フォームを作成する登録とログインはうまく動くようになりました。Identityは他のプロバイダ用のコードと同じOmniAuthのインターフェースを使用しているため、この機能を追加するためにコアのアプリケーションをそれほど修正する必要はありません。いうものの、注意をしなくてはいけない点がいくつかあります。Identityモデルにバリデーションがある場合、フォームに誤った情報を入力してもエラーが表示されません。これを示すために、ここでIdentityモデルにバリデーションを追加します。/^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/iend登録フォームで無効なメールアドレスを入力し登録をおこなおうとすると、どこが問題だったかが何も示されずにフォームに戻されます。同じように、ログインフォームもLoginフィールドにメールアドレスを入力しなくてはいけないことを明示しません。このフォームの見た目をカスタマイズするオプションもありますが、それほど種類は多くありません。よい対応としては、カスタムのフォームをアプリケーション自体の中に作ることで、見た目や振る舞いを完全に自由に制御するという方法があります。ログインフォームを移動します。ログインページでOmniAuthのログインフォームの代わりに、カスタムのログインフォームを表示します。%>このフォームは/auth/identity/callbackにPOSTします。これはOmniAuthの元のフォームがデータを送信する先です。フォームフィールドにはOmniAuthのフォームにあるのと同じ名前をつけます。ログインページをリロードするとログインフォームが表示され、他のフォームと同じにそこにユーザ情報を入力してログインすることができます。同じアプローチで登録フォームも置き換えることができますが、そのためには新規にコントローラを作成する必要があります。identitiesこのコントローラはリソースとして扱うので、routesファイルを修正してそれをリソースとして追加します。:identitiesendコントローラにnewアクションを追加します。登録フォームのバリデーションがうまくいかない場合、Rack環境変数にidentityオブジェクトが保存されることがあるので、それを取得してなんらかエラーメッセージがあれば表示します。endend関連付けられたテンプレートはかなり大きいですが、変わったところは特にありません。%>このテンプレートのフォームが/auth/identity/registerにPOSTします。エラーがあったらフォームの一番上の部分に表示します。その下にはOmniAuthの登録フォームと同じフィールドが並びます。現在のidentityオブジェクトにデータが存在する場合は、nameとemailフィールドに値を設定します。account」のリンクを修正して、OmniAuthのデフォルトフォームではなく新規に作成したフォームにリンクをはり直します。below.</p>このリンクをクリックすると、新しいフォームが表示され、そこから新規アカウントを登録することができます。新しいフォームは有効なアカウントを新規に作成するためにはうまく動作しますが、何らか無効な情報を入力してフォームを送信すると、OmniAuthの登録画面にリダイレクトされてしまいます。providerを修正します。}end追加したon_failed_registrationのオプションはRackアプリケーションを引数にとります。IdentitiesControllerのnewアクションを指定するよう設定して、再度新しく作成したフォームが表示されるようにします。このアクションを直接呼び出す場合にひとつ問題があります。developmentモードではアクションは常にリロードされないので、コードをラムダ(lambda)でラップしてアクションがキャッシュされないようにします。これらの変更を有効化するためにRailsサーバを再起動する必要がありますが、その後は無効な情報を入力した場合にもフォームは期待どおりの動きをします。identityが正しく動作するようになりました。カスタムフォームを作成するのは手間がかかりますが、すでにOmniAuthを使用していてアカウント登録機能を追加するだけでいいのであれば、優れたソリューションとして検討する価値があるでしょう。