| // 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 |