【sign_up編③】registrations_controllerの処理の解説

rails

本記事では、devise_token_authのregistration_controllerのcreateアクションについて隅々まで調査した記事になります(自分の理解用として)。

本記事では、createアクションのbuild_resource以降の処理について深掘りしていこうと思います!

beforeアクションや、build_resourceに関する調査は下記記事で行なっているので、興味ある方はみてみてください!

【sign_up編①】registrations_controllerのvalidate_sign_up_paramsの解説

【sign_up編②】registrations_controllerのbuild_resourceの解説

ソースコード

本記事では、devise_token_authのregistrations_controllerのcreateアクションの中のbuild_resource以降(13行目以降)の説明をしていきます。

コードの解読をしていく

それでは早速解読をしていきます。

unless @resource.present?(13行目)

unless @resource.present?
  raise DeviseTokenAuth::Errors::NoResourceDefinedError,
        "#{self.class.name} #build_resource does not define @resource,"\
        ' execution stopped.'
end

まずはこちら、@resourceインスタンスが存在しない場合はエラーを返すよという内容になります。

@resourceについては【sign_up編②】で説明しており、クライアントから受け取った新規登録のユーザー情報が入ったインスタンスが生成されているはずです。

ちなみに、発生させるエラーの内容は、libディレクトリの下記から参照できます。

# (1) 3行目
module DeviseTokenAuth
  module Errors
    class NoResourceDefinedError < StandardError; end
    class InvalidModel < StandardError; end
  end
end

StandardErrorはrubyの標準ライブラリで提供されているものです。

@resourceが存在しない場合、3行目以降のエラーを返し、独自のエラーとして発生させることができるという処理になります。

@redirect_urlの定義(20行目)

@redirect_url = params.fetch(
  :confirm_success_url,
  DeviseTokenAuth.default_confirm_success_url
)

fetch関数は、hash.fetchとハッシュに対して用いられるメソッドで、hash.fetch(:key, except)とした場合、ハッシュに:keyで指定したキーが含まれている場合、その値を、なければexceptを出力する処理になります。

ということで、paramsに:confirm_success_urlが含まれている場合はそれを@redirect_urlに、なければ、DeviseTokenAuth.default_confirm_success_urlが適用されるということになります。

ちなみにDeviseTokenAuth.default_confirm_success_urlは、アプリのconfig/initializers/devise_token_auth.rbに記載するで指定することができます。

config.default_confirm_success_url = "https://#{ENV.fetch('VUE_APP_HOST', nil)}
# 環境変数のVUE_APP_HOSTがredirect_urlとして指定される。

if confirmable_enabled? && !@redirect_url(26行目)

if confirmable_enabled? && !@redirect_url
  return render_create_error_missing_confirm_success_url
end

このコードは、confirmable_enabledがtureかつ、@redirect_urlが存在しない場合にエラーを出す処理になります。

confirmable_enabledを追ってみます。application_controllerにありました。

# (1) 66行目
def confirmable_enabled?
  resource_class.devise_modules.include?(:confirmable)
end

devise_modulesですが、それっぽい記述が下記にありました。

# (1-1) 116行目
def devise_modules_hook!
  yield
end

これは、modelsに記載されており、yieldしていることから、railsのapp/models/user.rbをインポートしているものと考えられます。よって、userモデルにデフォルトで書かれている

# (1-1-1)
devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :validatable, :lockable, :confirmable #←追加

ここに:confirmableを追加することで(1)がtrueになり、if文のconfirmable_enabledをtrueにするのではないかと思います。

ここの:confirmableは、sign_up時にメール認証するかどうかを設定するシンボルであり、最初に戻ると、

if confirmable_enabled? && !@redirect_url
  return render_create_error_missing_confirm_success_url
end

メール認証する場合かつredirect_urlが未定義の場合にエラーを返すということになります。

メール認証で送るurlがredirect_urlになりますので、redirect_urlがなければsign_upの確認の仕様がないですよね。そこでしっかりエラーを返しています。

return render_create_error_redirect_url_not_allowed(31行目)

return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?(@redirect_url)

この処理では、redirect_urlがブラックリストの場合はエラーになる処理になります。

# (1) 19行目
def blacklisted_redirect_url?(redirect_url)
  DeviseTokenAuth.redirect_whitelist && !DeviseTokenAuth::Url.whitelisted?(redirect_url)
end

redirect_whitelistはconfigファイルで定義します。

config.devise_token_auth.redirect_whitelist = ['https://example.com', 'https://another-example.com']

このように設定することで、許可されたurlのみがredirect_urlとして設定できます。設定しなければ全てのurlが許可されます。今回は、DeviseTokenAuth.redirect_whitelistに値がある(設定されている場合)ときにtrueになります。

!DeviseTokenAuth::Url.whitelisted?(redirect_url)の部分ですが、これは、設定したwhitelistにredirect_urlがない場合trueになります。

よって、whitelist以外のredirect_urlを設定している場合にエラーを返す処理となります。

callback_nameの処理(34行目)

callback_name = defined?(ActiveRecord) && resource_class < ActiveRecord::Base ? :commit : :create
      resource_class.set_callback(callback_name, :after, :send_on_create_confirmation_instructions)
      resource_class.skip_callback(callback_name, :after, :send_on_create_confirmation_instructions)

※ここの処理は現在調査中です

if @resource.respond_to? :skip_confirmation_notification!(38行目)

if @resource.respond_to? :skip_confirmation_notification!
   # Fix duplicate e-mails by disabling Devise confirmation e-mail
   @resource.skip_confirmation_notification!
end

ここでは、@resourceオブジェクトがskip_confirmation_notificationメソッドを持っているかを確認します。skip_confirmation_notificationは、deviseのmodelに定義があります。

# (1) 61行目 
def initialize(*args, &block)
  @bypass_confirmation_postpone = false
  @skip_reconfirmation_in_callback = false
  @reconfirmation_required = false
  @skip_confirmation_notification = false
  @raw_confirmation_token = nil
  super
end

# (1-1) 161行目
def skip_confirmation_notification!
  @skip_confirmation_notification = true
end

161行目にありました。skip_confirmation_notification!が@resourceのメソッドに含まれている場合は、trueとなり、@resource.skip_confirmation_notification!が適用され、メール認証をする必要がないという内容です。

@user.saveする前に@user.skip_confirmation_notification!とするとメール認証がスキップされるようです。

if @resource.save(43行目)

if @resource.save
  # (1)
  yield @resource if block_given?
  # (2) 
  unless @resource.confirmed?
    # user will require email authentication
    @resource.send_confirmation_instructions({
      client_config: params[:config_name],
      redirect_url: @redirect_url
    })
  end
  # (3)
  if active_for_authentication?
    # email auth has been bypassed, authenticate user
    @token = @resource.create_token
    @resource.save!
    update_auth_header
  end
  # (4)
  render_create_success
else
  # (5)
  clean_up_passwords @resource
  render_create_error
end

これは、@resourceが保存されたかどうかで処理を分岐しています。

(2)のunless @resource.confirmed?

上記は、メール認証を許可した場合、ユーザーに確認通知メールを送付する処理になります。

(3)if active_for_authentication?

if active_for_authentication?を追うと、

# (1)  201行目
def active_for_authentication?
  !@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?
end

であり、こちらもuserモデルに:active_for_authentication?関数を渡した場合に実行されます。

また、active_for_authenticationはユーザーのログイン制限に使われるみたいです。

ログイン制限をかけていなければ、user情報は登録されheader情報も更新されます。

(4)成功レスポンスを返す。

(5)失敗時の処理

clean_up_passwordでパスワード関連の情報をクリアし、ユーザー作成が失敗したことを示すエラーレスポンスを返します。

コメント

タイトルとURLをコピーしました