blob: a77b672e923ec8f073366653a3640a66a3c5f23b [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/chromeos/oauth2_token_service_delegate.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "chromeos/account_manager/account_manager.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace chromeos {
ChromeOSOAuth2TokenServiceDelegate::ChromeOSOAuth2TokenServiceDelegate(
AccountTrackerService* account_tracker_service,
chromeos::AccountManager* account_manager)
: account_tracker_service_(account_tracker_service),
account_manager_(account_manager),
weak_factory_(this) {
}
ChromeOSOAuth2TokenServiceDelegate::~ChromeOSOAuth2TokenServiceDelegate() {
account_manager_->RemoveObserver(this);
}
OAuth2AccessTokenFetcher*
ChromeOSOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
const std::string& account_id,
net::URLRequestContextGetter* getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
OAuth2AccessTokenConsumer* consumer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state_);
ValidateAccountId(account_id);
const AccountManager::AccountKey& account_key =
MapAccountIdToAccountKey(account_id);
// |OAuth2TokenService| will manage the lifetime of the released pointer.
return account_manager_
->CreateAccessTokenFetcher(account_key, url_loader_factory, consumer)
.release();
}
bool ChromeOSOAuth2TokenServiceDelegate::RefreshTokenIsAvailable(
const std::string& account_id) const {
if (load_credentials_state_ != LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) {
return false;
}
return account_manager_->IsTokenAvailable(
MapAccountIdToAccountKey(account_id));
}
void ChromeOSOAuth2TokenServiceDelegate::UpdateAuthError(
const std::string& account_id,
const GoogleServiceAuthError& error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(sinhak): Implement a backoff policy.
if (error.IsTransientError()) {
return;
}
auto it = errors_.find(account_id);
if (error.state() == GoogleServiceAuthError::NONE) {
if (it != errors_.end()) {
errors_.erase(it);
FireAuthErrorChanged(account_id, error);
}
} else if ((it == errors_.end()) || (it->second != error)) {
errors_[account_id] = error;
FireAuthErrorChanged(account_id, error);
}
}
GoogleServiceAuthError ChromeOSOAuth2TokenServiceDelegate::GetAuthError(
const std::string& account_id) const {
auto it = errors_.find(account_id);
if (it != errors_.end()) {
return it->second;
}
return GoogleServiceAuthError::AuthErrorNone();
}
std::vector<std::string> ChromeOSOAuth2TokenServiceDelegate::GetAccounts() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state_);
std::vector<std::string> accounts;
for (auto& account_key : account_keys_) {
std::string account_id = MapAccountKeyToAccountId(account_key);
if (!account_id.empty()) {
accounts.emplace_back(account_id);
}
}
return accounts;
}
void ChromeOSOAuth2TokenServiceDelegate::LoadCredentials(
const std::string& primary_account_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (load_credentials_state_ != LOAD_CREDENTIALS_NOT_STARTED) {
return;
}
load_credentials_state_ = LOAD_CREDENTIALS_IN_PROGRESS;
DCHECK(account_manager_);
account_manager_->AddObserver(this);
account_manager_->GetAccounts(
base::BindOnce(&ChromeOSOAuth2TokenServiceDelegate::GetAccountsCallback,
weak_factory_.GetWeakPtr()));
}
void ChromeOSOAuth2TokenServiceDelegate::UpdateCredentials(
const std::string& account_id,
const std::string& refresh_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state_);
DCHECK(!account_id.empty());
DCHECK(!refresh_token.empty());
ValidateAccountId(account_id);
const AccountManager::AccountKey& account_key =
MapAccountIdToAccountKey(account_id);
// Will result in AccountManager calling
// |ChromeOSOAuth2TokenServiceDelegate::OnTokenUpserted|.
account_manager_->UpsertToken(account_key, refresh_token);
}
net::URLRequestContextGetter*
ChromeOSOAuth2TokenServiceDelegate::GetRequestContext() const {
return account_manager_->GetUrlRequestContext();
}
OAuth2TokenServiceDelegate::LoadCredentialsState
ChromeOSOAuth2TokenServiceDelegate::GetLoadCredentialsState() const {
return load_credentials_state_;
}
void ChromeOSOAuth2TokenServiceDelegate::GetAccountsCallback(
std::vector<AccountManager::AccountKey> account_keys) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This callback should only be triggered during |LoadCredentials|, which
// implies that |load_credentials_state_| should in
// |LOAD_CREDENTIALS_IN_PROGRESS| state.
DCHECK_EQ(LOAD_CREDENTIALS_IN_PROGRESS, load_credentials_state_);
load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS;
// The typical order of |OAuth2TokenService::Observer| callbacks is:
// 1. OnStartBatchChanges
// 2. OnRefreshTokenAvailable
// 3. OnEndBatchChanges
// 4. OnRefreshTokensLoaded
{
ScopedBatchChange batch(this);
for (const auto& account_key : account_keys) {
OnTokenUpserted(account_key);
}
}
FireRefreshTokensLoaded();
}
std::string ChromeOSOAuth2TokenServiceDelegate::MapAccountKeyToAccountId(
const AccountManager::AccountKey& account_key) const {
DCHECK(account_key.IsValid());
if (account_key.account_type !=
account_manager::AccountType::ACCOUNT_TYPE_GAIA) {
return std::string();
}
const std::string& account_id =
account_tracker_service_->FindAccountInfoByGaiaId(account_key.id)
.account_id;
DCHECK(!account_id.empty()) << "Can't find account id";
return account_id;
}
AccountManager::AccountKey
ChromeOSOAuth2TokenServiceDelegate::MapAccountIdToAccountKey(
const std::string& account_id) const {
DCHECK(!account_id.empty());
const AccountInfo& account_info =
account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.gaia.empty()) << "Can't find account info";
return AccountManager::AccountKey{
account_info.gaia, account_manager::AccountType::ACCOUNT_TYPE_GAIA};
}
void ChromeOSOAuth2TokenServiceDelegate::OnTokenUpserted(
const AccountManager::AccountKey& account_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
account_keys_.insert(account_key);
std::string account_id = MapAccountKeyToAccountId(account_key);
if (account_id.empty()) {
return;
}
ScopedBatchChange batch(this);
FireRefreshTokenAvailable(account_id);
// We cannot directly use |UpdateAuthError| because it does not invoke
// |FireAuthErrorChanged| if |account_id|'s error state was already
// |GoogleServiceAuthError::State::NONE|, but |FireAuthErrorChanged| must be
// invoked here, regardless. See the comment below.
errors_.erase(account_id);
// See |OAuth2TokenService::Observer::OnAuthErrorChanged|.
// |OnAuthErrorChanged| must be always called after
// |OnRefreshTokenAvailable|, when refresh token is updated.
FireAuthErrorChanged(account_id, GoogleServiceAuthError::AuthErrorNone());
}
void ChromeOSOAuth2TokenServiceDelegate::OnAccountRemoved(
const AccountManager::AccountKey& account_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state_);
auto it = account_keys_.find(account_key);
if (it == account_keys_.end()) {
return;
}
account_keys_.erase(it);
std::string account_id = MapAccountKeyToAccountId(account_key);
if (account_id.empty()) {
return;
}
ScopedBatchChange batch(this);
// ProfileOAuth2TokenService will clear its cache for |account_id| when this
// is called. See |ProfileOAuth2TokenService::OnRefreshTokenRevoked|.
FireRefreshTokenRevoked(account_id);
}
void ChromeOSOAuth2TokenServiceDelegate::RevokeCredentials(
const std::string& account_id) {
// Signing out of Chrome is not possible on Chrome OS.
NOTREACHED();
}
void ChromeOSOAuth2TokenServiceDelegate::RevokeAllCredentials() {
// Signing out of Chrome is not possible on Chrome OS.
NOTREACHED();
}
} // namespace chromeos