deviseのHow To: Allow users to sign in with something other than their email address, emailアドレス以外でサインインを行う方法について翻訳しつつ実践していきます。
今回は公式wikiにあるように、usernameというfieldでサインインできるようにしていきますが、適宜置き換えれば好きな名前のカラムをサインイン用のカラムにすることができます。
言語、ライブラリのバージョン
導入から基本的な実装を知りたい場合は最小構成導入からログアウトまでを参照。
どんなことができるようになるのか
このようにemail以外のユニークなカラムで認証できるようになります。
事前準備
事前準備といっても、大掛かりにコードを書き換える必要はありません。deviseが提供している設定を変更し、認証キーに設定されているemailカラムを変更しましょう。
デフォルトではconfig/initializers/devise.rb
にconfig.authentication_keys = [:emil]
という設定がコメントアウトされているので、コメントアウトを外して任意のカラムに書き換えます。今回で言えばusername
というカラムにしたいので下記のようにします。
Devise.setup do |config| ` ` 中略 ~ config.authentication_keys = [:username] #コメントアウトを外して書き換え ` ` end
これで認証キーに設定されているカラムがusernameに変更されます。
補足: 複数のモデルでdeviseを使用している場合で一部のモデルのキーだけemailにしたくない場合
例えばuserモデル以外に、管理userとしてadmin_user
というモデルを利用している状態を想定してみます。どちらもdevise gemを使用して認証を行っているとします。
userモデルはusernameとpasswordという2つのキーで認証し、admin_user
はデフォルトのemail, passwordによって認証したいという要件を想定してみましょう。
この要件は、最初に紹介したconfig/initializers/devise.rb
にあるキーを書き換える方法は得策ではありません。このファイルの内容はdeviseの全ての設定を担っていますので、個別に変更したい場合はこちらの方法を採用しましょう。
deviseを使用しているmodelに個別に記載を加えます。
app/models/user.rb
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable end
デフォルトではUserモデルはこのようになっていますので、
:authentication_keys => [:username]
というオプションを:database_authenticatableに設定しましょう。 追加後のuserモデルはこのようになります。
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :authentication_keys => [:username] :registerable,:recoverable, :rememberable, :validatable end
:補足終了:
では実装に戻りましょう。config/initializers/devise.rbに追記するか,補足にあるような条件の場合はmodel側に個別に記載が済んだ状態になっていると思います。
以降の作業は共通になります。Rnails側でvalidationを設定しておきたいので、
validates :email, uniqueness: true validates :username, uniqueness: true
もモデルに追加しておきます。email, usernameカラムがユニークであるというバリデーションですね。追記したUserモデルは下記のようになります。emailにもユニーク制約をつけているのはdeviseが提供する他の機能でuserが一意である必要があるためです。
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :email, uniqueness: true validates :username, uniqueness: true end
ここまでで初期設定の記述は一旦終了です。
ただ、肝心のusernameカラムをまだ用意していないので、migrationfileを作成していきます。
$ rails g migration add_username_to_users username:string:uniq
この記述でユニークな制約をかけたstring型のusernameカラムをusersテーブルに追加します。 migrationファイルが作成されたらmigrateも続けて行います。
$ rails db:migrate
ここまでで一度schemaファイルを確認しておきましょう。
ActiveRecord::Schema.define(version: 2020_05_13_061723) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.string "username" t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["username"], name: "index_users_on_username", unique: true end end
先ほどのmigrateで上記のようにusernameカラムが作成され、indexが貼られていることを確認してください。
viewファイルをカスタマイズしてusernameを新規登録の際に登録できるようにする
ここまででusernameをモデルのキーにして、userモデルに登録できるようになりましたが、新規登録の画面で登録できるようになっていないため、viewをカスタマイズしていきます。
$ rails g devise:views user
でuserモデルに対応したviewファイルを作成できます 新規登録に関するviewファイルは生成時は下記のようになっているはずです。
app/views/users/registrations/new.html.slim
h2 | Sign usp = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render "devise/shared/error_messages", resource: resource .field = f.label :email br = f.email_field :email, autofocus: true, autocomplete: "email" .field = f.label :password - if @minimum_password_length em | ( = @minimum_password_length | characters minimum) br = f.password_field :password, autocomplete: "new-password" .field = f.label :password_confirmation br = f.password_field :password_confirmation, autocomplete: "new-password" .actions = f.submit "Sign up" = render "users/shared/links"
ここにusernameを入力するフォームを入れておきたいので、下記のようにフォームを追加してあげましょう。
h2 | Sign usp = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render "devise/shared/error_messages", resource: resource .field = f.label :email br = f.email_field :email, autofocus: true, autocomplete: "email" .field = f.label :username br = f.text_field :username, autocomplete: "username" .field = f.label :password - if @minimum_password_length em | ( = @minimum_password_length | characters minimum) br = f.password_field :password, autocomplete: "new-password" .field = f.label :password_confirmation br = f.password_field :password_confirmation, autocomplete: "new-password" .actions = f.submit "Sign up" = render "users/shared/links"
このようになりました。
この段階で画面にアクセスしてみると、表示はされるのですが、実際に値を送信して登録することはできません。
値を渡してあげるためにはcontroller側にも少し手を加えてあげる必要があります。操作するのはusers/registrations_controller.rbで、具体的には下記のようになっています。
# frozen_string_literal: true class Users::RegistrationsController < Devise::RegistrationsController # before_action :configure_sign_up_params, only: [:create] # before_action :configure_account_update_params, only: [:update] # GET /resource/sign_up # def new # super # end # POST /resource # def create # super # end # GET /resource/edit # def edit # super # end # PUT /resource # def update # super # end # DELETE /resource # def destroy # super # end # GET /resource/cancel # Forces the session data which is usually expired after sign # in to be expired now. This is useful if the user wants to # cancel oauth signing in/up in the middle of the process, # removing all OAuth session data. # def cancel # super # end # protected # If you have extra params to permit, append them to the sanitizer. # def configure_sign_up_params # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) # end # If you have extra params to permit, append them to the sanitizer. # def configure_account_update_params # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) # end # The path used after sign up. # def after_sign_up_path_for(resource) # super(resource) # end # The path used after sign up for inactive accounts. # def after_inactive_sign_up_path_for(resource) # super(resource) # end end
このファイルの中の
before_action :configure_sign_up_params, only: [:create]
と
def configure_sign_up_params devise_parameter_sanitizer.permit(:account_update, keys:[:attribute]) end
のコメントアウトを外しておきます。
nameカラムに値を入れたいので、:attribute
となっている部分を:username
に変えてあげます。これで、usernameを受け取り、登録することができるようになります。
ここまでで一旦動作確認をしておきます。
usernameとpasswordでloginできるようにする
新規登録が無事できるようになったので、登録したusernameでログインできるようにしていきましょう。
ログインに対応したviewファイルはapp/views/users/sessions/new.html.slim
になります。
内容はデフォルトだと
h2 | Log in = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| .field = f.label :email br = f.email_field :email, autofocus: true, autocomplete: "email" .field = f.label :password br = f.password_field :password, autocomplete: "current-password" - if devise_mapping.rememberable? .field = f.check_box :remember_me = f.label :remember_me .actions = f.submit "Log in" = render "users/shared/links"
このようになっています。 このemailの部分をusernameに変えてあげましょう。
h2 | Log in = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| .field = f.label :username br = f.email_field :username, autocomplete: "username" .field = f.label :password br = f.password_field :password, autocomplete: "current-password" - if devise_mapping.rememberable? .field = f.check_box :remember_me = f.label :remember_me .actions = f.submit "Log in" = render "users/shared/links"
このように、usernameとpasswordで登録するフォームに変更しました。
では実際にアクセスして、試してみます。
このようにusernameとpasswordでログインできました!
validetableを使用している場合の対処について
これで無事終了と行きたいところなのですが、emailをキーにしていない以上、データの更新や変更にemailを必須とするのはナンセンスなので、modelに幾つかメソッドを追記してバリデーションを無効化させます。
def email_required? false end def email_changed? false end
この2つのメソッドを追加します。Userモデルにdeviseを入れているので、 追記する場所はapp/models/user.rbになります。
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :email, uniqueness: true validates :username, uniqueness: true def email_required? false end def email_changed? false end end
これでusernameを変更したい場合にemailを必須としないようにできました!
最後に
実際のアプリやサービスではemailを使って認証することが多いと思いますが、簡単なアプリや社内で発行されたIDをキーにして認証を行いたい場合など、emailアドレス以外をキーにしてdeviseの認証を利用することで、認証ロジックをdeviseに任せることができるのはとても良いですね。