#include "ash/webui/graduation/webview_auth_handler.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/public/cpp/graduation/graduation_manager.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/timer/timer.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/multilogin_parameters.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "net/base/backoff_entry.h"
#include "url/gurl.h"
namespace ash::graduation {
namespace {
constexpr net::BackoffEntry::Policy kRetryBackoffPolicy = {
0,
500,
2.0,
0.1,
60 * 1000,
-1,
true,
};
WebviewAuthHandler::AuthResult GetAuthResultHistogramBucket(
signin::SetAccountsInCookieResult result) {
switch (result) {
case signin::SetAccountsInCookieResult::kSuccess:
return WebviewAuthHandler::AuthResult::kSuccess;
case signin::SetAccountsInCookieResult::kTransientError:
return WebviewAuthHandler::AuthResult::kTransientFailure;
case signin::SetAccountsInCookieResult::kPersistentError:
return WebviewAuthHandler::AuthResult::kPersistentFailure;
}
}
}
WebviewAuthHandler::WebviewAuthHandler(content::BrowserContext* context,
const std::string& webview_host_name)
: context_(context), retry_auth_backoff_(&kRetryBackoffPolicy) {
CHECK(context_);
storage_partition_config_ = content::StoragePartitionConfig::Create(
context_, webview_host_name,
std::string(), true);
}
WebviewAuthHandler::~WebviewAuthHandler() = default;
std::unique_ptr<GaiaAuthFetcher>
WebviewAuthHandler::CreateGaiaAuthFetcherForPartition(
GaiaAuthConsumer* consumer,
const gaia::GaiaSource& source) {
return std::make_unique<GaiaAuthFetcher>(
consumer, source,
GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess());
}
network::mojom::CookieManager*
WebviewAuthHandler::GetCookieManagerForPartition() {
return GetStoragePartition()->GetCookieManagerForBrowserProcess();
}
void WebviewAuthHandler::AuthenticateWebview(OnWebviewAuth callback) {
VLOG(1) << "graduation: webview auth started";
if (cookie_loader_) {
cookie_loader_.reset();
}
retry_auth_timer_.Stop();
signin::IdentityManager* identity_manager =
GraduationManager::Get()->GetIdentityManager(context_);
CHECK(identity_manager);
CoreAccountId primary_account_id =
identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
cookie_loader_ =
identity_manager->GetAccountsCookieMutator()
->SetAccountsInCookieForPartition(
this,
{gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER,
{primary_account_id}},
gaia::GaiaSource::kChromeOS,
base::BindOnce(&WebviewAuthHandler::OnAuthFinished,
base::Unretained(this), std::move(callback)));
}
void WebviewAuthHandler::OnAuthFinished(
OnWebviewAuth callback,
signin::SetAccountsInCookieResult cookie_result) {
VLOG(1) << "graduation: webview auth finished";
base::UmaHistogramEnumeration(kAuthResultHistogramName,
GetAuthResultHistogramBucket(cookie_result));
switch (cookie_result) {
case signin::SetAccountsInCookieResult::kSuccess:
VLOG(1) << "graduation: webview auth successful";
CompleteAuth(std::move(callback), true);
break;
case signin::SetAccountsInCookieResult::kTransientError:
retry_auth_backoff_.InformOfRequest(false);
LOG(WARNING) << "graduation: transient failure in webview auth";
if (retry_auth_backoff_.failure_count() < kMaxRetries) {
RetryAuth(std::move(callback));
} else {
LOG(ERROR) << "graduation: max retries reached on transient failure";
CompleteAuth(std::move(callback), false);
}
break;
case signin::SetAccountsInCookieResult::kPersistentError:
LOG(ERROR) << "graduation: persistent failure in webview auth";
CompleteAuth(std::move(callback), false);
break;
}
}
void WebviewAuthHandler::CompleteAuth(OnWebviewAuth callback, bool is_success) {
cookie_loader_.reset();
retry_auth_backoff_.Reset();
std::move(callback).Run(is_success);
}
void WebviewAuthHandler::RetryAuth(OnWebviewAuth callback) {
base::TimeDelta backoff_delay = retry_auth_backoff_.GetTimeUntilRelease();
VLOG(1) << "graduation: webview auth retry in: "
<< backoff_delay.InMilliseconds() << " ms";
retry_auth_timer_.Start(
FROM_HERE, backoff_delay,
base::BindOnce(&WebviewAuthHandler::AuthenticateWebview,
base::Unretained(this), std::move(callback)));
}
content::StoragePartition* WebviewAuthHandler::GetStoragePartition() {
content::StoragePartition* partition =
GraduationManager::Get()->GetStoragePartition(context_,
storage_partition_config_);
CHECK(partition) << "graduation: invalid storage partition";
return partition;
}
}