HTTP認証メソッドにContent-Type指定機能を追加
この変更により、HTTP認証の401レスポンスにContent-Typeを指定できるようになりました。これにより、JSONレスポンスを返すAPIなど、アプリケーションの要件に応じた認証エラーレスポンスの形式を統一できます。
背景
HTTP認証の401レスポンスは、従来はContent-Typeを制御する手段がありませんでした。authenticate_or_request_with_http_basic などのメソッドでカスタムメッセージは指定できましたが、レスポンスのContent-Typeは固定されていました。特にJSONベースのAPIでは、認証エラーもJSON形式で返したいという要件がありましたが、この実現には独自の実装が必要でした。
また、http_basic_authenticate_with クラスメソッドでは、既に内部的に存在していた message パラメータが公開されていませんでした。エラーメッセージをカスタマイズするには、より低レベルのメソッドを直接使用する必要がありました。
技術的な変更
HTTP認証の各メソッドに content_type パラメータが追加され、メソッドチェーン全体で伝播されるようになりました。
http_basic_authenticate_withの拡張
変更前:
def http_basic_authenticate_with(name:, password:, realm: nil, **options)
raise ArgumentError, "Expected name: to be a String, got #{name.class}" unless name.is_a?(String)
raise ArgumentError, "Expected password: to be a String, got #{password.class}" unless password.is_a?(String)
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
end
変更後:
def http_basic_authenticate_with(name:, password:, realm: nil, message: nil, content_type: nil, **options)
raise ArgumentError, "Expected name: to be a String, got #{name.class}" unless name.is_a?(String)
raise ArgumentError, "Expected password: to be a String, got #{password.class}" unless password.is_a?(String)
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm, message: message, content_type: content_type }
end
この変更により、クラスレベルの http_basic_authenticate_with で message と content_type の両方を指定できるようになりました。
メソッドチェーン全体への伝播
content_type パラメータは以下のメソッドチェーンを通じて最終的な認証要求メソッドまで伝播されます:
http_basic_authenticate_or_request_withauthenticate_or_request_with_http_basicrequest_http_basic_authenticationHttpAuthentication::Basic.authentication_request
各メソッドのシグネチャが統一的に拡張されています:
def authenticate_or_request_with_http_basic(realm = nil, message = nil, content_type = nil, &login_procedure)
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message, content_type)
end
def request_http_basic_authentication(realm = "Application", message = nil, content_type = nil)
HttpAuthentication::Basic.authentication_request(self, realm, message, content_type)
end
使用例
JSON形式の認証エラーレスポンスを返す場合の設定:
http_basic_authenticate_with(
name: "admin", password: "secret",
message: '{"error":"Access denied"}',
content_type: "application/json"
)
message と content_type のどちらも省略可能で、既存の動作は変更されません。
設計判断
オプショナルパラメータとしての追加 が採用されました。
この変更では、既存のメソッドシグネチャに対して後方互換性を保つため、content_type をオプショナルパラメータとして追加しています。デフォルト値は nil で、指定されない場合は従来の動作が維持されます。Digest認証とToken認証のメソッドにも同様のパターンで content_type が追加されており、HTTP認証全体で一貫したインターフェースになっています。
また、既に存在していた message パラメータを http_basic_authenticate_with レベルに公開することで、低レベルメソッドを直接呼び出すことなくカスタマイズが可能になりました。テストコードでは content_type: "application/json" を指定した場合に、レスポンスの media_type が正しく設定されることが検証されています。
まとめ
本PRは、HTTP認証の401レスポンスにContent-Type制御機能を追加し、既存の message パラメータを公開した変更です。メソッドチェーン全体に統一的にパラメータを伝播させることで、Basic、Digest、Token認証すべてで一貫したカスタマイズが可能になり、特にJSONベースのAPIにおける認証エラーレスポンスの形式統一を実現しています。