blob: 17dd84d29556cff1e02b889097541ade65ae9b09 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/trusted_vault/trusted_vault_access_token_fetcher_frontend.h"
#include <utility>
#include "base/types/expected.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "components/trusted_vault/trusted_vault_access_token_fetcher.h"
namespace trusted_vault {
namespace {
void FulfillPendingRequests(
std::vector<TrustedVaultAccessTokenFetcher::TokenCallback> pending_requests,
TrustedVaultAccessTokenFetcher::AccessTokenInfoOrError
access_token_or_error) {
for (TrustedVaultAccessTokenFetcher::TokenCallback& pending_request :
pending_requests) {
std::move(pending_request).Run(access_token_or_error);
}
}
} // namespace
TrustedVaultAccessTokenFetcherFrontend::TrustedVaultAccessTokenFetcherFrontend(
signin::IdentityManager* identity_manager)
: identity_manager_(identity_manager) {
DCHECK(identity_manager_);
identity_manager_observation_.Observe(identity_manager_);
UpdatePrimaryAccountIfNeeded();
}
TrustedVaultAccessTokenFetcherFrontend::
~TrustedVaultAccessTokenFetcherFrontend() = default;
base::WeakPtr<TrustedVaultAccessTokenFetcherFrontend>
TrustedVaultAccessTokenFetcherFrontend::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void TrustedVaultAccessTokenFetcherFrontend::FetchAccessToken(
const CoreAccountId& account_id,
TrustedVaultAccessTokenFetcher::TokenCallback callback) {
if (account_id != primary_account_) {
// The requester is likely not aware of a recent change to the primary
// account yet (this is possible because requests come from another
// sequence). Run |callback| immediately without access token.
// Although |account_id| can be in persistent auth error state, it's not
// relevant here since primary account change is the main reason for
// returning nullopt, so passing false is fine.
std::move(callback).Run(base::unexpected(
TrustedVaultAccessTokenFetcher::FetchingError::kNotPrimaryAccount));
return;
}
pending_requests_.emplace_back(std::move(callback));
if (ongoing_access_token_fetch_ == nullptr) {
StartAccessTokenFetch();
}
}
void TrustedVaultAccessTokenFetcherFrontend::OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event) {
UpdatePrimaryAccountIfNeeded();
}
void TrustedVaultAccessTokenFetcherFrontend::OnIdentityManagerShutdown(
signin::IdentityManager* identity_manager) {
CHECK_EQ(identity_manager, identity_manager_);
identity_manager_observation_.Reset();
}
void TrustedVaultAccessTokenFetcherFrontend::UpdatePrimaryAccountIfNeeded() {
CoreAccountInfo primary_account_info =
identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
if (primary_account_info.account_id == primary_account_) {
return;
}
ongoing_access_token_fetch_ = nullptr;
primary_account_ = primary_account_info.account_id;
// Fulfill |pending_requests_| since they belong to the previous
// |primary_account_|.
FulfillPendingRequestsAndMaybeDestroySelf(base::unexpected(
TrustedVaultAccessTokenFetcher::FetchingError::kNotPrimaryAccount));
}
void TrustedVaultAccessTokenFetcherFrontend::StartAccessTokenFetch() {
DCHECK(!ongoing_access_token_fetch_);
// Use kWaitUntilAvailable to avoid failed fetches while refresh tokens are
// still loading. Note, that it doesn't cause infinite waits for persistent
// auth errors.
ongoing_access_token_fetch_ = std::make_unique<
signin::PrimaryAccountAccessTokenFetcher>(
signin::OAuthConsumerId::kTrustedVaultFrontend, identity_manager_,
base::BindOnce(
&TrustedVaultAccessTokenFetcherFrontend::OnAccessTokenFetchCompleted,
base::Unretained(this)),
signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
signin::ConsentLevel::kSignin);
}
void TrustedVaultAccessTokenFetcherFrontend::OnAccessTokenFetchCompleted(
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info) {
ongoing_access_token_fetch_ = nullptr;
if (error.state() == GoogleServiceAuthError::NONE) {
FulfillPendingRequestsAndMaybeDestroySelf(access_token_info);
} else if (error.IsPersistentError()) {
FulfillPendingRequestsAndMaybeDestroySelf(base::unexpected(
TrustedVaultAccessTokenFetcher::FetchingError::kPersistentAuthError));
} else {
FulfillPendingRequestsAndMaybeDestroySelf(base::unexpected(
TrustedVaultAccessTokenFetcher::FetchingError::kTransientAuthError));
}
}
void TrustedVaultAccessTokenFetcherFrontend::
FulfillPendingRequestsAndMaybeDestroySelf(
TrustedVaultAccessTokenFetcher::AccessTokenInfoOrError
access_token_or_error) {
// Move the pending requests to a local variable to avoid UAF in case
// the callbacks synchronously destroy `this`.
// TODO(crbug.com/427316421): The cleaner fix would be to make sure that all
// callbacks run asynchronously (currently it is not the case with
// EnclaveManager if error happens while fetching access token).
std::vector<TrustedVaultAccessTokenFetcher::TokenCallback> pending_requests =
std::move(pending_requests_);
pending_requests_.clear();
FulfillPendingRequests(std::move(pending_requests), access_token_or_error);
}
} // namespace trusted_vault