From 028ceac66e710eebb6a700df90e694a543f55009 Mon Sep 17 00:00:00 2001 From: EfeDurmaz16 Date: Fri, 15 May 2026 22:43:31 +0300 Subject: [PATCH] fix(server): vary paid middleware responses --- lib/mpp/server/decorator.rb | 1 + lib/mpp/server/middleware.rb | 16 ++++++++++++++++ test/mpp/test_middleware.rb | 11 +++++++++++ 3 files changed, 28 insertions(+) diff --git a/lib/mpp/server/decorator.rb b/lib/mpp/server/decorator.rb index c31ee24..bb42213 100644 --- a/lib/mpp/server/decorator.rb +++ b/lib/mpp/server/decorator.rb @@ -20,6 +20,7 @@ def make_challenge_response(challenge, realm) "Cache-Control" => "no-store", "Content-Type" => "application/problem+json" } + Mpp::Server::Middleware.mark_authorization_bound_response(headers) { "_mpp_challenge" => true, "status" => 402, diff --git a/lib/mpp/server/middleware.rb b/lib/mpp/server/middleware.rb index b5be6e0..cc0eb15 100644 --- a/lib/mpp/server/middleware.rb +++ b/lib/mpp/server/middleware.rb @@ -43,9 +43,25 @@ def call(env) _credential, receipt = result headers["Payment-Receipt"] = receipt.to_payment_receipt + self.class.mark_authorization_bound_response(headers) [status, headers, body] end + + sig { params(headers: T::Hash[T.untyped, T.untyped]).void } + def self.mark_authorization_bound_response(headers) + headers["Cache-Control"] = "no-store" + + vary_values = headers["Vary"].to_s.split(",").map do |value| + value.strip.downcase + end + return if vary_values.include?("*") || vary_values.include?("authorization") + + headers["Vary"] = [headers["Vary"], "Authorization"] + .compact + .reject(&:empty?) + .join(", ") + end end end end diff --git a/test/mpp/test_middleware.rb b/test/mpp/test_middleware.rb index d48aa25..c0b8a26 100644 --- a/test/mpp/test_middleware.rb +++ b/test/mpp/test_middleware.rb @@ -31,6 +31,8 @@ def test_returns_402_when_charge_requested_without_auth assert_equal 402, status assert headers.key?("WWW-Authenticate") assert_equal "application/problem+json", headers["Content-Type"] + assert_equal "no-store", headers["Cache-Control"] + assert_vary_authorization headers end def test_attaches_receipt_on_successful_payment @@ -58,6 +60,8 @@ def test_attaches_receipt_on_successful_payment assert_equal 200, status assert headers.key?("Payment-Receipt") + assert_equal "no-store", headers["Cache-Control"] + assert_vary_authorization headers assert_equal ["OK"], body end @@ -70,6 +74,13 @@ def minimal_env } end + def assert_vary_authorization(headers) + vary_fields = headers.fetch("Vary", "").split(",").map do |field| + field.strip.downcase + end + assert_includes vary_fields, "authorization" + end + def mock_handler verify_fn = lambda { |credential, _request| Mpp::Receipt.success("ref-#{credential.challenge.id[0..7]}")