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