LoginSignup
9
8

More than 3 years have passed since last update.

devise_token_authで、Email以外でも認証を可能にする方法

Last updated at Posted at 2019-10-23

deviseのデフォルトでの認証はメールアドレスのみです。

今回は、「メールアドレス」または「電話番号」、どちらでも認証を行えるよう実装したいと思います。

Userモデルに認証のためのキーを追加する


class User < ActiveRecord::Base
  extend Devise::Models
  devise :database_authenticatable, :registerable,
         :rememberable, :trackable, :validatable,
         authentication_keys: [:login]
  include DeviseTokenAuth::Concerns::User

  # ...

end

authentication_keys: [:login]にて、 認証のためのキーを:emailから、:loginに変更します。

クライアント側からは、loginキーに「電話番号」または「メールアドレス」を入れて送ります。

Sessionsコントローラーを変更する

DeviseTokenAuth::SessionsControllerのcreateアクションをオーバーライドしてます。


class Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController

  def create
    field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
    @resource = nil

    if field
      q_value = get_case_insensitive_field_from_resource_params(field)

      @resource = find_resource(:email, q_value) if q_value.match?(/@/)
      @resource = find_resource(:phone_number, q_value) if q_value.match?(/\A\d{10}$|^\d{11}\z/)
    end

    if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
      valid_password = @resource.valid_password?(resource_params[:password])
      if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
        return render_create_error_bad_credentials
      end
      @token = @resource.create_token
      @resource.save

      sign_in(:user, @resource, store: false, bypass: false)

      yield @resource if block_given?

      render_create_success
    elsif @resource && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
      if @resource.respond_to?(:locked_at) && @resource.locked_at
        render_create_error_account_locked
      else
        render_create_error_not_confirmed
      end
    else
      render_create_error_bad_credentials
    end
  end

  #...

  private

  def resource_params
    params.permit(:password, :login)
  end

end

理想はsuperで呼び出して処理したいんですが、今回は無理でした。

なのでcreateメソッドを作り、そこに親のメソッドの処理を貼り付けて、必要な処理を追加という形にしてます。

主要なところだけ解説します。

ストロングパラメータでの受け取り


def resource_params
  params.permit(:password, :login)
end

見ての通りです。deviseのストロングパラメータをオーバーライドしましょう。

積集合を取る


field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first

resource_params.keys.map(&:to_sym)は、送られてきたパラメータのkeyをチェックし、配列にします。たとえば、値は[:password, :login]とかになります。

resource_class.authentication_keysは、先程設定した認証のためのkeyで、つまり[:login]です。

&でこの2つの積集合を取り、([:login]となります。)firstメソッドで、その中身を取っています。

つまり field => :loginです。

fieldがあればあとはDBまで潜りに行くだけ


    if field
      q_value = get_case_insensitive_field_from_resource_params(field)

      @resource = find_resource(:email, q_value) if q_value.match?(/@/)
      @resource = find_resource(:phone_number, q_value) if q_value.match?(/\A\d{10}$|^\d{11}\z/)
    end

get_case_insensitive_field_from_resource_params(field)は、resource_params[login]にスペースがあったら削除してくれるメソッドです。:emailキーの場合は、downcase!してくれますが、今回は:loginキーにしているので関係ないです。

あとはfind_resourceメソッドで入力された情報に合うユーザーがいるか探しにいきましょう。(クエリの発行)

ヒットしたら@resoucerにそのユーザーを返します。

その後の処理は、もともとの処理と変更ありませんので、解説はしません。

これで、「メールアドレス」または「電話番号」での認証について、成功です。

<補足>

現状だと、メールアドレスが認証のためのユニークなキー(uid)となっておりNULLを受け付けません。

電話番号をuidにしたい場合、以下のような対応策があります。

user.rb

class User < ActiveRecord::Base

  # ...

  before_validation :set_uid_from_phone_number

  def set_uid_from_phone_number
    self.uid = phone_number
  end

  # ...

end
sessions_controller.rb

  #オーバーライドで、providerをデフォルト設定である"email"から変更
  def provider
    "phone_number"
  end

参考

(公式ドキュメント)
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8