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