みるべあのエンジニア日記

勉強したことについてTwitterとガチブログの中間くらいのスタンスで書く日記

http_basic_authenticate_withがNoMethodErrorになった

前提

やりたいこと

ステージング環境でBasic認証を導入する。

f:id:milkybear814:20200614134259p:plain

修正前のコードとエラーの内容

class ApplicationController < ActionController::Base
  before_action :basic_auth_in_staging

  private

  def :basic_auth_in_staging
    http_basic_authenticate_with name: ENV.fetch('BASIC_AUTH_NAME'), password: ENV.fetch('BASIC_AUTH_PASS') if Rails.env.staging?
  end
end

としてステージング環境にアクセスしたら、

NoMethodError (undefined method `http_basic_authenticate_with' 以下略

ほぇ!?

Rails本体のコードを調査

Railsのソースコードを覗いてみると、

module ClassMethods
  def http_basic_authenticate_with(name:, password:, realm: nil, **options)
    before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
  end
end

def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
  authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
    ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
      ActiveSupport::SecurityUtils.secure_compare(given_password, password)
  end
end

は〜ん、before_actionBasic認証の主要メソッドであるhttp_basic_authenticate_or_request_withを呼び出しているだけのメソッドなのか。
before_actionbefore_actionを使ったメソッドを使ってしまったのが良くなかったのでしょうか。

修正後のコード

以下のように修正したら意図したとおりBasic認証が動作しました!

class ApplicationController < ActionController::Base
  before_action :basic_auth_in_staging

  private

  def
    return unless Rails.env.staging?

    authenticate_or_request_with_http_basic do |username, password|
      username == ENV.fetch('BASIC_AUTH_NAME') && password == ENV.fetch('BASIC_AUTH_PASS')
    end
  end 
end