1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/common/auth_service.h"
#include <string>
#include <vector>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/task/single_thread_task_runner.h"
#include "components/signin/public/identity_manager/access_token_fetcher.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "google_apis/common/auth_service_observer.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace google_apis {
namespace {
// OAuth2 authorization token retrieval request.
class AuthRequest {
public:
AuthRequest(signin::IdentityManager* identity_manager,
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AuthStatusCallback callback,
const std::vector<std::string>& scopes);
AuthRequest(const AuthRequest&) = delete;
AuthRequest& operator=(const AuthRequest&) = delete;
~AuthRequest();
private:
void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
signin::AccessTokenInfo token_info);
AuthStatusCallback callback_;
std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_;
base::ThreadChecker thread_checker_;
};
AuthRequest::AuthRequest(
signin::IdentityManager* identity_manager,
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AuthStatusCallback callback,
const std::vector<std::string>& scopes)
: callback_(std::move(callback)) {
DCHECK(identity_manager);
DCHECK(callback_);
access_token_fetcher_ = identity_manager->CreateAccessTokenFetcherForAccount(
account_id, "auth_service", url_loader_factory,
signin::ScopeSet(scopes.begin(), scopes.end()),
base::BindOnce(&AuthRequest::OnAccessTokenFetchComplete,
base::Unretained(this)),
signin::AccessTokenFetcher::Mode::kImmediate);
}
AuthRequest::~AuthRequest() {}
void AuthRequest::OnAccessTokenFetchComplete(
GoogleServiceAuthError error,
signin::AccessTokenInfo token_info) {
DCHECK(thread_checker_.CalledOnValidThread());
if (error.state() == GoogleServiceAuthError::NONE) {
std::move(callback_).Run(HTTP_SUCCESS, token_info.token);
} else {
LOG(WARNING) << "AuthRequest: token request using refresh token failed: "
<< error.ToString();
// There are many ways to fail, but if the failure is due to connection,
// it's likely that the device is off-line. We treat the error differently
// so that the file manager works while off-line.
if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
std::move(callback_).Run(NO_CONNECTION, std::string());
} else if (error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
std::move(callback_).Run(HTTP_FORBIDDEN, std::string());
} else {
// Permanent auth error.
std::move(callback_).Run(HTTP_UNAUTHORIZED, std::string());
}
}
delete this;
}
} // namespace
// This class is separate from AuthService itself so that AuthService doesn't
// need a public dependency on signin::IdentityManager::Observer, and therefore
// doesn't need to pull that dependency into all of its client classes.
class AuthService::IdentityManagerObserver
: public signin::IdentityManager::Observer {
public:
explicit IdentityManagerObserver(AuthService* service) : service_(service) {
identity_manager_observation_.Observe(service->identity_manager_.get());
}
~IdentityManagerObserver() override = default;
// signin::IdentityManager::Observer:
void OnRefreshTokenUpdatedForAccount(
const CoreAccountInfo& account_info) override {
service_->OnHandleRefreshToken(account_info.account_id, true);
}
void OnRefreshTokenRemovedForAccount(
const CoreAccountId& account_id) override {
service_->OnHandleRefreshToken(account_id, false);
}
void OnIdentityManagerShutdown(
signin::IdentityManager* identity_manager) override {
identity_manager_observation_.Reset();
}
private:
raw_ptr<AuthService> service_ = nullptr;
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
identity_manager_observation_{this};
};
AuthService::AuthService(
signin::IdentityManager* identity_manager,
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<std::string>& scopes)
: identity_manager_(identity_manager),
identity_manager_observer_(
std::make_unique<IdentityManagerObserver>(this)),
account_id_(account_id),
url_loader_factory_(url_loader_factory),
scopes_(scopes) {
DCHECK(identity_manager_);
has_refresh_token_ =
identity_manager_->HasAccountWithRefreshToken(account_id_);
}
AuthService::~AuthService() = default;
void AuthService::StartAuthentication(AuthStatusCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (HasAccessToken()) {
// We already have access token. Give it back to the caller asynchronously.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), HTTP_SUCCESS, access_token_));
} else if (HasRefreshToken()) {
// We have refresh token, let's get an access token.
new AuthRequest(
identity_manager_, account_id_, url_loader_factory_,
base::BindOnce(&AuthService::OnAuthCompleted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
scopes_);
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), NOT_READY, std::string()));
}
}
bool AuthService::HasAccessToken() const {
return !access_token_.empty();
}
bool AuthService::HasRefreshToken() const {
return has_refresh_token_;
}
const std::string& AuthService::access_token() const {
return access_token_;
}
void AuthService::ClearAccessToken() {
access_token_.clear();
}
void AuthService::ClearRefreshToken() {
OnHandleRefreshToken(account_id_, false);
}
void AuthService::OnAuthCompleted(AuthStatusCallback callback,
ApiErrorCode error,
const std::string& access_token) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(callback);
if (error == HTTP_SUCCESS) {
access_token_ = access_token;
} else if (error == HTTP_UNAUTHORIZED) {
// Refreshing access token using the refresh token is failed with 401 error
// (HTTP_UNAUTHORIZED). This means the current refresh token is invalid for
// the current scope, hence we clear the refresh token here to make
// HasRefreshToken() false, thus the invalidness is clearly observable.
// This is not for triggering refetch of the refresh token. UI should
// show some message to encourage user to log-off and log-in again in order
// to fetch new valid refresh token.
ClearRefreshToken();
}
std::move(callback).Run(error, access_token);
}
void AuthService::AddObserver(AuthServiceObserver* observer) {
observers_.AddObserver(observer);
}
void AuthService::RemoveObserver(AuthServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
void AuthService::OnHandleRefreshToken(const CoreAccountId& account_id,
bool has_refresh_token) {
if (account_id != account_id_)
return;
access_token_.clear();
has_refresh_token_ = has_refresh_token;
for (auto& observer : observers_)
observer.OnOAuth2RefreshTokenChanged();
}
} // namespace google_apis
|