• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/message_loop/message_loop_proxy.h"
13 #include "base/metrics/histogram.h"
14 #include "google_apis/drive/auth_service_observer.h"
15 #include "google_apis/gaia/google_service_auth_error.h"
16 #include "net/url_request/url_request_context_getter.h"
17 
18 namespace google_apis {
19 
20 namespace {
21 
22 // Used for success ratio histograms. 0 for failure, 1 for success,
23 // 2 for no connection (likely offline).
24 const int kSuccessRatioHistogramFailure = 0;
25 const int kSuccessRatioHistogramSuccess = 1;
26 const int kSuccessRatioHistogramNoConnection = 2;
27 const int kSuccessRatioHistogramTemporaryFailure = 3;
28 const int kSuccessRatioHistogramMaxValue = 4;  // The max value is exclusive.
29 
30 // OAuth2 authorization token retrieval request.
31 class AuthRequest : public OAuth2TokenService::Consumer {
32  public:
33   AuthRequest(OAuth2TokenService* oauth2_token_service,
34               const std::string& account_id,
35               net::URLRequestContextGetter* url_request_context_getter,
36               const AuthStatusCallback& callback,
37               const std::vector<std::string>& scopes);
38   virtual ~AuthRequest();
39 
40  private:
41   // Overridden from OAuth2TokenService::Consumer:
42   virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
43                                  const std::string& access_token,
44                                  const base::Time& expiration_time) OVERRIDE;
45   virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
46                                  const GoogleServiceAuthError& error) OVERRIDE;
47 
48   AuthStatusCallback callback_;
49   scoped_ptr<OAuth2TokenService::Request> request_;
50   base::ThreadChecker thread_checker_;
51 
52   DISALLOW_COPY_AND_ASSIGN(AuthRequest);
53 };
54 
AuthRequest(OAuth2TokenService * oauth2_token_service,const std::string & account_id,net::URLRequestContextGetter * url_request_context_getter,const AuthStatusCallback & callback,const std::vector<std::string> & scopes)55 AuthRequest::AuthRequest(
56     OAuth2TokenService* oauth2_token_service,
57     const std::string& account_id,
58     net::URLRequestContextGetter* url_request_context_getter,
59     const AuthStatusCallback& callback,
60     const std::vector<std::string>& scopes)
61     : OAuth2TokenService::Consumer("auth_service"),
62       callback_(callback) {
63   DCHECK(!callback_.is_null());
64   request_ = oauth2_token_service->
65       StartRequestWithContext(
66           account_id,
67           url_request_context_getter,
68           OAuth2TokenService::ScopeSet(scopes.begin(), scopes.end()),
69           this);
70 }
71 
~AuthRequest()72 AuthRequest::~AuthRequest() {}
73 
74 // Callback for OAuth2AccessTokenFetcher on success. |access_token| is the token
75 // used to start fetching user data.
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)76 void AuthRequest::OnGetTokenSuccess(const OAuth2TokenService::Request* request,
77                                     const std::string& access_token,
78                                     const base::Time& expiration_time) {
79   DCHECK(thread_checker_.CalledOnValidThread());
80 
81   UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
82                             kSuccessRatioHistogramSuccess,
83                             kSuccessRatioHistogramMaxValue);
84 
85   callback_.Run(HTTP_SUCCESS, access_token);
86   delete this;
87 }
88 
89 // Callback for OAuth2AccessTokenFetcher on failure.
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)90 void AuthRequest::OnGetTokenFailure(const OAuth2TokenService::Request* request,
91                                     const GoogleServiceAuthError& error) {
92   DCHECK(thread_checker_.CalledOnValidThread());
93 
94   LOG(WARNING) << "AuthRequest: token request using refresh token failed: "
95                << error.ToString();
96 
97   // There are many ways to fail, but if the failure is due to connection,
98   // it's likely that the device is off-line. We treat the error differently
99   // so that the file manager works while off-line.
100   if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
101     UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
102                               kSuccessRatioHistogramNoConnection,
103                               kSuccessRatioHistogramMaxValue);
104     callback_.Run(GDATA_NO_CONNECTION, std::string());
105   } else if (error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
106     // Temporary auth error.
107     UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
108                               kSuccessRatioHistogramTemporaryFailure,
109                               kSuccessRatioHistogramMaxValue);
110     callback_.Run(HTTP_FORBIDDEN, std::string());
111   } else {
112     // Permanent auth error.
113     UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
114                               kSuccessRatioHistogramFailure,
115                               kSuccessRatioHistogramMaxValue);
116     callback_.Run(HTTP_UNAUTHORIZED, std::string());
117   }
118   delete this;
119 }
120 
121 }  // namespace
122 
AuthService(OAuth2TokenService * oauth2_token_service,const std::string & account_id,net::URLRequestContextGetter * url_request_context_getter,const std::vector<std::string> & scopes)123 AuthService::AuthService(
124     OAuth2TokenService* oauth2_token_service,
125     const std::string& account_id,
126     net::URLRequestContextGetter* url_request_context_getter,
127     const std::vector<std::string>& scopes)
128     : oauth2_token_service_(oauth2_token_service),
129       account_id_(account_id),
130       url_request_context_getter_(url_request_context_getter),
131       scopes_(scopes),
132       weak_ptr_factory_(this) {
133   DCHECK(oauth2_token_service);
134 
135   // Get OAuth2 refresh token (if we have any) and register for its updates.
136   oauth2_token_service_->AddObserver(this);
137   has_refresh_token_ = oauth2_token_service_->RefreshTokenIsAvailable(
138       account_id_);
139 }
140 
~AuthService()141 AuthService::~AuthService() {
142   oauth2_token_service_->RemoveObserver(this);
143 }
144 
StartAuthentication(const AuthStatusCallback & callback)145 void AuthService::StartAuthentication(const AuthStatusCallback& callback) {
146   DCHECK(thread_checker_.CalledOnValidThread());
147   scoped_refptr<base::MessageLoopProxy> relay_proxy(
148       base::MessageLoopProxy::current());
149 
150   if (HasAccessToken()) {
151     // We already have access token. Give it back to the caller asynchronously.
152     relay_proxy->PostTask(FROM_HERE,
153                           base::Bind(callback, HTTP_SUCCESS, access_token_));
154   } else if (HasRefreshToken()) {
155     // We have refresh token, let's get an access token.
156     new AuthRequest(oauth2_token_service_,
157                     account_id_,
158                     url_request_context_getter_,
159                     base::Bind(&AuthService::OnAuthCompleted,
160                                weak_ptr_factory_.GetWeakPtr(),
161                                callback),
162                     scopes_);
163   } else {
164     relay_proxy->PostTask(FROM_HERE,
165                           base::Bind(callback, GDATA_NOT_READY, std::string()));
166   }
167 }
168 
HasAccessToken() const169 bool AuthService::HasAccessToken() const {
170   return !access_token_.empty();
171 }
172 
HasRefreshToken() const173 bool AuthService::HasRefreshToken() const {
174   return has_refresh_token_;
175 }
176 
access_token() const177 const std::string& AuthService::access_token() const {
178   return access_token_;
179 }
180 
ClearAccessToken()181 void AuthService::ClearAccessToken() {
182   access_token_.clear();
183 }
184 
ClearRefreshToken()185 void AuthService::ClearRefreshToken() {
186   has_refresh_token_ = false;
187 
188   FOR_EACH_OBSERVER(AuthServiceObserver,
189                     observers_,
190                     OnOAuth2RefreshTokenChanged());
191 }
192 
OnAuthCompleted(const AuthStatusCallback & callback,GDataErrorCode error,const std::string & access_token)193 void AuthService::OnAuthCompleted(const AuthStatusCallback& callback,
194                                   GDataErrorCode error,
195                                   const std::string& access_token) {
196   DCHECK(thread_checker_.CalledOnValidThread());
197   DCHECK(!callback.is_null());
198 
199   if (error == HTTP_SUCCESS) {
200     access_token_ = access_token;
201   } else if (error == HTTP_UNAUTHORIZED) {
202     // Refreshing access token using the refresh token is failed with 401 error
203     // (HTTP_UNAUTHORIZED). This means the current refresh token is invalid for
204     // Drive, hence we clear the refresh token here to make HasRefreshToken()
205     // false, thus the invalidness is clearly observable.
206     // This is not for triggering refetch of the refresh token. UI should
207     // show some message to encourage user to log-off and log-in again in order
208     // to fetch new valid refresh token.
209     ClearRefreshToken();
210   }
211 
212   callback.Run(error, access_token);
213 }
214 
AddObserver(AuthServiceObserver * observer)215 void AuthService::AddObserver(AuthServiceObserver* observer) {
216   observers_.AddObserver(observer);
217 }
218 
RemoveObserver(AuthServiceObserver * observer)219 void AuthService::RemoveObserver(AuthServiceObserver* observer) {
220   observers_.RemoveObserver(observer);
221 }
222 
OnRefreshTokenAvailable(const std::string & account_id)223 void AuthService::OnRefreshTokenAvailable(const std::string& account_id) {
224   OnHandleRefreshToken(true);
225 }
226 
OnRefreshTokenRevoked(const std::string & account_id)227 void AuthService::OnRefreshTokenRevoked(const std::string& account_id) {
228   OnHandleRefreshToken(false);
229 }
230 
OnHandleRefreshToken(bool has_refresh_token)231 void AuthService::OnHandleRefreshToken(bool has_refresh_token) {
232   access_token_.clear();
233   has_refresh_token_ = has_refresh_token;
234 
235   FOR_EACH_OBSERVER(AuthServiceObserver,
236                     observers_,
237                     OnOAuth2RefreshTokenChanged());
238 }
239 
240 }  // namespace google_apis
241