| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/webid/user_info_request.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "content/browser/webid/metrics.h" |
| #include "content/browser/webid/test/mock_api_permission_delegate.h" |
| #include "content/browser/webid/test/mock_idp_network_request_manager.h" |
| #include "content/browser/webid/test/mock_permission_delegate.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/test/test_render_frame_host.h" |
| #include "content/test/test_web_contents.h" |
| #include "net/http/http_status_code.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| using ApiPermissionStatus = |
| content::FederatedIdentityApiPermissionContextDelegate::PermissionStatus; |
| using FetchStatus = content::IdpNetworkRequestManager::FetchStatus; |
| using LoginState = content::IdentityRequestAccount::LoginState; |
| using ParseStatus = content::IdpNetworkRequestManager::ParseStatus; |
| using blink::mojom::RequestUserInfoStatus; |
| using ::testing::_; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| |
| namespace content::webid { |
| namespace { |
| |
| constexpr char kRpUrl[] = "https://rp.example"; |
| constexpr char kPersonalizedButtonFrameUrl[] = "https://idp.example/button"; |
| constexpr char kProviderUrl[] = "https://idp.example/fedcm.json"; |
| constexpr char kAccountsEndpoint[] = "https://idp.example/accounts"; |
| constexpr char kTokenEndpoint[] = "https://idp.example/token"; |
| constexpr char kLoginUrl[] = "https://idp.example/login"; |
| constexpr char kClientId[] = "client_id_123"; |
| |
| constexpr char kAccountEmailFormat[] = "%s@foo.com"; |
| constexpr char kAccountName[] = "The Liliputian"; |
| constexpr char kAccountGivenName[] = "Julius"; |
| constexpr char kAccountPicture[] = "https://image.com/yolo"; |
| constexpr char kAccountPhone[] = "(650) 243-3243"; |
| constexpr char kAccountUsername[] = "@julius"; |
| |
| struct AccountConfig { |
| std::string id; |
| std::optional<IdentityRequestAccount::LoginState> login_state; |
| bool was_granted_sharing_permission; |
| }; |
| |
| struct Config { |
| std::optional<bool> idp_signin_status; |
| std::vector<AccountConfig> accounts; |
| FetchStatus config_fetch_status; |
| FetchStatus accounts_fetch_status; |
| }; |
| |
| Config kValidConfig = { |
| /*idp_signin_status=*/true, |
| /*accounts=*/ |
| {{"account1", /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/true}}, |
| /*config_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK}, |
| /*accounts_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK}}; |
| |
| std::string GenerateEmailForUserId(const std::string& user_id) { |
| return base::StringPrintf(kAccountEmailFormat, user_id.c_str()); |
| } |
| |
| // Helper class for blocking till RequestUserInfoCallback is called. |
| class UserInfoCallbackHelper { |
| public: |
| UserInfoCallbackHelper() = default; |
| ~UserInfoCallbackHelper() = default; |
| |
| UserInfoCallbackHelper(const UserInfoCallbackHelper&) = delete; |
| UserInfoCallbackHelper& operator=(const UserInfoCallbackHelper&) = delete; |
| |
| // This can only be called once per lifetime of this object. |
| blink::mojom::FederatedAuthRequest::RequestUserInfoCallback callback() { |
| return base::BindOnce(&UserInfoCallbackHelper::Complete, |
| base::Unretained(this)); |
| } |
| |
| // Returns when callback() is called, which can be immediately if it has |
| // already been called. |
| void WaitForCallback() { |
| if (was_called_) { |
| return; |
| } |
| wait_for_callback_loop_.Run(); |
| } |
| |
| RequestUserInfoStatus user_info_status_; |
| std::optional<std::vector<blink::mojom::IdentityUserInfoPtr>> user_info_; |
| |
| private: |
| void Complete( |
| RequestUserInfoStatus user_info_status, |
| std::optional<std::vector<blink::mojom::IdentityUserInfoPtr>> user_info) { |
| CHECK(!was_called_); |
| user_info_status_ = user_info_status; |
| user_info_ = std::move(user_info); |
| was_called_ = true; |
| wait_for_callback_loop_.Quit(); |
| } |
| |
| bool was_called_{false}; |
| base::RunLoop wait_for_callback_loop_; |
| }; |
| |
| class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager { |
| public: |
| explicit TestIdpNetworkRequestManager(const Config& config) |
| : config_(config) {} |
| ~TestIdpNetworkRequestManager() override = default; |
| |
| void FetchWellKnown(const GURL& provider, |
| FetchWellKnownCallback callback) override { |
| has_fetched_well_known_ = true; |
| FetchStatus fetch_status = {ParseStatus::kSuccess, net::HTTP_OK}; |
| IdpNetworkRequestManager::WellKnown well_known; |
| std::set<GURL> well_known_urls = {GURL(kProviderUrl)}; |
| well_known.provider_urls = std::move(well_known_urls); |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), fetch_status, well_known)); |
| } |
| |
| void FetchConfig(const GURL& provider, |
| blink::mojom::RpMode rp_mode, |
| int idp_brand_icon_ideal_size, |
| int idp_brand_icon_minimum_size, |
| FetchConfigCallback callback) override { |
| has_fetched_config_ = true; |
| |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.accounts = GURL(kAccountsEndpoint); |
| endpoints.token = GURL(kTokenEndpoint); |
| |
| IdentityProviderMetadata idp_metadata; |
| idp_metadata.config_url = GURL(kProviderUrl); |
| idp_metadata.idp_login_url = GURL(kLoginUrl); |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), config_.config_fetch_status, |
| endpoints, idp_metadata)); |
| } |
| |
| void SendAccountsRequest(const url::Origin& idp_origin, |
| const GURL& accounts_url, |
| const std::string& client_id, |
| AccountsRequestCallback callback) override { |
| has_fetched_accounts_endpoint_ = true; |
| |
| std::vector<IdentityRequestAccountPtr> accounts; |
| for (const AccountConfig& account_config : config_.accounts) { |
| accounts.emplace_back(base::MakeRefCounted<IdentityRequestAccount>( |
| account_config.id, GenerateEmailForUserId(account_config.id), |
| kAccountName, GenerateEmailForUserId(account_config.id), kAccountName, |
| kAccountGivenName, GURL(kAccountPicture), kAccountPhone, |
| kAccountUsername, |
| /*login_hints=*/std::vector<std::string>(), |
| /*domain_hints=*/std::vector<std::string>(), |
| /*labels=*/std::vector<std::string>(), account_config.login_state)); |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(callback), config_.accounts_fetch_status, |
| std::move(accounts))); |
| } |
| |
| bool DidFetchAnyEndpoint() { |
| return has_fetched_well_known_ || has_fetched_config_ || |
| has_fetched_accounts_endpoint_; |
| } |
| |
| base::WeakPtr<TestIdpNetworkRequestManager> AsWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| protected: |
| bool has_fetched_well_known_{false}; |
| bool has_fetched_config_{false}; |
| bool has_fetched_accounts_endpoint_{false}; |
| |
| private: |
| const Config config_; |
| base::WeakPtrFactory<TestIdpNetworkRequestManager> weak_ptr_factory_{this}; |
| }; |
| |
| class TestApiPermissionDelegate : public MockApiPermissionDelegate { |
| public: |
| ApiPermissionStatus GetApiPermissionStatus( |
| const url::Origin& origin) override { |
| return ApiPermissionStatus::GRANTED; |
| } |
| }; |
| |
| class TestPermissionDelegate : public MockPermissionDelegate { |
| public: |
| bool HasSharingPermission(const url::Origin& relying_party_requester, |
| const url::Origin& relying_party_embedder, |
| const url::Origin& identity_provider) override { |
| url::Origin rp_origin_with_data = url::Origin::Create(GURL(kRpUrl)); |
| url::Origin idp_origin_with_data = |
| url::Origin::Create(GURL(kPersonalizedButtonFrameUrl)); |
| bool has_granted_permission_per_profile = |
| relying_party_requester == rp_origin_with_data && |
| relying_party_embedder == rp_origin_with_data && |
| identity_provider == idp_origin_with_data; |
| return has_granted_permission_per_profile && |
| !accounts_with_sharing_permission_.empty(); |
| } |
| |
| std::optional<base::Time> GetLastUsedTimestamp( |
| const url::Origin& relying_party_requester, |
| const url::Origin& relying_party_embedder, |
| const url::Origin& identity_provider, |
| const std::string& account_id) override { |
| url::Origin rp_origin_with_data = url::Origin::Create(GURL(kRpUrl)); |
| url::Origin idp_origin_with_data = |
| url::Origin::Create(GURL(kPersonalizedButtonFrameUrl)); |
| bool has_granted_permission_per_profile = |
| relying_party_requester == rp_origin_with_data && |
| relying_party_embedder == rp_origin_with_data && |
| identity_provider == idp_origin_with_data; |
| return has_granted_permission_per_profile && |
| accounts_with_sharing_permission_.count(account_id) |
| ? std::make_optional<base::Time>() |
| : std::nullopt; |
| } |
| |
| std::optional<bool> GetIdpSigninStatus( |
| const url::Origin& idp_origin) override { |
| return idp_signin_status_; |
| } |
| |
| void SetConfig(const Config& config) { |
| idp_signin_status_ = config.idp_signin_status; |
| |
| accounts_with_sharing_permission_.clear(); |
| for (const AccountConfig& account_config : config.accounts) { |
| if (account_config.was_granted_sharing_permission) { |
| accounts_with_sharing_permission_.insert(account_config.id); |
| } |
| } |
| } |
| |
| private: |
| std::optional<bool> idp_signin_status_; |
| std::set<std::string> accounts_with_sharing_permission_; |
| }; |
| |
| } // namespace |
| |
| class UserInfoRequestTest : public RenderViewHostImplTestHarness { |
| public: |
| ~UserInfoRequestTest() override = default; |
| |
| void SetUp() override { |
| RenderViewHostImplTestHarness::SetUp(); |
| |
| api_permission_delegate_ = std::make_unique<TestApiPermissionDelegate>(); |
| permission_delegate_ = std::make_unique<TestPermissionDelegate>(); |
| |
| static_cast<TestWebContents*>(web_contents()) |
| ->NavigateAndCommit(GURL(kRpUrl), ui::PAGE_TRANSITION_LINK); |
| |
| // Add a subframe that navigates to kPersonalizedButtonFrameUrl. |
| TestRenderFrameHost* subframe = static_cast<TestRenderFrameHost*>( |
| content::RenderFrameHostTester::For(main_rfh()) |
| ->AppendChild("subframe")); |
| iframe_render_frame_host_ = static_cast<TestRenderFrameHost*>( |
| NavigationSimulator::NavigateAndCommitFromDocument( |
| GURL(kPersonalizedButtonFrameUrl), subframe)); |
| } |
| |
| void TearDown() override { |
| iframe_render_frame_host_ = nullptr; |
| RenderViewHostImplTestHarness::TearDown(); |
| } |
| |
| void RunUserInfoTest( |
| const Config& config, |
| RequestUserInfoStatus expected_user_info_status, |
| const std::vector<std::string>& expected_account_user_ids) { |
| permission_delegate_->SetConfig(config); |
| |
| auto network_manager = |
| std::make_unique<TestIdpNetworkRequestManager>(config); |
| network_manager_ = network_manager->AsWeakPtr(); |
| |
| blink::mojom::IdentityProviderConfigPtr idp_ptr = |
| blink::mojom::IdentityProviderConfig::New(); |
| idp_ptr->config_url = GURL(kProviderUrl); |
| idp_ptr->client_id = kClientId; |
| |
| UserInfoCallbackHelper callback_helper; |
| request_ = UserInfoRequest::Create( |
| std::move(network_manager), permission_delegate_.get(), |
| api_permission_delegate_.get(), iframe_render_frame_host_, |
| std::move(idp_ptr)); |
| request_->SetCallbackAndStart(callback_helper.callback()); |
| callback_helper.WaitForCallback(); |
| |
| EXPECT_EQ(expected_user_info_status, callback_helper.user_info_status_); |
| CheckUserInfo(expected_account_user_ids, callback_helper.user_info_); |
| } |
| |
| void CheckUserInfo( |
| const std::vector<std::string>& expected_account_ids, |
| const std::optional<std::vector<blink::mojom::IdentityUserInfoPtr>>& |
| actual_user_info) { |
| if (expected_account_ids.empty()) { |
| EXPECT_EQ(actual_user_info, std::nullopt); |
| return; |
| } |
| |
| EXPECT_EQ(expected_account_ids.size(), actual_user_info->size()); |
| for (size_t i = 0; i < expected_account_ids.size(); ++i) { |
| EXPECT_EQ(GenerateEmailForUserId(expected_account_ids[i]), |
| actual_user_info->at(i)->email); |
| } |
| } |
| |
| bool DidFetchAnyEndpoint() { return network_manager_->DidFetchAnyEndpoint(); } |
| |
| void ExpectConsoleMessage(const std::string& message) { |
| std::vector<std::string> messages = |
| RenderFrameHostTester::For(iframe_render_frame_host_) |
| ->GetConsoleMessages(); |
| ASSERT_EQ(messages.size(), 1u); |
| EXPECT_EQ(messages[0], message); |
| } |
| |
| void ExpectUniqueIssue(UserInfoRequestResult result) { |
| EXPECT_EQ( |
| iframe_render_frame_host_->GetFederatedAuthUserInfoRequestIssueCount( |
| result), |
| 1); |
| EXPECT_EQ( |
| iframe_render_frame_host_->GetFederatedAuthUserInfoRequestIssueCount( |
| std::nullopt), |
| 1); |
| } |
| |
| protected: |
| raw_ptr<TestRenderFrameHost> iframe_render_frame_host_; |
| base::WeakPtr<TestIdpNetworkRequestManager> network_manager_; |
| std::unique_ptr<TestApiPermissionDelegate> api_permission_delegate_; |
| std::unique_ptr<TestPermissionDelegate> permission_delegate_; |
| std::unique_ptr<UserInfoRequest> request_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| TEST_F(UserInfoRequestTest, PreviouslySignedIn) { |
| const char kAccount1Id[] = "account1"; |
| const char kAccount2Id[] = "account2"; |
| |
| Config config = kValidConfig; |
| config.accounts = {{kAccount1Id, /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/true}, |
| {kAccount2Id, /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/false}}; |
| RunUserInfoTest(config, RequestUserInfoStatus::kSuccess, |
| {kAccount1Id, kAccount2Id}); |
| |
| histogram_tester_.ExpectUniqueSample("Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kSuccess, 1); |
| histogram_tester_.ExpectUniqueSample("Blink.FedCm.UserInfo.NumAccounts", |
| Metrics::NumAccounts::kMultiple, 1); |
| histogram_tester_.ExpectTotalCount( |
| "Blink.FedCm.UserInfo.TimeToRequestCompleted", 1); |
| } |
| |
| TEST_F(UserInfoRequestTest, NoSignedInAccount) { |
| const char kAccount1Id[] = "account1"; |
| const char kAccount2Id[] = "account2"; |
| |
| Config config = kValidConfig; |
| config.accounts = {{kAccount1Id, /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/false}, |
| {kAccount2Id, /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/false}}; |
| RunUserInfoTest(config, RequestUserInfoStatus::kError, {}); |
| EXPECT_FALSE(DidFetchAnyEndpoint()); |
| |
| histogram_tester_.ExpectUniqueSample( |
| "Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kNoAccountSharingPermission, 1); |
| histogram_tester_.ExpectTotalCount("Blink.FedCm.UserInfo.NumAccounts", 0); |
| histogram_tester_.ExpectTotalCount( |
| "Blink.FedCm.UserInfo.TimeToRequestCompleted", 0); |
| ExpectConsoleMessage( |
| "getUserInfo() failed because the user has not yet used FedCM on this " |
| "site with the provided IDP."); |
| ExpectUniqueIssue(UserInfoRequestResult::kNoAccountSharingPermission); |
| } |
| |
| TEST_F(UserInfoRequestTest, NotInApprovedClientsList) { |
| const char kAccount1Id[] = "account1"; |
| const char kAccount2Id[] = "account2"; |
| |
| Config config = kValidConfig; |
| config.accounts = { |
| {kAccount1Id, /*idp_claimed_login_state=*/LoginState::kSignUp, |
| /*was_granted_sharing_permission=*/true}, |
| {kAccount2Id, /*idp_claimed_login_state=*/LoginState::kSignUp, |
| /*was_granted_sharing_permission=*/true}}; |
| RunUserInfoTest(config, RequestUserInfoStatus::kError, {}); |
| |
| histogram_tester_.ExpectUniqueSample( |
| "Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kNoReturningUserFromFetchedAccounts, 1); |
| histogram_tester_.ExpectUniqueSample("Blink.FedCm.UserInfo.NumAccounts", |
| Metrics::NumAccounts::kZero, 1); |
| histogram_tester_.ExpectTotalCount( |
| "Blink.FedCm.UserInfo.TimeToRequestCompleted", 1); |
| ExpectConsoleMessage( |
| "getUserInfo() failed because no account received was a returning " |
| "account."); |
| ExpectUniqueIssue(UserInfoRequestResult::kNoReturningUserFromFetchedAccounts); |
| } |
| |
| TEST_F(UserInfoRequestTest, InApprovedClientsList) { |
| const char kAccount1Id[] = "account1"; |
| const char kAccount2Id[] = "account2"; |
| |
| Config config = kValidConfig; |
| config.accounts = { |
| {kAccount1Id, /*idp_claimed_login_state=*/LoginState::kSignIn, |
| /*was_granted_sharing_permission=*/true}, |
| {kAccount2Id, /*idp_claimed_login_state=*/LoginState::kSignUp, |
| /*was_granted_sharing_permission=*/true}}; |
| RunUserInfoTest(config, RequestUserInfoStatus::kSuccess, |
| {kAccount1Id, kAccount2Id}); |
| } |
| |
| TEST_F(UserInfoRequestTest, |
| NoSharingPermissionButIdpHasThirdPartyCookiesAccessAndClaimsSignin) { |
| const char kAccountId[] = "account"; |
| |
| Config config = kValidConfig; |
| config.accounts = {{kAccountId, |
| /*idp_claimed_login_state=*/LoginState::kSignIn, |
| /*was_granted_sharing_permission=*/false}}; |
| |
| // Pretend the IdP was given third-party cookies access. |
| EXPECT_CALL(*api_permission_delegate_, |
| HasThirdPartyCookiesAccess(_, GURL(kProviderUrl), |
| url::Origin::Create(GURL(kRpUrl)))) |
| .WillRepeatedly(Return(true)); |
| |
| RunUserInfoTest(config, RequestUserInfoStatus::kSuccess, {kAccountId}); |
| |
| histogram_tester_.ExpectUniqueSample("Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kSuccess, 1); |
| } |
| |
| TEST_F(UserInfoRequestTest, |
| NoSharingPermissionButIdpHasThirdPartyCookiesAccessButNotSignin) { |
| const char kAccountId[] = "account"; |
| |
| Config config = kValidConfig; |
| config.accounts = {{kAccountId, /*idp_claimed_login_state=*/std::nullopt, |
| /*was_granted_sharing_permission=*/false}}; |
| |
| // Pretend the IdP was given third-party cookies access. |
| EXPECT_CALL(*api_permission_delegate_, |
| HasThirdPartyCookiesAccess(_, GURL(kProviderUrl), |
| url::Origin::Create(GURL(kRpUrl)))) |
| .WillRepeatedly(Return(true)); |
| |
| RunUserInfoTest(config, RequestUserInfoStatus::kError, {}); |
| |
| histogram_tester_.ExpectUniqueSample( |
| "Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kNoReturningUserFromFetchedAccounts, 1); |
| } |
| |
| TEST_F(UserInfoRequestTest, ConfigFetchFailed) { |
| Config config = kValidConfig; |
| config.config_fetch_status = {ParseStatus::kHttpNotFoundError, 404}; |
| |
| RunUserInfoTest(config, RequestUserInfoStatus::kError, {}); |
| |
| histogram_tester_.ExpectUniqueSample( |
| "Blink.FedCm.UserInfo.Status", |
| UserInfoRequestResult::kInvalidConfigOrWellKnown, 1); |
| histogram_tester_.ExpectTotalCount("Blink.FedCm.UserInfo.NumAccounts", 0); |
| histogram_tester_.ExpectTotalCount( |
| "Blink.FedCm.UserInfo.TimeToRequestCompleted", 0); |
| |
| ExpectConsoleMessage( |
| "getUserInfo() failed because the config and well-known files were " |
| "invalid."); |
| ExpectUniqueIssue(UserInfoRequestResult::kInvalidConfigOrWellKnown); |
| } |
| |
| TEST_F(UserInfoRequestTest, IdpSigninStatusClearedWhenAccountsRequestFails) { |
| std::vector<std::optional<bool>> kTestCases = {std::nullopt, true}; |
| |
| for (const std::optional<bool>& test_case : kTestCases) { |
| EXPECT_CALL(*permission_delegate_, SetIdpSigninStatus(_, false, _)); |
| |
| Config config = kValidConfig; |
| config.idp_signin_status = test_case; |
| config.accounts_fetch_status = {ParseStatus::kHttpNotFoundError, 404}; |
| |
| RunUserInfoTest(config, RequestUserInfoStatus::kError, {}); |
| |
| testing::Mock::VerifyAndClearExpectations(permission_delegate_.get()); |
| } |
| } |
| |
| // Tests that returning accounts are returned first in the user info response. |
| TEST_F(UserInfoRequestTest, ReturningAccountsFirst) { |
| const char kAccount1Id[] = "account1"; |
| const char kAccount2Id[] = "account2"; |
| const char kAccount3Id[] = "account3"; |
| const char kAccount4Id[] = "account4"; |
| |
| Config config = kValidConfig; |
| config.accounts = { |
| {kAccount1Id, /*idp_claimed_login_state=*/LoginState::kSignUp, |
| /*was_granted_sharing_permission=*/false}, |
| {kAccount2Id, /*idp_claimed_login_state=*/LoginState::kSignIn, |
| /*was_granted_sharing_permission=*/true}, |
| {kAccount3Id, /*idp_claimed_login_state=*/LoginState::kSignUp, |
| /*was_granted_sharing_permission=*/false}, |
| {kAccount4Id, /*idp_claimed_login_state=*/LoginState::kSignIn, |
| /*was_granted_sharing_permission=*/true}}; |
| RunUserInfoTest(config, RequestUserInfoStatus::kSuccess, |
| {kAccount2Id, kAccount4Id, kAccount1Id, kAccount3Id}); |
| } |
| |
| } // namespace content::webid |