| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/webid/idp_network_request_manager.h" |
| |
| #include <array> |
| #include <map> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include "base/strings/stringprintf.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/task_environment.h" |
| #include "base/values.h" |
| #include "content/public/browser/identity_request_dialog_controller.h" |
| #include "content/public/browser/manifest_icon_downloader.h" |
| #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/public/mojom/client_security_state.mojom.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "url/gurl.h" |
| |
| using AccountList = content::IdpNetworkRequestManager::AccountList; |
| using ClientMetadata = content::IdpNetworkRequestManager::ClientMetadata; |
| using Endpoints = content::IdpNetworkRequestManager::Endpoints; |
| using FetchStatus = content::IdpNetworkRequestManager::FetchStatus; |
| using AccountsRequestCallback = |
| content::IdpNetworkRequestManager::AccountsRequestCallback; |
| using RevokeResponse = content::IdpNetworkRequestManager::RevokeResponse; |
| using LoginState = content::IdentityRequestAccount::LoginState; |
| |
| namespace content { |
| |
| namespace { |
| |
| // Values for testing. Real minimum and ideal sizes are different. |
| const int kTestIdpBrandIconMinimumSize = 16; |
| const int kTestIdpBrandIconIdealSize = 32; |
| |
| const char kTestIdpUrl[] = "https://idp.test"; |
| const char kTestRpUrl[] = "https://rp.test"; |
| const char kTestManifestListUrl[] = "https://idp.test/.well-known/web-identity"; |
| const char kTestManifestUrl[] = "https://idp.test/fedcm.json"; |
| const char kTestAccountsEndpoint[] = "https://idp.test/accounts_endpoint"; |
| const char kTestTokenEndpoint[] = "https://idp.test/token_endpoint"; |
| const char kTestClientMetadataEndpoint[] = |
| "https://idp.test/client_metadata_endpoint"; |
| |
| class IdpNetworkRequestManagerTest : public ::testing::Test { |
| public: |
| std::unique_ptr<IdpNetworkRequestManager> CreateTestManager() { |
| return std::make_unique<IdpNetworkRequestManager>( |
| GURL(kTestIdpUrl), url::Origin::Create(GURL(kTestRpUrl)), |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_), |
| network::mojom::ClientSecurityState::New()); |
| } |
| |
| std::tuple<FetchStatus, std::set<GURL>> |
| SendManifestListRequestAndWaitForResponse(const char* test_data) { |
| GURL manifest_list_url(kTestManifestListUrl); |
| test_url_loader_factory().AddResponse(manifest_list_url.spec(), test_data); |
| |
| base::RunLoop run_loop; |
| FetchStatus parsed_fetch_status; |
| std::set<GURL> parsed_urls; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus fetch_status, const std::set<GURL>& urls) { |
| parsed_fetch_status = fetch_status; |
| parsed_urls = urls; |
| run_loop.Quit(); |
| }); |
| |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->FetchManifestList(std::move(callback)); |
| run_loop.Run(); |
| |
| return {parsed_fetch_status, parsed_urls}; |
| } |
| |
| std::tuple<FetchStatus, IdentityProviderMetadata> |
| SendManifestRequestAndWaitForResponse(const char* test_data) { |
| GURL manifest_url(kTestManifestUrl); |
| test_url_loader_factory().AddResponse(manifest_url.spec(), test_data); |
| |
| base::RunLoop run_loop; |
| FetchStatus parsed_fetch_status; |
| IdentityProviderMetadata parsed_idp_metadata; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus fetch_status, Endpoints endpoints, |
| IdentityProviderMetadata idp_metadata) { |
| parsed_fetch_status = fetch_status; |
| parsed_idp_metadata = std::move(idp_metadata); |
| run_loop.Quit(); |
| }); |
| |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->FetchManifest(kTestIdpBrandIconIdealSize, |
| kTestIdpBrandIconMinimumSize, std::move(callback)); |
| run_loop.Run(); |
| |
| return {parsed_fetch_status, parsed_idp_metadata}; |
| } |
| |
| std::tuple<FetchStatus, AccountList> SendAccountsRequestAndWaitForResponse( |
| const char* test_accounts, |
| const char* client_id = "", |
| bool send_id_and_referrer = false) { |
| GURL accounts_endpoint(kTestAccountsEndpoint); |
| test_url_loader_factory().AddResponse(accounts_endpoint.spec(), |
| test_accounts); |
| |
| base::RunLoop run_loop; |
| FetchStatus parsed_accounts_response; |
| AccountList parsed_accounts; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus response, AccountList accounts) { |
| parsed_accounts_response = response; |
| parsed_accounts = accounts; |
| run_loop.Quit(); |
| }); |
| |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->SendAccountsRequest(accounts_endpoint, client_id, |
| std::move(callback)); |
| run_loop.Run(); |
| |
| return {parsed_accounts_response, parsed_accounts}; |
| } |
| |
| std::string SendTokenRequestAndWaitForResponse( |
| const char* account, |
| const char* request, |
| net::HttpStatusCode http_status = net::HTTP_OK) { |
| const char response[] = R"({"token": "token"})"; |
| GURL token_endpoint(kTestTokenEndpoint); |
| test_url_loader_factory().AddResponse(token_endpoint.spec(), response, |
| http_status); |
| |
| std::string token; |
| base::RunLoop run_loop; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus status, const std::string& token_response) { |
| token = token_response; |
| run_loop.Quit(); |
| }); |
| |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->SendTokenRequest(token_endpoint, account, request, |
| std::move(callback)); |
| run_loop.Run(); |
| return token; |
| } |
| |
| ClientMetadata SendClientMetadataRequestAndWaitForResponse( |
| const char* client_id, |
| net::HttpStatusCode http_status = net::HTTP_OK) { |
| const char response[] = R"({})"; |
| GURL client_id_endpoint(kTestClientMetadataEndpoint); |
| test_url_loader_factory().AddResponse( |
| client_id_endpoint.spec() + "?client_id=" + client_id, response, |
| http_status); |
| |
| ClientMetadata data; |
| base::RunLoop run_loop; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus status, ClientMetadata metadata) { |
| data = metadata; |
| run_loop.Quit(); |
| }); |
| |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->FetchClientMetadata(client_id_endpoint, client_id, |
| std::move(callback)); |
| run_loop.Run(); |
| return data; |
| } |
| |
| network::TestURLLoaderFactory& test_url_loader_factory() { |
| return test_url_loader_factory_; |
| } |
| |
| private: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| data_decoder::test::InProcessDataDecoder in_process_data_decoder; |
| }; |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountEmpty) { |
| const auto* test_empty_account_json = R"({ |
| "accounts" : [] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_empty_account_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountSingle) { |
| const auto* test_single_account_json = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "given_name": "Ken", |
| "picture": "https://idp.test/profile/1" |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_single_account_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| EXPECT_EQ(1UL, accounts.size()); |
| EXPECT_EQ("1234", accounts[0].id); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountMultiple) { |
| const auto* test_accounts_json = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "given_name": "Ken", |
| "picture": "https://idp.test/profile/1" |
| }, |
| { |
| "id" : "5678", |
| "email": "sam@idp.test", |
| "name": "Sam G. Test", |
| "given_name": "Sam", |
| "picture": "https://idp.test/profile/2" |
| } |
| ] |
| })"; |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| EXPECT_EQ(2UL, accounts.size()); |
| EXPECT_EQ("1234", accounts[0].id); |
| EXPECT_EQ("5678", accounts[1].id); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountOptionalFields) { |
| // given_name and picture fields are optional |
| const auto* test_accounts_json = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example" |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| EXPECT_EQ("1234", accounts[0].id); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountRequiredFields) { |
| { |
| const auto* test_accounts_missing_account_id_json = R"({"accounts" : [{ |
| "email": "ken@idp.test", |
| "name": "Ken R. Example" |
| }]})"; |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse( |
| test_accounts_missing_account_id_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| { |
| const auto* test_accounts_missing_email_json = R"({"accounts" : [{ |
| "id" : "1234", |
| "name": "Ken R. Example" |
| }]})"; |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_missing_email_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| { |
| const auto* test_accounts_missing_name_json = R"({"accounts" : [{ |
| "id" : "1234", |
| "email": "ken@idp.test" |
| }]})"; |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_missing_name_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountPictureUrl) { |
| const auto* test_accounts_json = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "picture": "https://idp.test/profile/1234" |
| }, |
| { |
| "id" : "567", |
| "email": "sam@idp.test", |
| "name": "Sam R. Example", |
| "picture": "invalid_url" |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| EXPECT_TRUE(accounts[0].picture.is_valid()); |
| EXPECT_EQ(GURL("https://idp.test/profile/1234"), accounts[0].picture); |
| EXPECT_FALSE(accounts[1].picture.is_valid()); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountUnicode) { |
| auto TestAccountWithKeyValue = [](const std::string& key, |
| const std::string& value) { |
| const auto* json = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "%s": "%s" |
| } |
| ] |
| })"; |
| return base::StringPrintf(json, key.c_str(), value.c_str()); |
| }; |
| |
| std::array<std::string, 3> test_values{"ascii", "🦖", "مجید"}; |
| |
| for (auto& test_value : test_values) { |
| const auto& accounts_json = TestAccountWithKeyValue("name", test_value); |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(accounts_json.c_str()); |
| |
| EXPECT_EQ(1UL, accounts.size()); |
| EXPECT_EQ(test_value, accounts[0].name); |
| } |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountInvalid) { |
| const auto* test_invalid_account_json = "{}"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_invalid_account_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseAccountMalformed) { |
| const auto* test_invalid_account_json = "malformed_json"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_invalid_account_json); |
| |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response); |
| EXPECT_TRUE(accounts.empty()); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ComputeManifestListUrl) { |
| EXPECT_EQ("https://localhost:8000/.well-known/web-identity", |
| IdpNetworkRequestManager::ComputeManifestListUrl( |
| GURL("https://localhost:8000/test/")) |
| ->spec()); |
| |
| EXPECT_EQ("https://google.com/.well-known/web-identity", |
| IdpNetworkRequestManager::ComputeManifestListUrl( |
| GURL("https://www.google.com:8000/test/")) |
| ->spec()); |
| |
| EXPECT_EQ(absl::nullopt, IdpNetworkRequestManager::ComputeManifestListUrl( |
| GURL("https://192.101.0.1/test/"))); |
| } |
| |
| // Test that IdpNetworkRequestManager::FetchManifestList() fails when the |
| // identity provider domain is empty. |
| TEST_F(IdpNetworkRequestManagerTest, FetchManifestListIllegalDomainFails) { |
| GURL illegal_idp_url("https://192.101.0.1/test/"); |
| |
| network::TestURLLoaderFactory test_url_loader_factory; |
| auto network_manager = std::make_unique<IdpNetworkRequestManager>( |
| illegal_idp_url, url::Origin::Create(GURL(kTestRpUrl)), |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory), |
| network::mojom::ClientSecurityState::New()); |
| |
| std::string manifest_list_contents = |
| "({\"provider_urls\": [\"" + illegal_idp_url.spec() + "\"]})"; |
| |
| base::RunLoop run_loop; |
| auto callback = base::BindLambdaForTesting( |
| [&](FetchStatus fetch_status, const std::set<GURL>& urls) { |
| EXPECT_EQ(FetchStatus::kHttpNotFoundError, fetch_status); |
| run_loop.Quit(); |
| }); |
| network_manager->FetchManifestList(std::move(callback)); |
| run_loop.Run(); |
| |
| // Manifest list download should not have been attempted. |
| EXPECT_EQ(0, test_url_loader_factory.NumPending()); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseManifestList) { |
| FetchStatus fetch_status; |
| std::set<GURL> urls; |
| |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"({ |
| "provider_urls": ["https://idp.test/fedcm.json"] |
| })"); |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(std::set<GURL>{GURL("https://idp.test/fedcm.json")}, urls); |
| |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"({ |
| "provider_urls": ["https://idp.test/path/fedcm.json"] |
| })"); |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(std::set<GURL>{GURL("https://idp.test/path/fedcm.json")}, urls); |
| |
| // Value not a list |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"({ |
| "provider_urls": "https://idp.test/fedcm.json" |
| })"); |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, fetch_status); |
| |
| // Toplevel not a dictionary |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"( |
| ["https://idp.test/fedcm.json"] |
| )"); |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, fetch_status); |
| |
| // Incorrect key |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"({ |
| "providers": ["https://idp.test/fedcm.json"] |
| })"); |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, fetch_status); |
| |
| // Array entry not a string |
| std::tie(fetch_status, urls) = SendManifestListRequestAndWaitForResponse(R"({ |
| "provider_urls": [1] |
| })"); |
| EXPECT_EQ(FetchStatus::kInvalidResponseError, fetch_status); |
| } |
| |
| // Test that the "alpha" value in the "branding" JSON is ignored. |
| TEST_F(IdpNetworkRequestManagerTest, ParseManifestBrandingRemoveAlpha) { |
| const char test_json[] = R"({ |
| "branding" : { |
| "background_color": "#20202020" |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(SkColorSetARGB(0xff, 0x20, 0x20, 0x20), |
| idp_metadata.brand_background_color); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseManifestBrandingInvalidColor) { |
| const char test_json[] = R"({ |
| "branding" : { |
| "background_color": "fake_color" |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(absl::nullopt, idp_metadata.brand_background_color); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, |
| ParseManifestIgnoreInsufficientContrastTextColor) { |
| const char test_json[] = R"({ |
| "branding" : { |
| "background_color": "#000000", |
| "color": "#010101" |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(SkColorSetRGB(0, 0, 0), idp_metadata.brand_background_color); |
| EXPECT_EQ(absl::nullopt, idp_metadata.brand_text_color); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, |
| ParseManifestBrandingIgnoreCustomTextColorNoCustomBackgroundColor) { |
| const char test_json[] = R"({ |
| "branding" : { |
| "color": "blue" |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(absl::nullopt, idp_metadata.brand_background_color); |
| EXPECT_EQ(absl::nullopt, idp_metadata.brand_text_color); |
| } |
| |
| TEST_F(IdpNetworkRequestManagerTest, ParseManifestBrandingSelectBestSize) { |
| const char test_json[] = R"({ |
| "branding" : { |
| "icons": [ |
| { |
| "url": "https://example.com/10.png", |
| "size": 10 |
| }, |
| { |
| "url": "https://example.com/16.png", |
| "size": 16 |
| }, |
| { |
| "url": "https://example.com/31.png", |
| "size": 31 |
| }, |
| { |
| "url": "https://example.com/32.png", |
| "size": 32 |
| }, |
| { |
| "url": "https://example.com/33.png", |
| "size": 33 |
| } |
| ] |
| } |
| })"; |
| |
| ASSERT_EQ(32, kTestIdpBrandIconIdealSize); |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ("https://example.com/32.png", idp_metadata.brand_icon_url.spec()); |
| } |
| |
| // Test that the icon is rejected if there is an explicit brand icon size in the |
| // manifest and it is smaller than the `idp_brand_icon_minimum_size` parameter |
| // passed to IdpNetworkRequestManager::FetchManifest(). |
| TEST_F(IdpNetworkRequestManagerTest, ParseManifestBrandingMinSize) { |
| ASSERT_EQ(16, kTestIdpBrandIconMinimumSize); |
| |
| { |
| const char test_json[] = R"({ |
| "branding" : { |
| "icons": [ |
| { |
| "url": "https://example.com/15.png", |
| "size": 15 |
| } |
| ] |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ(GURL(), idp_metadata.brand_icon_url); |
| } |
| |
| { |
| const char test_json[] = R"({ |
| "branding" : { |
| "icons": [ |
| { |
| "url": "https://example.com/16.png", |
| "size": 16 |
| } |
| ] |
| } |
| })"; |
| |
| FetchStatus fetch_status; |
| IdentityProviderMetadata idp_metadata; |
| std::tie(fetch_status, idp_metadata) = |
| SendManifestRequestAndWaitForResponse(test_json); |
| |
| EXPECT_EQ(FetchStatus::kSuccess, fetch_status); |
| EXPECT_EQ("https://example.com/16.png", idp_metadata.brand_icon_url.spec()); |
| } |
| } |
| |
| // Tests that we send the correct referrer for account requests. |
| TEST_F(IdpNetworkRequestManagerTest, AccountRequestReferrer) { |
| bool called = false; |
| auto interceptor = |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| called = true; |
| EXPECT_EQ(GURL(kTestAccountsEndpoint), request.url); |
| EXPECT_EQ(request.request_body, nullptr); |
| EXPECT_EQ(false, request.referrer.is_valid()); |
| }); |
| test_url_loader_factory().SetInterceptor(interceptor); |
| |
| const char test_accounts_json[] = R"({ |
| "accounts" : [ |
| { |
| "id" : "1234", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example" |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json); |
| |
| ASSERT_TRUE(called); |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| } |
| |
| // Verifies that we correctly check the signed-in status. |
| TEST_F(IdpNetworkRequestManagerTest, AccountSignedInStatus) { |
| bool called = false; |
| auto interceptor = |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| called = true; |
| EXPECT_EQ(GURL(kTestAccountsEndpoint), request.url); |
| EXPECT_EQ(request.request_body, nullptr); |
| EXPECT_FALSE(request.referrer.is_valid()); |
| }); |
| test_url_loader_factory().SetInterceptor(interceptor); |
| |
| const char test_accounts_json[] = R"({ |
| "accounts" : [ |
| { |
| "id" : "1", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "approved_clients": ["xxx"] |
| }, |
| { |
| "id" : "2", |
| "email": "jim@idp.test", |
| "name": "Jim R. Example", |
| "approved_clients": [] |
| }, |
| { |
| "id" : "3", |
| "email": "rashida@idp.test", |
| "name": "Rashida R. Example", |
| "approved_clients": ["yyy"] |
| }, |
| { |
| "id" : "4", |
| "email": "wei@idp.test", |
| "name": "Wei R. Example" |
| }, |
| { |
| "id" : "5", |
| "email": "hans@idp.test", |
| "name": "Hans R. Example", |
| "approved_clients": ["xxx", "yyy"] |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json, "xxx"); |
| |
| EXPECT_TRUE(called); |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| ASSERT_EQ(5ul, accounts.size()); |
| ASSERT_TRUE(accounts[0].login_state.has_value()); |
| EXPECT_EQ(LoginState::kSignIn, *accounts[0].login_state); |
| ASSERT_TRUE(accounts[1].login_state.has_value()); |
| EXPECT_EQ(LoginState::kSignUp, *accounts[1].login_state); |
| ASSERT_TRUE(accounts[2].login_state.has_value()); |
| EXPECT_EQ(LoginState::kSignUp, *accounts[2].login_state); |
| EXPECT_FALSE(accounts[3].login_state.has_value()); |
| ASSERT_TRUE(accounts[4].login_state.has_value()); |
| EXPECT_EQ(LoginState::kSignIn, *accounts[4].login_state); |
| } |
| |
| // Tests the token request implementation. |
| TEST_F(IdpNetworkRequestManagerTest, TokenRequest) { |
| bool called = false; |
| auto interceptor = |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| called = true; |
| EXPECT_EQ(GURL(kTestTokenEndpoint), request.url); |
| EXPECT_EQ(GURL(kTestRpUrl), request.referrer); |
| |
| // Check that the request body is correct (should be "request") |
| ASSERT_NE(request.request_body, nullptr); |
| ASSERT_EQ(1ul, request.request_body->elements()->size()); |
| const network::DataElement& elem = |
| request.request_body->elements()->at(0); |
| ASSERT_EQ(network::DataElement::Tag::kBytes, elem.type()); |
| const network::DataElementBytes& byte_elem = |
| elem.As<network::DataElementBytes>(); |
| EXPECT_EQ("request", byte_elem.AsStringPiece()); |
| }); |
| test_url_loader_factory().SetInterceptor(interceptor); |
| std::string token = SendTokenRequestAndWaitForResponse("account", "request"); |
| ASSERT_TRUE(called); |
| ASSERT_EQ("token", token); |
| } |
| |
| // Tests the client metadata implementation. |
| TEST_F(IdpNetworkRequestManagerTest, ClientMetadata) { |
| bool called = false; |
| auto interceptor = |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| called = true; |
| std::string url_string = |
| std::string(kTestClientMetadataEndpoint) + "?client_id=xxx"; |
| EXPECT_EQ(GURL(url_string), request.url); |
| EXPECT_EQ(request.request_body, nullptr); |
| EXPECT_EQ(GURL(kTestRpUrl), request.referrer); |
| }); |
| test_url_loader_factory().SetInterceptor(interceptor); |
| ClientMetadata data = SendClientMetadataRequestAndWaitForResponse("xxx"); |
| ASSERT_TRUE(called); |
| ASSERT_EQ("", data.privacy_policy_url); |
| ASSERT_EQ("", data.terms_of_service_url); |
| } |
| |
| // Tests that we correctly records metrics regarding approved_clients. |
| TEST_F(IdpNetworkRequestManagerTest, RecordApprovedClientsMetrics) { |
| base::HistogramTester histogram_tester; |
| bool called = false; |
| auto interceptor = |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| called = true; |
| EXPECT_EQ(GURL(kTestAccountsEndpoint), request.url); |
| EXPECT_EQ(request.request_body, nullptr); |
| EXPECT_FALSE(request.referrer.is_valid()); |
| }); |
| test_url_loader_factory().SetInterceptor(interceptor); |
| |
| const char test_accounts_json[] = R"({ |
| "accounts" : [ |
| { |
| "id" : "1", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "approved_clients": [] |
| }, |
| { |
| "id" : "2", |
| "email": "jim@idp.test", |
| "name": "Jim R. Example", |
| "approved_clients": ["xxx"] |
| }, |
| { |
| "id" : "3", |
| "email": "rashida@idp.test", |
| "name": "Rashida R. Example", |
| "approved_clients": ["xxx", "yyy"] |
| }, |
| { |
| "id" : "4", |
| "email": "wei@idp.test", |
| "name": "Wei R. Example" |
| } |
| ] |
| })"; |
| |
| FetchStatus accounts_response; |
| AccountList accounts; |
| std::tie(accounts_response, accounts) = |
| SendAccountsRequestAndWaitForResponse(test_accounts_json, "xxx"); |
| |
| EXPECT_TRUE(called); |
| EXPECT_EQ(FetchStatus::kSuccess, accounts_response); |
| ASSERT_EQ(4ul, accounts.size()); |
| |
| histogram_tester.ExpectTotalCount("Blink.FedCm.ApprovedClientsExistence", 4); |
| histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsExistence", 1, |
| 3); |
| histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsExistence", 0, |
| 1); |
| |
| histogram_tester.ExpectTotalCount("Blink.FedCm.ApprovedClientsSize", 3); |
| histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 0, 1); |
| histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 1, 1); |
| histogram_tester.ExpectBucketCount("Blink.FedCm.ApprovedClientsSize", 2, 1); |
| } |
| |
| // Test that the callback is not called after IdpNetworkRequestManager is |
| // destroyed. |
| TEST_F(IdpNetworkRequestManagerTest, DontCallCallbackAfterManagerDeletion) { |
| const char test_accounts_json[] = R"({ |
| "accounts" : [ |
| { |
| "id" : "1", |
| "email": "ken@idp.test", |
| "name": "Ken R. Example", |
| "approved_clients": [] |
| } |
| ] |
| })"; |
| |
| GURL accounts_endpoint(kTestAccountsEndpoint); |
| test_url_loader_factory().AddResponse(accounts_endpoint.spec(), |
| test_accounts_json); |
| |
| bool callback_called = false; |
| auto callback = base::BindLambdaForTesting( |
| [&callback_called](FetchStatus response, AccountList accounts) { |
| callback_called = true; |
| }); |
| |
| { |
| std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); |
| manager->SendAccountsRequest(accounts_endpoint, /*client_id=*/"", |
| std::move(callback)); |
| // Destroy `manager`. |
| } |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(callback_called); |
| } |
| |
| } // namespace |
| |
| } // namespace content |