blob: d3f4bde4691adb08550491e6c1fce54de32fc132 [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/identity_manager_impl.h"
#include <utility>
#include "base/time/time.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/identity/public/mojom/account.mojom.h"
namespace identity {
IdentityManagerImpl::AccessTokenRequest::AccessTokenRequest(
const std::string& account_id,
const ScopeSet& scopes,
const std::string& consumer_id,
GetAccessTokenCallback consumer_callback,
ProfileOAuth2TokenService* token_service,
IdentityManagerImpl* manager)
: OAuth2TokenService::Consumer(consumer_id),
token_service_(token_service),
consumer_callback_(std::move(consumer_callback)),
manager_(manager) {
token_service_request_ =
token_service_->StartRequest(account_id, scopes, this);
}
IdentityManagerImpl::AccessTokenRequest::~AccessTokenRequest() = default;
void IdentityManagerImpl::AccessTokenRequest::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
OnRequestCompleted(request, token_response.access_token,
token_response.expiration_time,
GoogleServiceAuthError::AuthErrorNone());
}
void IdentityManagerImpl::AccessTokenRequest::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
OnRequestCompleted(request, base::nullopt, base::Time(), error);
}
void IdentityManagerImpl::AccessTokenRequest::OnRequestCompleted(
const OAuth2TokenService::Request* request,
const base::Optional<std::string>& access_token,
base::Time expiration_time,
const GoogleServiceAuthError& error) {
std::move(consumer_callback_).Run(access_token, expiration_time, error);
// Causes |this| to be deleted.
manager_->AccessTokenRequestCompleted(this);
}
// static
void IdentityManagerImpl::Create(mojom::IdentityManagerRequest request,
AccountTrackerService* account_tracker,
SigninManagerBase* signin_manager,
ProfileOAuth2TokenService* token_service) {
new IdentityManagerImpl(std::move(request), account_tracker, signin_manager,
token_service);
}
IdentityManagerImpl::IdentityManagerImpl(
mojom::IdentityManagerRequest request,
AccountTrackerService* account_tracker,
SigninManagerBase* signin_manager,
ProfileOAuth2TokenService* token_service)
: binding_(this, std::move(request)),
account_tracker_(account_tracker),
signin_manager_(signin_manager),
token_service_(token_service) {
signin_manager_shutdown_subscription_ =
signin_manager_->RegisterOnShutdownCallback(
base::BindRepeating(&IdentityManagerImpl::OnSigninManagerShutdown,
base::Unretained(this)));
binding_.set_connection_error_handler(base::BindRepeating(
&IdentityManagerImpl::OnConnectionError, base::Unretained(this)));
token_service_->AddObserver(this);
signin_manager_->AddObserver(this);
}
IdentityManagerImpl::~IdentityManagerImpl() {
token_service_->RemoveObserver(this);
signin_manager_->RemoveObserver(this);
binding_.Close();
}
void IdentityManagerImpl::GetPrimaryAccountInfo(
GetPrimaryAccountInfoCallback callback) {
// It's annoying that this can't be trivially implemented in terms of
// GetAccountInfoFromGaiaId(), but there's no SigninManagerBase method that
// directly returns the authenticated GAIA ID. We can of course get it from
// the AccountInfo but once we have the AccountInfo we ... have the
// AccountInfo.
AccountInfo account_info = signin_manager_->GetAuthenticatedAccountInfo();
AccountState account_state = GetStateOfAccount(account_info);
std::move(callback).Run(account_info, account_state);
}
void IdentityManagerImpl::GetPrimaryAccountWhenAvailable(
GetPrimaryAccountWhenAvailableCallback callback) {
AccountInfo account_info = signin_manager_->GetAuthenticatedAccountInfo();
AccountState account_state = GetStateOfAccount(account_info);
if (!account_state.has_refresh_token ||
token_service_->RefreshTokenHasError(account_info.account_id)) {
primary_account_available_callbacks_.push_back(std::move(callback));
return;
}
DCHECK(!account_info.account_id.empty());
DCHECK(!account_info.email.empty());
DCHECK(!account_info.gaia.empty());
std::move(callback).Run(account_info, account_state);
}
void IdentityManagerImpl::GetAccountInfoFromGaiaId(
const std::string& gaia_id,
GetAccountInfoFromGaiaIdCallback callback) {
AccountInfo account_info = account_tracker_->FindAccountInfoByGaiaId(gaia_id);
AccountState account_state = GetStateOfAccount(account_info);
std::move(callback).Run(account_info, account_state);
}
void IdentityManagerImpl::GetAccounts(GetAccountsCallback callback) {
std::vector<mojom::AccountPtr> accounts;
for (const std::string& account_id : token_service_->GetAccounts()) {
AccountInfo account_info = account_tracker_->GetAccountInfo(account_id);
AccountState account_state = GetStateOfAccount(account_info);
mojom::AccountPtr account =
mojom::Account::New(account_info, account_state);
if (account->state.is_primary_account) {
accounts.insert(accounts.begin(), std::move(account));
} else {
accounts.push_back(std::move(account));
}
}
std::move(callback).Run(std::move(accounts));
}
void IdentityManagerImpl::GetAccessToken(const std::string& account_id,
const ScopeSet& scopes,
const std::string& consumer_id,
GetAccessTokenCallback callback) {
std::unique_ptr<AccessTokenRequest> access_token_request =
std::make_unique<AccessTokenRequest>(account_id, scopes, consumer_id,
std::move(callback), token_service_,
this);
access_token_requests_[access_token_request.get()] =
std::move(access_token_request);
}
void IdentityManagerImpl::OnRefreshTokenAvailable(
const std::string& account_id) {
OnAccountStateChange(account_id);
}
void IdentityManagerImpl::GoogleSigninSucceeded(
const AccountInfo& account_info) {
OnAccountStateChange(account_info.account_id);
}
void IdentityManagerImpl::OnAccountStateChange(const std::string& account_id) {
AccountInfo account_info = account_tracker_->GetAccountInfo(account_id);
AccountState account_state = GetStateOfAccount(account_info);
// Check whether the primary account is available and notify any waiting
// consumers if so.
if (account_state.is_primary_account && account_state.has_refresh_token &&
!token_service_->RefreshTokenHasError(account_info.account_id)) {
DCHECK(!account_info.account_id.empty());
DCHECK(!account_info.email.empty());
DCHECK(!account_info.gaia.empty());
for (auto&& callback : primary_account_available_callbacks_) {
std::move(callback).Run(account_info, account_state);
}
primary_account_available_callbacks_.clear();
}
}
void IdentityManagerImpl::AccessTokenRequestCompleted(
AccessTokenRequest* request) {
access_token_requests_.erase(request);
}
AccountState IdentityManagerImpl::GetStateOfAccount(
const AccountInfo& account_info) {
AccountState account_state;
account_state.has_refresh_token =
token_service_->RefreshTokenIsAvailable(account_info.account_id);
account_state.is_primary_account =
(account_info.account_id == signin_manager_->GetAuthenticatedAccountId());
return account_state;
}
void IdentityManagerImpl::OnSigninManagerShutdown() {
delete this;
}
void IdentityManagerImpl::OnConnectionError() {
delete this;
}
} // namespace identity