blob: 86ae8fb36b75b4c1ad00d7d42919f83526170830 [file] [log] [blame]
// Copyright 2017 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 "services/identity/public/cpp/identity_manager.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace identity {
IdentityManager::IdentityManager(SigninManagerBase* signin_manager,
ProfileOAuth2TokenService* token_service,
AccountTrackerService* account_tracker_service)
: signin_manager_(signin_manager),
token_service_(token_service),
account_tracker_service_(account_tracker_service) {
// Initialize the state of the primary account.
primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
// Initialize the state of accounts with refresh tokens.
// |account_id| is moved into |accounts_with_refresh_tokens_|.
// Do not change this to "const std::string&".
for (std::string account_id : token_service->GetAccounts()) {
AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty());
accounts_with_refresh_tokens_.emplace(std::move(account_id),
std::move(account_info));
}
signin_manager_->AddObserver(this);
#if !defined(OS_CHROMEOS)
SigninManager::FromSigninManagerBase(signin_manager_)
->set_diagnostics_client(this);
#endif
token_service_->AddDiagnosticsObserver(this);
token_service_->set_diagnostics_client(this);
}
IdentityManager::~IdentityManager() {
signin_manager_->RemoveObserver(this);
#if !defined(OS_CHROMEOS)
SigninManager::FromSigninManagerBase(signin_manager_)
->set_diagnostics_client(nullptr);
#endif
token_service_->RemoveDiagnosticsObserver(this);
token_service_->set_diagnostics_client(nullptr);
}
AccountInfo IdentityManager::GetPrimaryAccountInfo() {
#if defined(OS_CHROMEOS)
// On ChromeOS in production, the authenticated account is set very early in
// startup and never changed. Hence, the information held by the
// IdentityManager should always correspond to that held by SigninManager.
// NOTE: the above invariant is not guaranteed to hold in tests. If you
// are seeing this DCHECK go off in a testing context, it means that you need
// to set the IdentityManager's primary account info in the test at the place
// where you are setting the authenticated account info in the SigninManager.
// TODO(blundell): Add the API to do this once we hit the first case and
// document the API to use here.
DCHECK_EQ(signin_manager_->GetAuthenticatedAccountId(),
primary_account_info_.account_id);
// Note: If the primary account's refresh token gets revoked, then the account
// gets removed from AccountTrackerService (via
// AccountFetcherService::OnRefreshTokenRevoked), and so SigninManager's
// GetAuthenticatedAccountInfo is empty (even though
// GetAuthenticatedAccountId is NOT empty).
if (!signin_manager_->GetAuthenticatedAccountInfo().account_id.empty()) {
DCHECK_EQ(signin_manager_->GetAuthenticatedAccountInfo().account_id,
primary_account_info_.account_id);
DCHECK_EQ(signin_manager_->GetAuthenticatedAccountInfo().gaia,
primary_account_info_.gaia);
// TODO(842670): As described in the bug, AccountTrackerService's email
// address can be updated after it is initially set on ChromeOS. Figure out
// right long-term solution for this problem.
if (signin_manager_->GetAuthenticatedAccountInfo().email !=
primary_account_info_.email) {
// This update should only be to move it from normalized form to the form
// in which the user entered the email when creating the account. The
// below check verifies that the normalized forms of the two email
// addresses are identical.
DCHECK(gaia::AreEmailsSame(
signin_manager_->GetAuthenticatedAccountInfo().email,
primary_account_info_.email));
primary_account_info_.email =
signin_manager_->GetAuthenticatedAccountInfo().email;
}
}
#endif // defined(OS_CHROMEOS)
return primary_account_info_;
}
bool IdentityManager::HasPrimaryAccount() {
return !primary_account_info_.account_id.empty();
}
std::vector<AccountInfo> IdentityManager::GetAccountsWithRefreshTokens() {
// TODO(blundell): It seems wasteful to construct this vector every time this
// method is called, but it also seems bad to maintain the vector as an ivar
// along the map.
std::vector<AccountInfo> accounts;
accounts.reserve(accounts_with_refresh_tokens_.size());
for (const auto& pair : accounts_with_refresh_tokens_) {
accounts.push_back(pair.second);
}
return accounts;
}
bool IdentityManager::HasAccountWithRefreshToken(
const std::string& account_id) {
return base::ContainsKey(accounts_with_refresh_tokens_, account_id);
}
bool IdentityManager::HasPrimaryAccountWithRefreshToken() {
return HasAccountWithRefreshToken(GetPrimaryAccountInfo().account_id);
}
std::unique_ptr<AccessTokenFetcher>
IdentityManager::CreateAccessTokenFetcherForAccount(
const std::string& account_id,
const std::string& oauth_consumer_name,
const OAuth2TokenService::ScopeSet& scopes,
AccessTokenFetcher::TokenCallback callback) {
return std::make_unique<AccessTokenFetcher>(account_id, oauth_consumer_name,
token_service_, scopes,
std::move(callback));
}
std::unique_ptr<PrimaryAccountAccessTokenFetcher>
IdentityManager::CreateAccessTokenFetcherForPrimaryAccount(
const std::string& oauth_consumer_name,
const OAuth2TokenService::ScopeSet& scopes,
AccessTokenFetcher::TokenCallback callback,
PrimaryAccountAccessTokenFetcher::Mode mode) {
return std::make_unique<PrimaryAccountAccessTokenFetcher>(
oauth_consumer_name, signin_manager_, token_service_, scopes,
std::move(callback), mode);
}
void IdentityManager::RemoveAccessTokenFromCache(
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes,
const std::string& access_token) {
// TODO(843510): Consider making the request to ProfileOAuth2TokenService
// asynchronously once there are no direct clients of PO2TS. This change would
// need to be made together with changing all callsites to
// ProfileOAuth2TokenService::RequestAccessToken() to be made asynchronously
// as well (to maintain ordering in the case where a client removes an access
// token from the cache and then immediately requests an access token).
token_service_->InvalidateAccessToken(account_id, scopes, access_token);
}
void IdentityManager::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void IdentityManager::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void IdentityManager::AddDiagnosticsObserver(DiagnosticsObserver* observer) {
diagnostics_observer_list_.AddObserver(observer);
}
void IdentityManager::RemoveDiagnosticsObserver(DiagnosticsObserver* observer) {
diagnostics_observer_list_.RemoveObserver(observer);
}
void IdentityManager::SetPrimaryAccountSynchronouslyForTests(
const std::string& gaia_id,
const std::string& email_address,
const std::string& refresh_token) {
DCHECK(!refresh_token.empty());
SetPrimaryAccountSynchronously(gaia_id, email_address, refresh_token);
}
void IdentityManager::SetPrimaryAccountSynchronously(
const std::string& gaia_id,
const std::string& email_address,
const std::string& refresh_token) {
signin_manager_->SetAuthenticatedAccountInfo(gaia_id, email_address);
primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
if (!refresh_token.empty()) {
token_service_->UpdateCredentials(primary_account_info_.account_id,
refresh_token);
}
}
#if !defined(OS_CHROMEOS)
void IdentityManager::WillFireGoogleSigninSucceeded(
const AccountInfo& account_info) {
// TODO(843510): Consider setting this info and notifying observers
// asynchronously in response to GoogleSigninSucceeded() once there are no
// direct clients of SigninManager.
primary_account_info_ = account_info;
}
void IdentityManager::WillFireGoogleSignedOut(const AccountInfo& account_info) {
// TODO(843510): Consider setting this info and notifying observers
// asynchronously in response to GoogleSigninSucceeded() once there are no
// direct clients of SigninManager.
DCHECK(account_info.account_id == primary_account_info_.account_id);
DCHECK(account_info.gaia == primary_account_info_.gaia);
DCHECK(account_info.email == primary_account_info_.email);
primary_account_info_ = AccountInfo();
}
#endif
void IdentityManager::GoogleSigninSucceeded(const AccountInfo& account_info) {
DCHECK(account_info.account_id == primary_account_info_.account_id);
DCHECK(account_info.gaia == primary_account_info_.gaia);
DCHECK(account_info.email == primary_account_info_.email);
for (auto& observer : observer_list_) {
observer.OnPrimaryAccountSet(account_info);
}
}
void IdentityManager::GoogleSignedOut(const AccountInfo& account_info) {
DCHECK(!HasPrimaryAccount());
for (auto& observer : observer_list_) {
observer.OnPrimaryAccountCleared(account_info);
}
}
void IdentityManager::WillFireOnRefreshTokenAvailable(
const std::string& account_id,
bool is_valid) {
AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty());
// Insert the account into |accounts_with_refresh_tokens_|.
auto insertion_result = accounts_with_refresh_tokens_.emplace(
account_id, std::move(account_info));
// The account might have already been present (e.g., this method can fire on
// updating an invalid token to a valid one or vice versa); in this case we
// sanity-check that the cached account info has the expected values.
if (!insertion_result.second) {
AccountInfo cached_account_info = insertion_result.first->second;
DCHECK_EQ(account_info.gaia, cached_account_info.gaia);
DCHECK_EQ(account_info.email, cached_account_info.email);
}
for (auto& observer : observer_list_) {
observer.OnRefreshTokenUpdatedForAccount(account_info, is_valid);
}
}
void IdentityManager::WillFireOnRefreshTokenRevoked(
const std::string& account_id) {
AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty());
auto iterator = accounts_with_refresh_tokens_.find(account_id);
DCHECK(iterator != accounts_with_refresh_tokens_.end());
accounts_with_refresh_tokens_.erase(iterator);
for (auto& observer : observer_list_) {
observer.OnRefreshTokenRemovedForAccount(account_info);
}
}
void IdentityManager::OnAccessTokenRequested(
const std::string& account_id,
const std::string& consumer_id,
const OAuth2TokenService::ScopeSet& scopes) {
// TODO(843510): Consider notifying observers asynchronously once there
// are no direct clients of ProfileOAuth2TokenService.
for (auto& observer : diagnostics_observer_list_) {
observer.OnAccessTokenRequested(account_id, consumer_id, scopes);
}
}
} // namespace identity