Commit 44066d85 authored by David Roger's avatar David Roger Committed by Commit Bot

[signin] Split multilogin code out of GaiaCookieManagerService

This CL is a code cleanup of the multilogin code.
The code is split in two new classes, and should be a lot clearer.

The retry logic has been revisited, and in particular a potential
infinite loop was removed:
in the previous code, a "invalid tokens" response was always retried.
The code was relying on Android (GMSCore) to invalidate and evict the access
token, so that the same token is not retried endlessly.
However, in case of a GMSCore bug, an infinite loop may happen.
We don't have a proof that this was happening, but as we have seen a large
number of errors coming from a few devices, this CL adds protection against
this and only retries once.

Change-Id: I097b6104f12f6abb65b1a30f75d753cef6610c17
Bug: 957536
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1572131
Commit-Queue: David Roger <droger@chromium.org>
Reviewed-by: default avatarMihai Sardarescu <msarda@chromium.org>
Cr-Commit-Position: refs/heads/master@{#654826}
parent 9162ace6
...@@ -80,6 +80,10 @@ static_library("internals") { ...@@ -80,6 +80,10 @@ static_library("internals") {
"gaia_cookie_manager_service.h", "gaia_cookie_manager_service.h",
"oauth2_token_service_delegate_android.cc", "oauth2_token_service_delegate_android.cc",
"oauth2_token_service_delegate_android.h", "oauth2_token_service_delegate_android.h",
"oauth_multilogin_helper.cc",
"oauth_multilogin_helper.h",
"oauth_multilogin_token_fetcher.cc",
"oauth_multilogin_token_fetcher.h",
"profile_oauth2_token_service.cc", "profile_oauth2_token_service.cc",
"profile_oauth2_token_service.h", "profile_oauth2_token_service.h",
"profile_oauth2_token_service_delegate_chromeos.cc", "profile_oauth2_token_service_delegate_chromeos.cc",
...@@ -274,9 +278,10 @@ source_set("unit_tests") { ...@@ -274,9 +278,10 @@ source_set("unit_tests") {
"device_id_helper_unittest.cc", "device_id_helper_unittest.cc",
"dice_account_reconcilor_delegate_unittest.cc", "dice_account_reconcilor_delegate_unittest.cc",
"gaia_cookie_manager_service_unittest.cc", "gaia_cookie_manager_service_unittest.cc",
"identity_utils_unittest.cc",
"mice_account_reconcilor_delegate_unittest.cc", "mice_account_reconcilor_delegate_unittest.cc",
"mutable_profile_oauth2_token_service_delegate_unittest.cc", "mutable_profile_oauth2_token_service_delegate_unittest.cc",
"oauth_multilogin_helper_unittest.cc",
"oauth_multilogin_token_fetcher_unittest.cc",
"profile_oauth2_token_service_delegate_chromeos_unittest.cc", "profile_oauth2_token_service_delegate_chromeos_unittest.cc",
"signin_error_controller_unittest.cc", "signin_error_controller_unittest.cc",
"signin_header_helper_unittest.cc", "signin_header_helper_unittest.cc",
......
...@@ -12,18 +12,18 @@ ...@@ -12,18 +12,18 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/callback.h" #include "base/callback_forward.h"
#include "base/containers/circular_deque.h" #include "base/containers/circular_deque.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "components/signin/core/browser/oauth_multilogin_helper.h"
#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_client.h"
#include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/oauth2_token_service.h" #include "google_apis/gaia/oauth2_token_service.h"
#include "google_apis/gaia/oauth_multilogin_result.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
#include "net/base/backoff_entry.h" #include "net/base/backoff_entry.h"
#include "services/network/public/mojom/cookie_manager.mojom.h" #include "services/network/public/mojom/cookie_manager.mojom.h"
...@@ -70,8 +70,7 @@ struct MultiloginParameters { ...@@ -70,8 +70,7 @@ struct MultiloginParameters {
// GAIA cookie are blocked (such as youtube). This is executed once for the // GAIA cookie are blocked (such as youtube). This is executed once for the
// lifetime of this object, when the first call is made to AddAccountToCookie. // lifetime of this object, when the first call is made to AddAccountToCookie.
class GaiaCookieManagerService : public GaiaAuthConsumer, class GaiaCookieManagerService : public GaiaAuthConsumer,
public network::mojom::CookieChangeListener, public network::mojom::CookieChangeListener {
public OAuth2TokenService::Consumer {
public: public:
enum GaiaCookieRequestType { enum GaiaCookieRequestType {
ADD_ACCOUNT, ADD_ACCOUNT,
...@@ -264,11 +263,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -264,11 +263,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
SetAccountsInCookieCompletedCallback SetAccountsInCookieCompletedCallback
set_accounts_in_cookies_completed_callback); set_accounts_in_cookies_completed_callback);
// Takes list of account_ids from the front request, matches them with a
// corresponding stored access_token and calls StartMultilogin.
// Virtual for testing purposes.
virtual void SetAccountsInCookieWithTokens();
// Returns if the listed accounts are up to date or not. The out parameter // Returns if the listed accounts are up to date or not. The out parameter
// will be assigned the current cached accounts (whether they are not up to // will be assigned the current cached accounts (whether they are not up to
// date or not). If the accounts are not up to date, a ListAccounts fetch is // date or not). If the accounts are not up to date, a ListAccounts fetch is
...@@ -319,20 +313,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -319,20 +313,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
void OnUbertokenFetchComplete(GoogleServiceAuthError error, void OnUbertokenFetchComplete(GoogleServiceAuthError error,
const std::string& uber_token); const std::string& uber_token);
private: // Final call in the Setting accounts in cookie procedure. Public for testing.
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, void OnSetAccountsFinished(const GoogleServiceAuthError& error);
MultiloginSuccessAndCookiesSet);
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
MultiloginFailurePersistentError);
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
MultiloginFailureMaxRetriesReached);
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
FetcherRetriesZeroedBetweenCalls);
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
MultiloginFailureInvalidGaiaCredentialsMobile);
FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
MultiloginFailureInvalidGaiaCredentialsDesktop);
private:
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory(); scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
// Calls the AddAccountToCookie completion callback. // Calls the AddAccountToCookie completion callback.
...@@ -351,57 +335,24 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -351,57 +335,24 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
network::mojom::CookieChangeCause cause) override; network::mojom::CookieChangeCause cause) override;
void OnCookieListenerConnectionError(); void OnCookieListenerConnectionError();
// Overridden from OAuth2TokenService::Consumer.
void OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) override;
// Called when either refresh or access token becomes available.
void OnTokenFetched(const std::string& account_id, const std::string& token);
// Overridden from GaiaAuthConsumer. // Overridden from GaiaAuthConsumer.
void OnMergeSessionSuccess(const std::string& data) override; void OnMergeSessionSuccess(const std::string& data) override;
void OnMergeSessionFailure(const GoogleServiceAuthError& error) override; void OnMergeSessionFailure(const GoogleServiceAuthError& error) override;
void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override;
void OnListAccountsSuccess(const std::string& data) override; void OnListAccountsSuccess(const std::string& data) override;
void OnListAccountsFailure(const GoogleServiceAuthError& error) override; void OnListAccountsFailure(const GoogleServiceAuthError& error) override;
void OnLogOutSuccess() override; void OnLogOutSuccess() override;
void OnLogOutFailure(const GoogleServiceAuthError& error) override; void OnLogOutFailure(const GoogleServiceAuthError& error) override;
// Callback for CookieManager::SetCanonicalCookie.
void OnCookieSet(const std::string& cookie_name,
const std::string& cookie_domain,
net::CanonicalCookie::CookieInclusionStatus status);
// Final call in the Setting accounts in cookie procedure. Virtual for testing
// purposes.
virtual void OnSetAccountsFinished(const GoogleServiceAuthError& error);
// Helper method for AddAccountToCookie* methods. // Helper method for AddAccountToCookie* methods.
void AddAccountToCookieInternal( void AddAccountToCookieInternal(
const std::string& account_id, const std::string& account_id,
gaia::GaiaSource source, gaia::GaiaSource source,
AddAccountToCookieCompletedCallback completion_callback); AddAccountToCookieCompletedCallback completion_callback);
// Helper function to trigger fetching retry in case of failure for only
// failed account id. Virtual for testing purposes.
virtual void StartFetchingAccessTokenForMultilogin(
const std::string& account_id);
// Starts the process of fetching the access token with OauthLogin scope and
// performing SetAccountsInCookie on success. Virtual so that it can be
// overridden in tests.
virtual void StartFetchingAccessTokensForMultilogin();
// Starts the proess of fetching the uber token and performing a merge session // Starts the proess of fetching the uber token and performing a merge session
// for the next account. Virtual so that it can be overriden in tests. // for the next account. Virtual so that it can be overriden in tests.
virtual void StartFetchingUbertoken(); virtual void StartFetchingUbertoken();
// Starts the process of setting accounts in cookie.
void StartFetchingMultiLogin(
const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& accounts);
// Virtual for testing purposes. // Virtual for testing purposes.
virtual void StartFetchingMergeSession(); virtual void StartFetchingMergeSession();
...@@ -415,9 +366,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -415,9 +366,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
// Virtual for testing purpose. // Virtual for testing purpose.
virtual void StartFetchingLogOut(); virtual void StartFetchingLogOut();
// Starts setting parsed cookies in browser.
void StartSettingCookies(const OAuthMultiloginResult& result);
// Start the next request, if needed. // Start the next request, if needed.
void HandleNextRequest(); void HandleNextRequest();
...@@ -429,6 +377,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -429,6 +377,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_; std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_;
ExternalCcResultFetcher external_cc_result_fetcher_; ExternalCcResultFetcher external_cc_result_fetcher_;
std::unique_ptr<signin::OAuthMultiloginHelper> oauth_multilogin_helper_;
// If the GaiaAuthFetcher or SimpleURLLoader fails, retry with exponential // If the GaiaAuthFetcher or SimpleURLLoader fails, retry with exponential
// backoff and network delay. // backoff and network delay.
...@@ -439,21 +388,9 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, ...@@ -439,21 +388,9 @@ class GaiaCookieManagerService : public GaiaAuthConsumer,
// The last fetched ubertoken, for use in MergeSession retries. // The last fetched ubertoken, for use in MergeSession retries.
std::string uber_token_; std::string uber_token_;
// Access tokens for use inside SetAccountsToCookie.
// TODO (valeriyas): make FetchUberToken use those instead of a separate
// access_token.
std::unordered_map<std::string, std::string> access_tokens_;
// Current list of processed token requests;
std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_;
// The access token that can be used to prime the UberToken fetch. // The access token that can be used to prime the UberToken fetch.
std::string access_token_; std::string access_token_;
// List of pairs (cookie name and cookie domain) that have to be set in
// cookie jar.
std::set<std::pair<std::string, std::string>> cookies_to_set_;
// Connection to the CookieManager that signals when the GAIA cookies change. // Connection to the CookieManager that signals when the GAIA cookies change.
mojo::Binding<network::mojom::CookieChangeListener> cookie_listener_binding_; mojo::Binding<network::mojom::CookieChangeListener> cookie_listener_binding_;
......
// Copyright 2019 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 "components/signin/core/browser/oauth_multilogin_helper.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
#include "components/signin/core/browser/signin_client.h"
#include "google_apis/gaia/oauth_multilogin_result.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace {
constexpr int kMaxFetcherRetries = 3;
void RecordMultiloginFinished(GoogleServiceAuthError error) {
UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginFinished", error.state(),
GoogleServiceAuthError::NUM_STATES);
}
std::string FindTokenForAccount(
const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs,
const std::string& account_id) {
for (auto it = token_id_pairs.cbegin(); it != token_id_pairs.cend(); ++it) {
if (account_id == it->gaia_id_)
return it->token_;
}
return std::string();
}
} // namespace
namespace signin {
OAuthMultiloginHelper::OAuthMultiloginHelper(
SigninClient* signin_client,
OAuth2TokenService* token_service,
const std::vector<std::string>& account_ids,
base::OnceCallback<void(const GoogleServiceAuthError&)> callback)
: signin_client_(signin_client),
token_service_(token_service),
account_ids_(account_ids),
callback_(std::move(callback)),
weak_ptr_factory_(this) {
DCHECK(signin_client_);
DCHECK(token_service_);
DCHECK(!account_ids_.empty());
DCHECK(callback_);
#ifndef NDEBUG
// Check that there is no duplicate accounts.
std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
account_ids_.end());
DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
#endif
StartFetchingTokens();
}
OAuthMultiloginHelper::~OAuthMultiloginHelper() = default;
void OAuthMultiloginHelper::StartFetchingTokens() {
DCHECK(!token_fetcher_);
DCHECK(token_id_pairs_.empty());
token_fetcher_ = std::make_unique<signin::OAuthMultiloginTokenFetcher>(
signin_client_, token_service_, account_ids_,
base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensSuccess,
base::Unretained(this)),
base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensFailure,
base::Unretained(this)));
}
void OAuthMultiloginHelper::OnAccessTokensSuccess(
const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs) {
DCHECK(token_id_pairs_.empty());
token_id_pairs_ = token_id_pairs;
DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
token_fetcher_.reset();
signin_client_->DelayNetworkCall(
base::BindOnce(&OAuthMultiloginHelper::StartFetchingMultiLogin,
weak_ptr_factory_.GetWeakPtr()));
}
void OAuthMultiloginHelper::OnAccessTokensFailure(
const GoogleServiceAuthError& error) {
// Copy the error because it is owned by token_fetcher_.
GoogleServiceAuthError error_copy = error;
token_fetcher_.reset();
std::move(callback_).Run(error_copy);
// Do not add anything below this line, because this may be deleted.
}
void OAuthMultiloginHelper::StartFetchingMultiLogin() {
DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
this, gaia::GaiaSource::kChrome, signin_client_->GetURLLoaderFactory());
gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_);
}
void OAuthMultiloginHelper::OnOAuthMultiloginFinished(
const OAuthMultiloginResult& result) {
RecordMultiloginFinished(result.error());
if (result.error().state() == GoogleServiceAuthError::NONE) {
VLOG(1) << "Multilogin successful accounts="
<< base::JoinString(account_ids_, " ");
StartSettingCookies(result);
return;
}
// If Gaia responded with status: "INVALID_TOKENS", we have to mark tokens as
// invalid.
if (result.error().state() ==
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
for (const std::string& failed_account_id : result.failed_accounts()) {
std::string failed_token =
FindTokenForAccount(token_id_pairs_, failed_account_id);
if (failed_token.empty()) {
LOG(ERROR)
<< "Unexpected failed token for account not present in request: "
<< failed_account_id;
continue;
}
token_service_->InvalidateTokenForMultilogin(failed_account_id,
failed_token);
}
}
// Maybe some access tokens were expired, try to get new ones.
bool is_transient_error =
result.error().IsTransientError() || !result.failed_accounts().empty();
if (is_transient_error && ++fetcher_retries_ < kMaxFetcherRetries) {
UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginRetry", result.error().state(),
GoogleServiceAuthError::NUM_STATES);
token_id_pairs_.clear();
StartFetchingTokens();
return;
}
std::move(callback_).Run(result.error());
// Do not add anything below this line, because this may be deleted.
}
void OAuthMultiloginHelper::StartSettingCookies(
const OAuthMultiloginResult& result) {
DCHECK(cookies_to_set_.empty());
network::mojom::CookieManager* cookie_manager =
signin_client_->GetCookieManager();
const std::vector<net::CanonicalCookie>& cookies = result.cookies();
for (const net::CanonicalCookie& cookie : cookies) {
cookies_to_set_.insert(std::make_pair(cookie.Name(), cookie.Domain()));
}
for (const net::CanonicalCookie& cookie : cookies) {
if (cookies_to_set_.find(std::make_pair(cookie.Name(), cookie.Domain())) !=
cookies_to_set_.end()) {
base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)>
callback = base::BindOnce(&OAuthMultiloginHelper::OnCookieSet,
weak_ptr_factory_.GetWeakPtr(),
cookie.Name(), cookie.Domain());
net::CookieOptions options;
options.set_include_httponly();
// Permit it to set a SameSite cookie if it wants to.
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
cookie_manager->SetCanonicalCookie(
cookie, "https", options,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback), net::CanonicalCookie::CookieInclusionStatus::
EXCLUDE_UNKNOWN_ERROR));
} else {
LOG(ERROR) << "Duplicate cookie found: " << cookie.Name() << " "
<< cookie.Domain();
}
}
}
void OAuthMultiloginHelper::OnCookieSet(
const std::string& cookie_name,
const std::string& cookie_domain,
net::CanonicalCookie::CookieInclusionStatus status) {
cookies_to_set_.erase(std::make_pair(cookie_name, cookie_domain));
bool success =
(status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
if (!success) {
LOG(ERROR) << "Failed to set cookie " << cookie_name
<< " for domain=" << cookie_domain << ".";
}
UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success);
if (cookies_to_set_.empty())
std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone());
// Do not add anything below this line, because this may be deleted.
}
} // namespace signin
// Copyright 2019 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.
#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
class GaiaAuthFetcher;
class OAuth2TokenService;
class SigninClient;
namespace signin {
class OAuthMultiloginTokenFetcher;
// This is a helper class that drives the OAuth multilogin process.
// The main steps are:
// - fetch access tokens with login scope,
// - call the oauth multilogin endpoint,
// - get the cookies from the response body, and set them in the cookie manager.
// It is safe to delete this object from within the callbacks.
class OAuthMultiloginHelper : public GaiaAuthConsumer {
public:
OAuthMultiloginHelper(
SigninClient* signin_client,
OAuth2TokenService* token_service,
const std::vector<std::string>& account_ids,
base::OnceCallback<void(const GoogleServiceAuthError&)> callback);
~OAuthMultiloginHelper() override;
private:
// Starts fetching tokens with OAuthMultiloginTokenFetcher.
void StartFetchingTokens();
// Callbacks for OAuthMultiloginTokenFetcher.
void OnAccessTokensSuccess(
const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
token_id_pairs);
void OnAccessTokensFailure(const GoogleServiceAuthError& error);
// Actual call to the multilogin endpoint.
void StartFetchingMultiLogin();
// Overridden from GaiaAuthConsumer.
void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override;
// Starts setting parsed cookies in browser.
void StartSettingCookies(const OAuthMultiloginResult& result);
// Callback for CookieManager::SetCanonicalCookie.
void OnCookieSet(const std::string& cookie_name,
const std::string& cookie_domain,
net::CanonicalCookie::CookieInclusionStatus status);
SigninClient* signin_client_;
OAuth2TokenService* token_service_;
int fetcher_retries_ = 0;
// Account ids to set in the cookie.
const std::vector<std::string> account_ids_;
// Access tokens, in the same order as the account ids.
std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
base::OnceCallback<void(const GoogleServiceAuthError&)> callback_;
std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
std::unique_ptr<OAuthMultiloginTokenFetcher> token_fetcher_;
// List of pairs (cookie name and cookie domain) that have to be set in
// cookie jar.
std::set<std::pair<std::string, std::string>> cookies_to_set_;
base::WeakPtrFactory<OAuthMultiloginHelper> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginHelper);
};
} // namespace signin
#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_
// Copyright 2019 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 "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
#include <algorithm>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "components/signin/core/browser/signin_client.h"
namespace {
void RecordGetAccessTokenFinished(GoogleServiceAuthError error) {
UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenFinished", error.state(),
GoogleServiceAuthError::NUM_STATES);
}
} // namespace
namespace signin {
OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher(
SigninClient* signin_client,
OAuth2TokenService* token_service,
const std::vector<std::string>& account_ids,
SuccessCallback success_callback,
FailureCallback failure_callback)
: OAuth2TokenService::Consumer("oauth_multilogin_token_fetcher"),
signin_client_(signin_client),
token_service_(token_service),
account_ids_(account_ids),
success_callback_(std::move(success_callback)),
failure_callback_(std::move(failure_callback)),
weak_ptr_factory_(this) {
DCHECK(signin_client_);
DCHECK(token_service_);
DCHECK(!account_ids_.empty());
DCHECK(success_callback_);
DCHECK(failure_callback_);
#ifndef NDEBUG
// Check that there is no duplicate accounts.
std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
account_ids_.end());
DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
#endif
for (const std::string& account_id : account_ids_)
StartFetchingToken(account_id);
}
OAuthMultiloginTokenFetcher::~OAuthMultiloginTokenFetcher() = default;
void OAuthMultiloginTokenFetcher::StartFetchingToken(
const std::string& account_id) {
DCHECK(!account_id.empty());
token_requests_.push_back(
token_service_->StartRequestForMultilogin(account_id, this));
}
void OAuthMultiloginTokenFetcher::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
std::string account_id = request->GetAccountId();
DCHECK(account_ids_.cend() !=
std::find(account_ids_.cbegin(), account_ids_.cend(), account_id));
const std::string token = token_response.access_token;
DCHECK(!token.empty());
// Do not use |request| and |token_response| below this line, as they are
// deleted.
EraseRequest(request);
const auto& inserted =
access_tokens_.insert(std::make_pair(account_id, token));
DCHECK(inserted.second); // If this fires, we have a duplicate account.
if (access_tokens_.size() == account_ids_.size()) {
std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs;
for (const auto& id : account_ids_) {
const auto& it = access_tokens_.find(id);
DCHECK(!it->second.empty());
// TODO(https://crbug.com/956503): Don't assume that the account ID is the
// Gaia ID.
token_id_pairs.emplace_back(id, it->second);
}
RecordGetAccessTokenFinished(GoogleServiceAuthError::AuthErrorNone());
std::move(success_callback_).Run(token_id_pairs);
// Do not add anything below this line, as this may be deleted.
}
}
void OAuthMultiloginTokenFetcher::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
std::string account_id = request->GetAccountId();
VLOG(1) << "Failed to retrieve accesstoken account=" << account_id
<< " error=" << error.ToString();
if (error.IsTransientError() &&
retried_requests_.find(account_id) == retried_requests_.end()) {
retried_requests_.insert(account_id);
UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenRetry", error.state(),
GoogleServiceAuthError::NUM_STATES);
EraseRequest(request);
// Fetching fresh access tokens requires network.
signin_client_->DelayNetworkCall(
base::BindOnce(&OAuthMultiloginTokenFetcher::StartFetchingToken,
weak_ptr_factory_.GetWeakPtr(), account_id));
return;
}
RecordGetAccessTokenFinished(error);
// Copy the error because it is a reference owned by token_requests_.
GoogleServiceAuthError error_copy = error;
token_requests_.clear(); // Cancel pending requests.
std::move(failure_callback_).Run(error_copy);
// Do not add anything below this line, as this may be deleted.
}
void OAuthMultiloginTokenFetcher::EraseRequest(
const OAuth2TokenService::Request* request) {
for (auto it = token_requests_.begin(); it != token_requests_.end(); ++it) {
if (it->get() == request) {
token_requests_.erase(it);
return;
}
}
NOTREACHED() << "Request not found";
}
} // namespace signin
// Copyright 2019 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.
#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/bind_helpers.h"
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_token_service.h"
class SigninClient;
namespace signin {
// Fetches multilogin access tokens in parallel for multiple accounts.
// It is safe to delete this object from within the callbacks.
class OAuthMultiloginTokenFetcher : public OAuth2TokenService::Consumer {
public:
using SuccessCallback = base::OnceCallback<void(
const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&)>;
using FailureCallback =
base::OnceCallback<void(const GoogleServiceAuthError&)>;
OAuthMultiloginTokenFetcher(SigninClient* signin_client,
OAuth2TokenService* token_service,
const std::vector<std::string>& account_ids,
SuccessCallback success_callback,
FailureCallback failure_callback);
~OAuthMultiloginTokenFetcher() override;
private:
void StartFetchingToken(const std::string& account_id);
// Overridden from OAuth2TokenService::Consumer.
void OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) override;
// Helper function to remove a request from token_requests_.
void EraseRequest(const OAuth2TokenService::Request* request);
SigninClient* signin_client_;
OAuth2TokenService* token_service_;
const std::vector<std::string> account_ids_;
SuccessCallback success_callback_;
FailureCallback failure_callback_;
std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_;
std::map<std::string, std::string> access_tokens_;
std::set<std::string> retried_requests_; // Requests are retried once.
base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginTokenFetcher);
};
} // namespace signin
#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_
// Copyright 2019 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 "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
#include <map>
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/test/scoped_task_environment.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "google_apis/gaia/fake_oauth2_token_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace signin {
namespace {
const char kAccountId[] = "account_id";
const char kAccessToken[] = "access_token";
// Status of the token fetch.
enum class FetchStatus { kSuccess, kFailure, kPending };
} // namespace
class OAuthMultiloginTokenFetcherTest : public testing::Test {
public:
OAuthMultiloginTokenFetcherTest() : test_signin_client_(/*prefs=*/nullptr) {}
~OAuthMultiloginTokenFetcherTest() override = default;
std::unique_ptr<OAuthMultiloginTokenFetcher> CreateFetcher(
const std::vector<std::string> account_ids) {
return std::make_unique<OAuthMultiloginTokenFetcher>(
&test_signin_client_, &token_service_, account_ids,
base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnSuccess,
base::Unretained(this)),
base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnFailure,
base::Unretained(this)));
}
// Returns the status of the token fetch.
FetchStatus GetFetchStatus() const {
if (success_callback_called_)
return FetchStatus::kSuccess;
return failure_callback_called_ ? FetchStatus::kFailure
: FetchStatus::kPending;
}
protected:
// Success callback for OAuthMultiloginTokenFetcher.
void OnSuccess(const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
token_id_pairs) {
DCHECK(!success_callback_called_);
DCHECK(token_id_pairs_.empty());
success_callback_called_ = true;
token_id_pairs_ = token_id_pairs;
}
// Failure callback for OAuthMultiloginTokenFetcher.
void OnFailure(const GoogleServiceAuthError& error) {
DCHECK(!failure_callback_called_);
failure_callback_called_ = true;
error_ = error;
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
bool success_callback_called_ = false;
bool failure_callback_called_ = false;
GoogleServiceAuthError error_;
std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
TestSigninClient test_signin_client_;
FakeOAuth2TokenService token_service_;
};
TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) {
token_service_.AddAccount(kAccountId);
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({kAccountId});
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
OAuth2AccessTokenConsumer::TokenResponse success_response;
success_response.access_token = kAccessToken;
token_service_.IssueAllTokensForAccount(kAccountId, success_response);
EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
// Check result.
EXPECT_EQ(1u, token_id_pairs_.size());
EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
}
TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) {
token_service_.AddAccount(kAccountId);
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({kAccountId});
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
token_service_.IssueErrorForAllPendingRequestsForAccount(
kAccountId,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
}
TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) {
token_service_.AddAccount(kAccountId);
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({kAccountId});
// Connection failure will be retried.
token_service_.IssueErrorForAllPendingRequestsForAccount(
kAccountId,
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
// Success on retry.
OAuth2AccessTokenConsumer::TokenResponse success_response;
success_response.access_token = kAccessToken;
token_service_.IssueAllTokensForAccount(kAccountId, success_response);
EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
// Check result.
EXPECT_EQ(1u, token_id_pairs_.size());
EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
}
TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) {
token_service_.AddAccount(kAccountId);
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({kAccountId});
// Repeated connection failures.
token_service_.IssueErrorForAllPendingRequestsForAccount(
kAccountId,
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
token_service_.IssueErrorForAllPendingRequestsForAccount(
kAccountId,
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
// Stop retrying, and fail.
EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error_.state());
}
// The flow succeeds even if requests are received out of order.
TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsSuccess) {
token_service_.AddAccount("account_1");
token_service_.AddAccount("account_2");
token_service_.AddAccount("account_3");
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({"account_1", "account_2", "account_3"});
OAuth2AccessTokenConsumer::TokenResponse success_response;
success_response.access_token = "token_3";
token_service_.IssueAllTokensForAccount("account_3", success_response);
success_response.access_token = "token_1";
token_service_.IssueAllTokensForAccount("account_1", success_response);
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
success_response.access_token = "token_2";
token_service_.IssueAllTokensForAccount("account_2", success_response);
EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
// Check result.
EXPECT_EQ(3u, token_id_pairs_.size());
EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
EXPECT_EQ("token_1", token_id_pairs_[0].token_);
EXPECT_EQ("token_2", token_id_pairs_[1].token_);
EXPECT_EQ("token_3", token_id_pairs_[2].token_);
}
TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsTransientError) {
token_service_.AddAccount("account_1");
token_service_.AddAccount("account_2");
token_service_.AddAccount("account_3");
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({"account_1", "account_2", "account_3"});
// Connection failures will be retried.
token_service_.IssueErrorForAllPendingRequestsForAccount(
"account_1",
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
token_service_.IssueErrorForAllPendingRequestsForAccount(
"account_2",
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
token_service_.IssueErrorForAllPendingRequestsForAccount(
"account_3",
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
// Success on retry.
OAuth2AccessTokenConsumer::TokenResponse success_response;
success_response.access_token = kAccessToken;
success_response.access_token = "token_1";
token_service_.IssueAllTokensForAccount("account_1", success_response);
success_response.access_token = "token_2";
token_service_.IssueAllTokensForAccount("account_2", success_response);
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
success_response.access_token = "token_3";
token_service_.IssueAllTokensForAccount("account_3", success_response);
EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
// Check result.
EXPECT_EQ(3u, token_id_pairs_.size());
EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
EXPECT_EQ("token_1", token_id_pairs_[0].token_);
EXPECT_EQ("token_2", token_id_pairs_[1].token_);
EXPECT_EQ("token_3", token_id_pairs_[2].token_);
}
TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsPersistentError) {
token_service_.AddAccount("account_1");
token_service_.AddAccount("account_2");
token_service_.AddAccount("account_3");
std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
CreateFetcher({"account_1", "account_2", "account_3"});
EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
token_service_.IssueErrorForAllPendingRequestsForAccount(
"account_2",
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
// Fail as soon as one of the accounts is in error.
EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state());
}
} // namespace signin
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "components/signin/core/browser/list_accounts_test_utils.h" #include "components/signin/core/browser/list_accounts_test_utils.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/gaia_urls.h"
...@@ -58,7 +60,12 @@ enum class AccountsCookiesMutatorAction { ...@@ -58,7 +60,12 @@ enum class AccountsCookiesMutatorAction {
namespace identity { namespace identity {
class AccountsCookieMutatorTest : public testing::Test { class AccountsCookieMutatorTest : public testing::Test {
public: public:
AccountsCookieMutatorTest() : identity_test_env_(&test_url_loader_factory_) {} AccountsCookieMutatorTest()
: test_signin_client_(&prefs_),
identity_test_env_(test_url_loader_factory(),
&prefs_,
signin::AccountConsistencyMethod::kDisabled,
&test_signin_client_) {}
~AccountsCookieMutatorTest() override {} ~AccountsCookieMutatorTest() override {}
...@@ -72,7 +79,7 @@ class AccountsCookieMutatorTest : public testing::Test { ...@@ -72,7 +79,7 @@ class AccountsCookieMutatorTest : public testing::Test {
void PrepareURLLoaderResponsesForAction(AccountsCookiesMutatorAction action) { void PrepareURLLoaderResponsesForAction(AccountsCookiesMutatorAction action) {
switch (action) { switch (action) {
case AccountsCookiesMutatorAction::kAddAccountToCookie: case AccountsCookiesMutatorAction::kAddAccountToCookie:
test_url_loader_factory_.AddResponse( test_url_loader_factory()->AddResponse(
GaiaUrls::GetInstance() GaiaUrls::GetInstance()
->oauth1_login_url() ->oauth1_login_url()
.Resolve(base::StringPrintf("?source=%s&issueuberauth=1", .Resolve(base::StringPrintf("?source=%s&issueuberauth=1",
...@@ -80,14 +87,14 @@ class AccountsCookieMutatorTest : public testing::Test { ...@@ -80,14 +87,14 @@ class AccountsCookieMutatorTest : public testing::Test {
.spec(), .spec(),
kTestUberToken, net::HTTP_OK); kTestUberToken, net::HTTP_OK);
test_url_loader_factory_.AddResponse( test_url_loader_factory()->AddResponse(
GaiaUrls::GetInstance() GaiaUrls::GetInstance()
->GetCheckConnectionInfoURLWithSource( ->GetCheckConnectionInfoURLWithSource(
GaiaConstants::kChromeSource) GaiaConstants::kChromeSource)
.spec(), .spec(),
std::string(), net::HTTP_OK); std::string(), net::HTTP_OK);
test_url_loader_factory_.AddResponse( test_url_loader_factory()->AddResponse(
GaiaUrls::GetInstance() GaiaUrls::GetInstance()
->merge_session_url() ->merge_session_url()
.Resolve(base::StringPrintf( .Resolve(base::StringPrintf(
...@@ -97,7 +104,7 @@ class AccountsCookieMutatorTest : public testing::Test { ...@@ -97,7 +104,7 @@ class AccountsCookieMutatorTest : public testing::Test {
std::string(), net::HTTP_OK); std::string(), net::HTTP_OK);
break; break;
case AccountsCookiesMutatorAction::kSetAccountsInCookie: case AccountsCookiesMutatorAction::kSetAccountsInCookie:
test_url_loader_factory_.AddResponse( test_url_loader_factory()->AddResponse(
GaiaUrls::GetInstance() GaiaUrls::GetInstance()
->oauth_multilogin_url() ->oauth_multilogin_url()
.Resolve(base::StringPrintf("?source=%s", .Resolve(base::StringPrintf("?source=%s",
...@@ -106,11 +113,11 @@ class AccountsCookieMutatorTest : public testing::Test { ...@@ -106,11 +113,11 @@ class AccountsCookieMutatorTest : public testing::Test {
std::string(kTestOAuthMultiLoginResponse), net::HTTP_OK); std::string(kTestOAuthMultiLoginResponse), net::HTTP_OK);
break; break;
case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts: case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts:
signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); signin::SetListAccountsResponseNoAccounts(test_url_loader_factory());
break; break;
case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount: case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount:
signin::SetListAccountsResponseOneAccount( signin::SetListAccountsResponseOneAccount(
kTestAccountEmail, kTestAccountGaiaId, &test_url_loader_factory_); kTestAccountEmail, kTestAccountGaiaId, test_url_loader_factory());
break; break;
} }
} }
...@@ -128,12 +135,13 @@ class AccountsCookieMutatorTest : public testing::Test { ...@@ -128,12 +135,13 @@ class AccountsCookieMutatorTest : public testing::Test {
} }
network::TestURLLoaderFactory* test_url_loader_factory() { network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_; return test_signin_client_.test_url_loader_factory();
} }
private: private:
base::test::ScopedTaskEnvironment task_environment_; base::test::ScopedTaskEnvironment task_environment_;
network::TestURLLoaderFactory test_url_loader_factory_; sync_preferences::TestingPrefServiceSyncable prefs_;
TestSigninClient test_signin_client_;
identity::IdentityTestEnvironment identity_test_env_; identity::IdentityTestEnvironment identity_test_env_;
DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorTest); DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorTest);
......
...@@ -353,9 +353,9 @@ class IdentityManagerTest : public testing::Test { ...@@ -353,9 +353,9 @@ class IdentityManagerTest : public testing::Test {
network::mojom::CookieChangeCause::EXPLICIT); network::mojom::CookieChangeCause::EXPLICIT);
} }
void SimulateOAuthMultiloginFinished(GaiaAuthConsumer* consumer, void SimulateOAuthMultiloginFinished(GaiaCookieManagerService* manager,
const OAuthMultiloginResult& result) { const GoogleServiceAuthError& error) {
consumer->OnOAuthMultiloginFinished(result); manager->OnSetAccountsFinished(error);
} }
std::string primary_account_id() { return primary_account_id_; } std::string primary_account_id() { return primary_account_id_; }
...@@ -1813,29 +1813,9 @@ TEST_F(IdentityManagerTest, ...@@ -1813,29 +1813,9 @@ TEST_F(IdentityManagerTest,
identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie( identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
account_ids, gaia::GaiaSource::kChrome, std::move(completion_callback)); account_ids, gaia::GaiaSource::kChrome, std::move(completion_callback));
// Sample success cookie response.
std::string data =
R"()]}'
{
"status": "OK",
"cookies":[
{
"name":"SID",
"value":"vAlUe1",
"domain":".google.ru",
"path":"/",
"isSecure":true,
"isHttpOnly":false,
"priority":"HIGH",
"maxAge":63070000
}
]
}
)";
OAuthMultiloginResult result(data);
SimulateOAuthMultiloginFinished( SimulateOAuthMultiloginFinished(
identity_manager()->GetGaiaCookieManagerService(), result); identity_manager()->GetGaiaCookieManagerService(),
GoogleServiceAuthError::AuthErrorNone());
EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback,
GoogleServiceAuthError::AuthErrorNone()); GoogleServiceAuthError::AuthErrorNone());
...@@ -1861,66 +1841,27 @@ TEST_F(IdentityManagerTest, ...@@ -1861,66 +1841,27 @@ TEST_F(IdentityManagerTest,
// Sample an erroneous response. // Sample an erroneous response.
GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR);
OAuthMultiloginResult result(error);
SimulateOAuthMultiloginFinished( SimulateOAuthMultiloginFinished(
identity_manager()->GetGaiaCookieManagerService(), result); identity_manager()->GetGaiaCookieManagerService(), error);
EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, error); EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, error);
} }
TEST_F(IdentityManagerTest, CallbackSentOnAccountsCookieDeletedByUserAction) { TEST_F(IdentityManagerTest, CallbackSentOnAccountsCookieDeletedByUserAction) {
const char kTestAccountId[] = "account_id";
const char kTestAccountId2[] = "account_id2";
const std::vector<std::string> account_ids = {kTestAccountId,
kTestAccountId2};
// Needed to insert request in the queue.
identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
account_ids, gaia::GaiaSource::kChrome,
GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
// Sample success cookie response.
std::string data =
R"()]}'
{
"status": "OK",
"cookies":[
{
"name":"APISID",
"value":"vAlUe1",
"domain":".google.com",
"path":"/",
"isSecure":true,
"isHttpOnly":false,
"priority":"HIGH",
"maxAge":63070000
}
]
}
)";
OAuthMultiloginResult result(data);
SimulateOAuthMultiloginFinished(
identity_manager()->GetGaiaCookieManagerService(), result);
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop; base::RunLoop run_loop;
identity_manager_observer()->SetOnCookieDeletedByUserCallback( identity_manager_observer()->SetOnCookieDeletedByUserCallback(
run_loop.QuitClosure()); run_loop.QuitClosure());
net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/",
const std::vector<net::CanonicalCookie>& cookies = result.cookies(); base::Time(), base::Time(), base::Time(), false,
false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT);
SimulateCookieDeletedByUser(identity_manager()->GetGaiaCookieManagerService(), SimulateCookieDeletedByUser(identity_manager()->GetGaiaCookieManagerService(),
cookies[0]); cookie);
run_loop.Run(); run_loop.Run();
} }
TEST_F(IdentityManagerTest, OnNetworkInitialized) { TEST_F(IdentityManagerTest, OnNetworkInitialized) {
const char kTestAccountId[] = "account_id";
const char kTestAccountId2[] = "account_id2";
const std::vector<std::string> account_ids = {kTestAccountId,
kTestAccountId2};
auto test_cookie_manager = std::make_unique<network::TestCookieManager>(); auto test_cookie_manager = std::make_unique<network::TestCookieManager>();
network::TestCookieManager* test_cookie_manager_ptr = network::TestCookieManager* test_cookie_manager_ptr =
test_cookie_manager.get(); test_cookie_manager.get();
...@@ -1928,42 +1869,10 @@ TEST_F(IdentityManagerTest, OnNetworkInitialized) { ...@@ -1928,42 +1869,10 @@ TEST_F(IdentityManagerTest, OnNetworkInitialized) {
identity_manager()->OnNetworkInitialized(); identity_manager()->OnNetworkInitialized();
// Needed to insert request in the queue.
identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie(
account_ids, gaia::GaiaSource::kChrome,
GaiaCookieManagerService::SetAccountsInCookieCompletedCallback());
// Sample success cookie response.
std::string data =
R"()]}'
{
"status": "OK",
"cookies":[
{
"name":"APISID",
"value":"vAlUe1",
"domain":".google.com",
"path":"/",
"isSecure":true,
"isHttpOnly":false,
"priority":"HIGH",
"maxAge":63070000
}
]
}
)";
OAuthMultiloginResult result(data);
SimulateOAuthMultiloginFinished(
identity_manager()->GetGaiaCookieManagerService(), result);
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop; base::RunLoop run_loop;
identity_manager_observer()->SetOnCookieDeletedByUserCallback( identity_manager_observer()->SetOnCookieDeletedByUserCallback(
run_loop.QuitClosure()); run_loop.QuitClosure());
const std::vector<net::CanonicalCookie>& cookies = result.cookies();
// Dispatch a known change of a known cookie instance *through the mojo // Dispatch a known change of a known cookie instance *through the mojo
// pipe* in order to ensure the GCMS is listening to CookieManager changes. // pipe* in order to ensure the GCMS is listening to CookieManager changes.
// //
...@@ -1975,8 +1884,12 @@ TEST_F(IdentityManagerTest, OnNetworkInitialized) { ...@@ -1975,8 +1884,12 @@ TEST_F(IdentityManagerTest, OnNetworkInitialized) {
// Note that this call differs from calling SimulateCookieDeletedByUser() // Note that this call differs from calling SimulateCookieDeletedByUser()
// directly in the sense that SimulateCookieDeletedByUser() does not go // directly in the sense that SimulateCookieDeletedByUser() does not go
// through any mojo pipe. // through any mojo pipe.
net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/",
base::Time(), base::Time(), base::Time(), false,
false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT);
test_cookie_manager_ptr->DispatchCookieChange( test_cookie_manager_ptr->DispatchCookieChange(
cookies[0], network::mojom::CookieChangeCause::EXPLICIT); cookie, network::mojom::CookieChangeCause::EXPLICIT);
run_loop.Run(); run_loop.Run();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment