| // Copyright 2023 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/config_fetcher.h" |
| |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/webid/test/mock_idp_network_request_manager.h" |
| #include "content/public/common/content_features.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 "url/gurl.h" |
| #include "url/origin.h" |
| |
| using FetchStatus = content::IdpNetworkRequestManager::FetchStatus; |
| using ParseStatus = content::IdpNetworkRequestManager::ParseStatus; |
| using ::testing::_; |
| using ::testing::StrictMock; |
| using ::testing::WithArg; |
| |
| namespace content::webid { |
| |
| class ConfigFetcherTest : public RenderViewHostImplTestHarness { |
| protected: |
| ConfigFetcherTest() = default; |
| ~ConfigFetcherTest() override = default; |
| |
| void SetUp() override { RenderViewHostImplTestHarness::SetUp(); } |
| |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| TEST_F(ConfigFetcherTest, FailedToFetchWellKnown) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| // Returns a 404 for the fetch of the well-known file. |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| std::move(callback).Run( |
| {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get a kWellKnownHttpNotFound. |
| fetcher.Start( |
| {{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting([&loop](std::vector<ConfigFetcher::FetchResult> |
| result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_TRUE(result[0].error); |
| EXPECT_EQ( |
| result[0].error->result, |
| blink::mojom::FederatedAuthRequestResult::kWellKnownHttpNotFound); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, FailedToFetchWellKnownButNoEnforcement) { |
| feature_list_.InitAndEnableFeature( |
| features::kFedCmWithoutWellKnownEnforcement); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| // Returns a 404 for the fetch of the well-known file. |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| std::move(callback).Run( |
| {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get no error in the result. |
| fetcher.Start({{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_FALSE(result[0].error); |
| EXPECT_TRUE(result[0].wellknown.provider_urls.empty()); |
| EXPECT_EQ(result[0].endpoints.token, |
| GURL("https://idp.example/token.php")); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, FailedToFetchConfig) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 404 for the fetch of the config file. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| std::move(callback).Run( |
| {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND}, |
| /*endpoints=*/{}, /*metadata=*/{}); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get a kConfigHttpNotFound. |
| fetcher.Start( |
| {{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_TRUE(result[0].error); |
| EXPECT_EQ( |
| result[0].error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigHttpNotFound); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, SucceedsToFetchConfigButInvalidResponse) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 200 but with an empty and invalid response. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| /*endpoints=*/{}, /*metadata=*/{}); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get a kConfigHttpNotFound. |
| fetcher.Start( |
| {{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting([&loop](std::vector<ConfigFetcher::FetchResult> |
| result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_TRUE(result[0].error); |
| EXPECT_EQ( |
| result[0].error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, SuccessfullAndValidResponse) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 200 but with an empty and invalid response. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get a kConfigHttpNotFound. |
| fetcher.Start({{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_FALSE(result[0].error); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, |
| TooManyProvidersInWellKnownLeadsToErrorWellKnownTooBig) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 200 but with an empty and invalid response. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.provider_urls = { |
| GURL("https://idp.example/fedcm.json"), |
| GURL("https://idp.example/one-too-many.json")}; |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get a kConfigHttpNotFound. |
| fetcher.Start( |
| {{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_TRUE(result[0].error); |
| EXPECT_EQ( |
| result[0].error->result, |
| blink::mojom::FederatedAuthRequestResult::kWellKnownTooBig); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, ProvidersUrlsIgnoredWhenAccountEndpointsMatch) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 200 but with an empty and invalid response. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| well_known.accounts = GURL("https://idp.example/accounts.php"); |
| well_known.login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get no error in the result. |
| fetcher.Start({{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_FALSE(result[0].error); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, ProvidersUrlsCanbeEmptyWhenAccountEndpointsMatch) { |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| // Returns a 200 but with an empty and invalid response. |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| well_known.accounts = GURL("https://idp.example/accounts.php"); |
| well_known.login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get no error in the result. |
| fetcher.Start({{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/false}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_FALSE(result[0].error); |
| EXPECT_TRUE(result[0].wellknown.provider_urls.empty()); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| TEST_F(ConfigFetcherTest, ValidFetchResult) { |
| // The most basic valid fetch result is one where: |
| // (a) both the well-known and the config files were loaded successfully |
| // (b) there is an accounts and token endpoint in the config file |
| // (c) the well-known file contains the configURL |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| result.metadata = std::move(metadata); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| EXPECT_FALSE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidMissingAcccountsEndpoint) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidCrossOriginAcccountsEndpoint) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://cross-origin.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidMissingTokenEndpoint) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidCrossOriginTokenEndpoint) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://cross-origin.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidCrossOriginSigninUrl) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://cross-origin.example/sign-in"); |
| result.metadata = metadata; |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidConfigUrlNotInProviders) { |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php"); |
| |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = { |
| GURL("https://another-idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| result.metadata = std::move(metadata); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigNotInWellKnown); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidConfigUrlNotInWellKnown) { |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php"); |
| |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = { |
| GURL("https://idp.example/another-file.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| result.metadata = std::move(metadata); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigNotInWellKnown); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidWellKnownTooManyProviders) { |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php"); |
| |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = { |
| GURL("https://idp.example/fedcm.json"), |
| GURL("https://idp.example/another-one.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| result.metadata = std::move(metadata); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kWellKnownTooBig); |
| } |
| |
| TEST_F(ConfigFetcherTest, SkippingTheChecksWithTheWellKnownFlag) { |
| feature_list_.InitAndEnableFeature( |
| features::kFedCmWithoutWellKnownEnforcement); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php"); |
| |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = { |
| GURL("https://idp.example/another-file.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| result.metadata = std::move(metadata); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_FALSE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidWellKnownWithoutSignInUrl) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.wellknown.accounts = GURL("https://idp.example/accounts"); |
| result.identity_provider_config_url = |
| GURL("https://idp.example/another-file.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidWellKnownWithoutAccountsEndpoint) { |
| ConfigFetcher::FetchResult result; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.wellknown.login_url = GURL("https://idp.example/signin"); |
| result.identity_provider_config_url = |
| GURL("https://idp.example/another-file.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, ValidWellKnownWithMatchingAccountsAndSignInUrl) { |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.wellknown.accounts = GURL("https://idp.example/accounts"); |
| result.wellknown.login_url = GURL("https://idp.example/sign-in"); |
| result.identity_provider_config_url = |
| GURL("https://idp.example/another-file.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_FALSE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, |
| FailResultEvenIfConfigUrlMatchesWhenAccountsEndpointIsAvailable) { |
| // In this test, we verify that when the accounts endpoint is available and |
| // not matching the one provided in the configURL, we do to allow falling |
| // back to checking based on the fact that the provider_url contains the |
| // configURL. |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL("https://idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.accounts = GURL("https://idp.example/another-accounts"); |
| result.wellknown.login_url = GURL("https://idp.example/sign-in"); |
| |
| // Even if the configURL is present in the provider_urls, the presence of |
| // the accounts_endpoint disqualifies this configuration. |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| // EXPECT_EQ(result.error->result, blink::mojom::FederatedAuthRequestResult:: |
| // kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, |
| SuccessResultEvenWithEmptyAccountsEndpointWithLightweightFedCm) { |
| // Validate that when LightweightFedCM is enabled, it's permissible to have an |
| // empty accounts_endpoint set. |
| feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode); |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL(); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.login_url = GURL("https://idp.example/sign-in"); |
| result.wellknown.accounts = GURL(); |
| |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_FALSE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, ProvidersUrlsCanbeEmptyWhenLightweightIsEnabled) { |
| // Validate that when LightweightFedCM is enabled, |
| // it's permissible to have an empty accounts_endpoint set and |
| // no provider_config_urls, so long as the accounts url is empty in both the |
| // wellknown and config. |
| feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode); |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL(); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.login_url = GURL("https://idp.example/sign-in"); |
| result.wellknown.accounts = GURL(); |
| |
| result.wellknown.provider_urls = {}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_FALSE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, |
| FailureResultWithMismatchingAccountsEndpointWithLightweightFedCm) { |
| // Validate that when LightweightFedCM is enabled, it's still an error to have |
| // a non-same-origin accounts endpoint. |
| feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode); |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL("https://not-the-idp.example/accounts"); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.login_url = GURL(); |
| result.wellknown.accounts = GURL(); |
| |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, |
| FailureResultWithEmptyAccountsEndpointWithoutLightweightFedCm) { |
| // Validate that when LightweightFedCM is disabled, it's still an error to not |
| // define an accounts endpoint. |
| ConfigFetcher::FetchResult result; |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = GURL("https://idp.example/sign-in"); |
| result.metadata = metadata; |
| result.endpoints.accounts = GURL(); |
| result.endpoints.token = GURL("https://idp.example/token"); |
| result.wellknown.login_url = GURL(); |
| result.wellknown.accounts = GURL(); |
| |
| result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")}; |
| result.identity_provider_config_url = GURL("https://idp.example/fedcm.json"); |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidEmptyConfig) { |
| ConfigFetcher::FetchResult result; |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigInvalidResponse); |
| } |
| |
| TEST_F(ConfigFetcherTest, InvalidNetworkError) { |
| ConfigFetcher::FetchResult result; |
| result.error = ConfigFetcher::FetchError( |
| blink::mojom::FederatedAuthRequestResult::kConfigHttpNotFound, |
| RequestIdTokenStatus::kConfigHttpNotFound, |
| /*additional_console_error_message=*/std::nullopt); |
| |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| fetcher.ValidateAndMaybeSetError(result); |
| |
| EXPECT_TRUE(result.error); |
| EXPECT_EQ(result.error->result, |
| blink::mojom::FederatedAuthRequestResult::kConfigHttpNotFound); |
| } |
| |
| TEST_F(ConfigFetcherTest, RegisteredIdpSkipsWellKnownCheck) { |
| base::test::ScopedFeatureList list; |
| list.InitAndEnableFeature(features::kFedCmIdPRegistration); |
| auto network_manager = |
| std::make_unique<StrictMock<MockIdpNetworkRequestManager>>(); |
| ConfigFetcher fetcher(*main_rfh(), network_manager.get()); |
| |
| EXPECT_CALL(*network_manager, FetchConfig) |
| .WillOnce(WithArg<4>( |
| [](IdpNetworkRequestManager::FetchConfigCallback callback) { |
| IdpNetworkRequestManager::Endpoints endpoints; |
| endpoints.token = GURL("https://idp.example/token.php"); |
| endpoints.accounts = GURL("https://idp.example/accounts.php"); |
| |
| IdentityProviderMetadata metadata; |
| metadata.idp_login_url = |
| GURL("https://idp.example/idp_login_url.php"); |
| std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK}, |
| endpoints, metadata); |
| })); |
| |
| // Returns a 404 for the fetch of the well-known file. |
| EXPECT_CALL(*network_manager, FetchWellKnown) |
| .WillOnce(WithArg<1>( |
| [](IdpNetworkRequestManager::FetchWellKnownCallback callback) { |
| IdpNetworkRequestManager::WellKnown well_known; |
| std::move(callback).Run( |
| {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND}, |
| well_known); |
| })); |
| |
| base::RunLoop loop; |
| |
| // Asserts that we get success despite well-known failing. |
| fetcher.Start({{GURL("https://idp.example/fedcm.json"), |
| /*force_skip_well_known_enforcement=*/true}}, |
| blink::mojom::RpMode::kPassive, |
| /*icon_ideal_size=*/0, |
| /*icon_minimum_size=*/0, |
| base::BindLambdaForTesting( |
| [&loop](std::vector<ConfigFetcher::FetchResult> result) { |
| EXPECT_EQ(result.size(), 1ul); |
| EXPECT_FALSE(result[0].error); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| } // namespace content::webid |