Low watch ✔︎ verified CVSS 6.5 chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in Payments
DescriptionMissing authorization in Payments
ComponentPayments
Sub-componentWebAuthn
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety — factors set by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed
Reviewer notes: Core mechanism (no RP-ID-vs-origin check before store; fix adds ValidateDomainAndRelyingPartyID) is confirmed by diff. Overstates reachability (plain script) and ignores the CredentialStoreAPIs early-return at cc:158-162.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker477726837
Fix commite3ba22448968 (chromium/src) +140/-12
CWECWE-862
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedAutodidact
Reported2026-01-22 · bounty $1,000 (per vendor advisory)
Fix landed2026-06-02 (public git)
Disclosed2026-09-29
Patch gap119 days — fix public in source before the advisory

Background

Secure Payment Confirmation (SPC)
a Web Payments feature that lets a site enroll and later assert WebAuthn-style payment credentials, implemented browser-side by payments::SecurePaymentConfirmationService.
RP ID (Relying Party ID)
the domain string a credential is scoped to; it must belong to (or be a registrable suffix of) the calling origin, so one origin cannot create credentials for another.
`WebAuthRequestSecurityChecker`
the browser-side component that validates that a renderer-supplied RP ID is permitted for the caller’s origin (via ValidateDomainAndRelyingPartyID).

Root Cause Analysis

Re-analyzed on 2026-10-03 against the actual fix commit e3ba22448968. An earlier analysis was withdrawn because it had been written against a commit that was not the security fix (f8e6432922c9).

SecurePaymentConfirmationService::StorePaymentCredential is a Mojo method callable from the renderer with a renderer-chosen rp_id (secure_payment_confirmation_service.cc:139-143). In the pre-fix code the only entry gating was web_data_service_ presence, content::IsFrameAllowedToUseSecurePaymentConfirmation(&render_frame_host()), and non-empty credential_id/rp_id/user_id (secure_payment_confirmation_service.cc:144-151); nothing tied rp_id to the caller’s origin. If features::kSecurePaymentConfirmationUseCredentialStoreAPIs is enabled, the method returns SUCCESS without storing anything (secure_payment_confirmation_service.cc:158-162, “the credential information will already have been stored during creation”); only with that feature disabled does it store the credential directly, constructing SecurePaymentConfirmationCredential(credential_id, rp_id, user_id) and calling web_data_service_->AddSecurePaymentConfirmationCredential(...) (secure_payment_confirmation_service.cc:164-173). The invariant that a credential’s RP ID must be authorized for the calling origin was therefore never enforced on the store path, allowing a confused-deputy / cross-origin credential write.

The fix splits the flow: StorePaymentCredential now invokes render_frame_host().GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(origin(), rp_id, ...kMakePaymentCredential..., BindOnce(&...::ContinueStorePaymentCredentialAfterRpIdCheck,...)) (diff, added lines) and the new continuation rejects any rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS by reporting a bad message and calling ResetAndDeleteThis(), so the credential is not stored. The check now runs before the feature branch, so a mismatched rp_id is rejected regardless of the feature state. A guard remote_validation_ || ... is added to the entry check (diff at pre-fix line 144) to reject re-entrant calls while a validation is in flight, and a weak_this check protects against the validator invoking its callback synchronously and deleting this.

Key insight
The store path trusted a renderer-supplied RP ID without ever checking it against the caller’s origin; the fix inserts a mandatory ValidateDomainAndRelyingPartyID authorization check and refuses to persist (and kills the pipe) when it fails.

Attack Path

  1. Compromised renderer A compromised renderer in an allowed frame obtains the mojom::SecurePaymentConfirmationService interface. (inferred: the material does not show ordinary script reaching StorePaymentCredential with an arbitrary rp_id, and the fix treats a mismatch as a Mojo bad message, "Invalid RP ID in StorePaymentCredential", which implies a compromised-renderer threat model.)
  2. Forge the RP ID It calls StorePaymentCredential supplying a rp_id for a domain it does not control, with a non-empty credential_id and user_id.
  3. Unauthorized store Pre-fix, all gating checks pass and, when kSecurePaymentConfirmationUseCredentialStoreAPIs is disabled (otherwise the call returns at :158-162 without storing), AddSecurePaymentConfirmationCredential persists a credential scoped to the victim RP ID (secure_payment_confirmation_service.cc:164-173).
  4. Confused deputy A credential attributable to another relying party now exists in the browser’s store, i.e. cross-origin credential corruption. (inferred: downstream payment-authentication consequences are not shown in the material.)

Impact Assessment

A compromised renderer could, with kSecurePaymentConfirmationUseCredentialStoreAPIs disabled, cause the browser to store an SPC payment credential under a relying-party ID not authorized for its origin, a missing-authorization / confused-deputy write into the browser-side credential store. The advisory rates this low severity and reports no in-the-wild exploitation. Precondition is a context already permitted to use SPC (IsFrameAllowedToUseSecurePaymentConfirmation).

Changed Functions

FunctionChangeNotes
if
components/payments/content/secure_payment_confirmation_service.cc
modified
SecurePaymentConfirmationServiceTest
components/payments/content/secure_payment_confirmation_service_unittest.cc
modified

Files Changed

  • components/payments/content/secure_payment_confirmation_service.cc
  • components/payments/content/secure_payment_confirmation_service.h
  • components/payments/content/secure_payment_confirmation_service_unittest.cc

Audit Directions

  • Renderer-supplied identifiers
    Flag any browser-side handler that persists or acts on a renderer-provided RP ID, origin, or domain without validating it against origin() / the caller’s security context.
  • Split-callback authorization
    When a check is made asynchronous via a callback (e.g. ValidateDomainAndRelyingPartyID), verify the callback both enforces the failure path and guards against synchronous invocation deleting this (the weak_this / WeakPtr pattern).
  • DocumentService lifetime
    For content::DocumentService subclasses, confirm failure paths call ResetAndDeleteThis() and that in-flight operations cannot be re-entered (the remote_validation_ guard).
diff --git a/components/payments/content/secure_payment_confirmation_service.cc b/components/payments/content/secure_payment_confirmation_service.cc
index 398922d..01a45f0 100644
--- a/components/payments/content/secure_payment_confirmation_service.cc
+++ b/components/payments/content/secure_payment_confirmation_service.cc
@@ -22,8 +22,10 @@
 #include "components/webauthn/core/browser/internal_authenticator.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/secure_payment_confirmation_utils.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/webauth_request_security_checker.h"
 #include "content/public/common/content_features.h"
 #include "crypto/random.h"
 #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
@@ -141,7 +143,7 @@
     const std::string& rp_id,
     const std::vector<uint8_t>& user_id,
     StorePaymentCredentialCallback callback) {
-  if (!web_data_service_ ||
+  if (remote_validation_ || !web_data_service_ ||
       !content::IsFrameAllowedToUseSecurePaymentConfirmation(
           &render_frame_host()) ||
       credential_id.empty() || rp_id.empty() || user_id.empty()) {
@@ -150,6 +152,49 @@
     return;
   }
 
+  base::WeakPtr<SecurePaymentConfirmationService> weak_this =
+      weak_ptr_factory_.GetWeakPtr();
+
+  auto remote_validation =
+      render_frame_host()
+          .GetWebAuthRequestSecurityChecker()
+          ->ValidateDomainAndRelyingPartyID(
+              origin(), rp_id,
+              content::WebAuthRequestSecurityChecker::RequestType::
+                  kMakePaymentCredential,
+              /*remote_desktop_client_override_origin=*/std::nullopt,
+              base::BindOnce(&SecurePaymentConfirmationService::
+                                 ContinueStorePaymentCredentialAfterRpIdCheck,
+                             weak_this, mojo::GetBadMessageCallback(),
+                             credential_id, rp_id, user_id,
+                             std::move(callback)));
+
+  // ValidateDomainAndRelyingPartyID might run the callback synchronously.
+  // If validation fails, the callback will call `ResetAndDeleteThis()` and
+  // delete `this`. We must check `weak_this` to avoid a UAF when storing the
+  // returned validation handle.
+  if (weak_this) {
+    remote_validation_ = std::move(remote_validation);
+  }
+}
+
+void SecurePaymentConfirmationService::
+    ContinueStorePaymentCredentialAfterRpIdCheck(
+        mojo::ReportBadMessageCallback bad_message_callback,
+        std::vector<uint8_t> credential_id,
+        const std::string& rp_id,
+        std::vector<uint8_t> user_id,
+        StorePaymentCredentialCallback callback,
+        blink::mojom::AuthenticatorStatus rp_id_validation_result) {
+  remote_validation_.reset();
+  // If the RP ID check failed, we cannot store the credential.
+  if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
+    std::move(bad_message_callback)
+        .Run("Invalid RP ID in StorePaymentCredential");
+    ResetAndDeleteThis();
+    return;
+  }
+
   RecordFirstSystemPromptResult(
       SecurePaymentConfirmationEnrollSystemPromptResult::kAccepted);
 
@@ -162,8 +207,8 @@
   }
 
   web_data_service_->AddSecurePaymentConfirmationCredential(
-      std::make_unique<SecurePaymentConfirmationCredential>(credential_id,
-                                                            rp_id, user_id),
+      std::make_unique<SecurePaymentConfirmationCredential>(
+          std::move(credential_id), rp_id, std::move(user_id)),
       base::BindOnce([](WebDataServiceBase::Handle h,
                         std::unique_ptr<WDTypedResult> result) {
         return result && static_cast<WDResult<bool>*>(result.get())->GetValue()
diff --git a/components/payments/content/secure_payment_confirmation_service.h b/components/payments/content/secure_payment_confirmation_service.h
index 6734b41c..5c2d2301 100644
--- a/components/payments/content/secure_payment_confirmation_service.h
+++ b/components/payments/content/secure_payment_confirmation_service.h
@@ -15,10 +15,12 @@
 #include "base/memory/weak_ptr.h"
 #include "components/payments/content/browser_binding/passkey_browser_binder.h"
 #include "components/payments/core/secure_payment_confirmation_metrics.h"
+#include "components/webauthn/core/browser/remote_validation.h"
 #include "components/webdata/common/web_data_service_base.h"
 #include "components/webdata/common/web_data_service_consumer.h"
 #include "content/public/browser/document_service.h"
 #include "content/public/browser/global_routing_id.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/mojom/payments/secure_payment_confirmation_service.mojom.h"
 
@@ -86,6 +88,16 @@
       scoped_refptr<BrowserBoundKeyStore> browser_bound_key_store);
 
  private:
+  // Called after the RpId check has been completed, to continue the process of
+  // storing the payment credential.
+  void ContinueStorePaymentCredentialAfterRpIdCheck(
+      mojo::ReportBadMessageCallback bad_message_callback,
+      std::vector<uint8_t> credential_id,
+      const std::string& rp_id,
+      std::vector<uint8_t> user_id,
+      StorePaymentCredentialCallback callback,
+      blink::mojom::AuthenticatorStatus rp_id_validation_result);
+
   // MakeCredentialCallback:
   void OnAuthenticatorMakeCredential(
       SecurePaymentConfirmationService::MakePaymentCredentialCallback callback,
@@ -119,6 +131,7 @@
   std::unique_ptr<PasskeyBrowserBinder> passkey_browser_binder_;
   scoped_refptr<BrowserBoundKeyStore> test_browser_bound_key_store_;
   std::string browser_bound_key_store_keychain_access_group_;
+  std::unique_ptr<webauthn::RemoteValidation> remote_validation_;
 
   base::WeakPtrFactory<SecurePaymentConfirmationService> weak_ptr_factory_{
       this};
diff --git a/components/payments/content/secure_payment_confirmation_service_unittest.cc b/components/payments/content/secure_payment_confirmation_service_unittest.cc
index 0b72b5f..2e59ed7 100644
--- a/components/payments/content/secure_payment_confirmation_service_unittest.cc
+++ b/components/payments/content/secure_payment_confirmation_service_unittest.cc
@@ -23,8 +23,10 @@
 #include "components/webauthn/core/browser/mock_internal_authenticator.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_web_contents_factory.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
@@ -44,12 +46,6 @@
 
 namespace {
 
-struct SecurePaymentConfirmationServiceDeleter {
-  void operator()(SecurePaymentConfirmationService* spc_service) {
-    spc_service->ResetAndDeleteThis();
-  }
-};
-
 #if !BUILDFLAG(IS_IOS)
 static const int32_t kAlgorithmIdentifier = 1;
 static const int32_t kAnotherAlgorithmIdentifier = 2;
@@ -58,6 +54,12 @@
 
 #endif
 
+struct SecurePaymentConfirmationServiceDeleter {
+  void operator()(SecurePaymentConfirmationService* spc_service) {
+    spc_service->ResetAndDeleteThis();
+  }
+};
+
 }  // namespace
 
 // Base class for unittests testing SecurePaymentConfirmationService, which
@@ -73,15 +75,20 @@
  protected:
   void InitializeSecurePaymentConfirmationService(
       bool with_authenticator = true,
-      bool is_off_the_record = false) {
+      bool is_off_the_record = false,
+      std::string caller_url = "https://relying-party.example") {
     context_.set_is_off_the_record(is_off_the_record);
     web_contents_ = web_contents_factory_.CreateWebContents(&context_);
     CHECK(!mock_internal_authenticator_);
     CHECK(!spc_service_);
 
-    mojo::PendingRemote<mojom::SecurePaymentConfirmationService> remote;
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents_, GURL(caller_url));
+
     mojo::PendingReceiver<mojom::SecurePaymentConfirmationService> receiver =
-        remote.InitWithNewPipeAndPassReceiver();
+        spc_service_remote_.BindNewPipeAndPassReceiver();
+    // Retain the remote at class scope to ensure the service remains alive
+    // during RunLoop execution.
     spc_service_ = std::unique_ptr<SecurePaymentConfirmationService,
                                    SecurePaymentConfirmationServiceDeleter>(
         new SecurePaymentConfirmationService(
@@ -116,6 +123,9 @@
   base::MockCallback<mojom::SecurePaymentConfirmationService::
                          SecurePaymentConfirmationAvailabilityCallback>
       mock_secure_payment_confirmation_availability_callback_;
+  // Retain the remote at class scope to prevent `spc_service_` (which is a
+  // DocumentService) from self-destructing when a RunLoop executes.
+  mojo::Remote<mojom::SecurePaymentConfirmationService> spc_service_remote_;
 };
 
 class SecurePaymentConfirmationServiceTest
@@ -297,6 +307,66 @@
       /*expected_bucket_count=*/1);
 }
 
+TEST_F(SecurePaymentConfirmationServiceTest,
+       StorePaymentCredential_RpIdCheckFailed) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({::features::kSecurePaymentConfirmation}, {});
+
+  InitializeSecurePaymentConfirmationService(
+      /*with_authenticator=*/false,
+      /*is_off_the_record=*/false,
+      /*caller_url=*/"https://attacker.example");
+
+  const std::vector<uint8_t> credential_id = {0x01, 0x02, 0x03, 0x04};
+  const std::string rp_id = "relying-party.example";
+  const std::vector<uint8_t> user_id = {0x10, 0x11, 0x12, 0x13};
+  base::MockCallback<
+      mojom::SecurePaymentConfirmationService::StorePaymentCredentialCallback>
+      mock_store_payment_credential_callback;
+
+  mojo::test::BadMessageObserver bad_message_observer;
+
+  spc_service_remote_->StorePaymentCredential(
+      credential_id, rp_id, user_id,
+      mock_store_payment_credential_callback.Get());
+
+  spc_service_.release();
+
+  EXPECT_EQ("Invalid RP ID in StorePaymentCredential",
+            bad_message_observer.WaitForBadMessage());
+}
+
+TEST_F(SecurePaymentConfirmationServiceTest,
+       StorePaymentCredential_RpIdCheckSuccess) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      {::features::kSecurePaymentConfirmation,
+       features::kSecurePaymentConfirmationUseCredentialStoreAPIs},
+      {});
+
+  InitializeSecurePaymentConfirmationService();
+
+  const std::vector<uint8_t> credential_id = {0x01, 0x02, 0x03, 0x04};
+  const std::string rp_id = "relying-party.example";
+  const std::vector<uint8_t> user_id = {0x10, 0x11, 0x12, 0x13};
+
+  base::MockCallback<
+      mojom::SecurePaymentConfirmationService::StorePaymentCredentialCallback>
+      mock_store_payment_credential_callback;
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(mock_store_payment_credential_callback,
+              Run(mojom::PaymentCredentialStorageStatus::SUCCESS))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
+
+  spc_service_remote_->StorePaymentCredential(
+      credential_id, rp_id, user_id,
+      mock_store_payment_credential_callback.Get());
+
+  run_loop.Run();
+}
+
 #if !BUILDFLAG(IS_IOS)
 
 struct CredentialTestParams {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/payments/content/secure_payment_confirmation_service_unittest.cc b/components/payments/content/secure_payment_confirmation_service_unittest.cc
index 0b72b5f..2e59ed7 100644
--- a/components/payments/content/secure_payment_confirmation_service_unittest.cc
+++ b/components/payments/content/secure_payment_confirmation_service_unittest.cc
@@ -23,8 +23,10 @@
 #include "components/webauthn/core/browser/mock_internal_authenticator.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_web_contents_factory.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
@@ -44,12 +46,6 @@
 
 namespace {
 
-struct SecurePaymentConfirmationServiceDeleter {
-  void operator()(SecurePaymentConfirmationService* spc_service) {
-    spc_service->ResetAndDeleteThis();
-  }
-};
-
 #if !BUILDFLAG(IS_IOS)
 static const int32_t kAlgorithmIdentifier = 1;
 static const int32_t kAnotherAlgorithmIdentifier = 2;
@@ -58,6 +54,12 @@
 
 #endif
 
+struct SecurePaymentConfirmationServiceDeleter {
+  void operator()(SecurePaymentConfirmationService* spc_service) {
+    spc_service->ResetAndDeleteThis();
+  }
+};
+
 }  // namespace
 
 // Base class for unittests testing SecurePaymentConfirmationService, which
@@ -73,15 +75,20 @@
  protected:
   void InitializeSecurePaymentConfirmationService(
       bool with_authenticator = true,
-      bool is_off_the_record = false) {
+      bool is_off_the_record = false,
+      std::string caller_url = "https://relying-party.example") {
     context_.set_is_off_the_record(is_off_the_record);
     web_contents_ = web_contents_factory_.CreateWebContents(&context_);
     CHECK(!mock_internal_authenticator_);
     CHECK(!spc_service_);
 
-    mojo::PendingRemote<mojom::SecurePaymentConfirmationService> remote;
+    content::NavigationSimulator::NavigateAndCommitFromBrowser(
+        web_contents_, GURL(caller_url));
+
     mojo::PendingReceiver<mojom::SecurePaymentConfirmationService> receiver =
-        remote.InitWithNewPipeAndPassReceiver();
+        spc_service_remote_.BindNewPipeAndPassReceiver();
+    // Retain the remote at class scope to ensure the service remains alive
+    // during RunLoop execution.
     spc_service_ = std::unique_ptr<SecurePaymentConfirmationService,
                                    SecurePaymentConfirmationServiceDeleter>(
         new SecurePaymentConfirmationService(
@@ -116,6 +123,9 @@
   base::MockCallback<mojom::SecurePaymentConfirmationService::
                          SecurePaymentConfirmationAvailabilityCallback>
       mock_secure_payment_confirmation_availability_callback_;
+  // Retain the remote at class scope to prevent `spc_service_` (which is a
+  // DocumentService) from self-destructing when a RunLoop executes.
+  mojo::Remote<mojom::SecurePaymentConfirmationService> spc_service_remote_;
 };
 
 class SecurePaymentConfirmationServiceTest
@@ -297,6 +307,66 @@
       /*expected_bucket_count=*/1);
 }
 
+TEST_F(SecurePaymentConfirmationServiceTest,
+       StorePaymentCredential_RpIdCheckFailed) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({::features::kSecurePaymentConfirmation}, {});
+
+  InitializeSecurePaymentConfirmationService(
+      /*with_authenticator=*/false,
+      /*is_off_the_record=*/false,
+      /*caller_url=*/"https://attacker.example");
+
+  const std::vector<uint8_t> credential_id = {0x01, 0x02, 0x03, 0x04};
+  const std::string rp_id = "relying-party.example";
+  const std::vector<uint8_t> user_id = {0x10, 0x11, 0x12, 0x13};
+  base::MockCallback<
+      mojom::SecurePaymentConfirmationService::StorePaymentCredentialCallback>
+      mock_store_payment_credential_callback;
+
+  mojo::test::BadMessageObserver bad_message_observer;
+
+  spc_service_remote_->StorePaymentCredential(
+      credential_id, rp_id, user_id,
+      mock_store_payment_credential_callback.Get());
+
+  spc_service_.release();
+
+  EXPECT_EQ("Invalid RP ID in StorePaymentCredential",
+            bad_message_observer.WaitForBadMessage());
+}
+
+TEST_F(SecurePaymentConfirmationServiceTest,
+       StorePaymentCredential_RpIdCheckSuccess) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      {::features::kSecurePaymentConfirmation,
+       features::kSecurePaymentConfirmationUseCredentialStoreAPIs},
+      {});
+
+  InitializeSecurePaymentConfirmationService();
+
+  const std::vector<uint8_t> credential_id = {0x01, 0x02, 0x03, 0x04};
+  const std::string rp_id = "relying-party.example";
+  const std::vector<uint8_t> user_id = {0x10, 0x11, 0x12, 0x13};
+
+  base::MockCallback<
+      mojom::SecurePaymentConfirmationService::StorePaymentCredentialCallback>
+      mock_store_payment_credential_callback;
+
+  base::RunLoop run_loop;
+
+  EXPECT_CALL(mock_store_payment_credential_callback,
+              Run(mojom::PaymentCredentialStorageStatus::SUCCESS))
+      .WillOnce([&run_loop] { run_loop.Quit(); });
+
+  spc_service_remote_->StorePaymentCredential(
+      credential_id, rp_id, user_id,
+      mock_store_payment_credential_callback.Get());
+
+  run_loop.Run();
+}
+
 #if !BUILDFLAG(IS_IOS)
 
 struct CredentialTestParams {
Loading diff…