devise_token_authで、Email以外でも認証を可能にする方法
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にしたい場合、以下のような対応策があります。
class User < ActiveRecord::Base
# ...
before_validation :set_uid_from_phone_number
def set_uid_from_phone_number
self.uid = phone_number
end
# ...
end
#オーバーライドで、providerをデフォルト設定である"email"から変更
def provider
"phone_number"
end
参考
Author And Source
この問題について(devise_token_authで、Email以外でも認証を可能にする方法), 我々は、より多くの情報をここで見つけました https://qiita.com/wonder_meet/items/9b251fad137a125052f2著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .