OLD | NEW |
| (Empty) |
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 "chrome/browser/google_apis/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 "chrome/browser/google_apis/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 | |
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 : callback_(callback) { | |
62 DCHECK(!callback_.is_null()); | |
63 request_ = oauth2_token_service-> | |
64 StartRequestWithContext( | |
65 account_id, | |
66 url_request_context_getter, | |
67 OAuth2TokenService::ScopeSet(scopes.begin(), scopes.end()), | |
68 this); | |
69 } | |
70 | |
71 AuthRequest::~AuthRequest() {} | |
72 | |
73 // Callback for OAuth2AccessTokenFetcher on success. |access_token| is the token | |
74 // used to start fetching user data. | |
75 void AuthRequest::OnGetTokenSuccess(const OAuth2TokenService::Request* request, | |
76 const std::string& access_token, | |
77 const base::Time& expiration_time) { | |
78 DCHECK(thread_checker_.CalledOnValidThread()); | |
79 | |
80 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess", | |
81 kSuccessRatioHistogramSuccess, | |
82 kSuccessRatioHistogramMaxValue); | |
83 | |
84 callback_.Run(HTTP_SUCCESS, access_token); | |
85 delete this; | |
86 } | |
87 | |
88 // Callback for OAuth2AccessTokenFetcher on failure. | |
89 void AuthRequest::OnGetTokenFailure(const OAuth2TokenService::Request* request, | |
90 const GoogleServiceAuthError& error) { | |
91 DCHECK(thread_checker_.CalledOnValidThread()); | |
92 | |
93 LOG(WARNING) << "AuthRequest: token request using refresh token failed: " | |
94 << error.ToString(); | |
95 | |
96 // There are many ways to fail, but if the failure is due to connection, | |
97 // it's likely that the device is off-line. We treat the error differently | |
98 // so that the file manager works while off-line. | |
99 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) { | |
100 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess", | |
101 kSuccessRatioHistogramNoConnection, | |
102 kSuccessRatioHistogramMaxValue); | |
103 callback_.Run(GDATA_NO_CONNECTION, std::string()); | |
104 } else if (error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) { | |
105 // Temporary auth error. | |
106 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess", | |
107 kSuccessRatioHistogramTemporaryFailure, | |
108 kSuccessRatioHistogramMaxValue); | |
109 callback_.Run(HTTP_FORBIDDEN, std::string()); | |
110 } else { | |
111 // Permanent auth error. | |
112 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess", | |
113 kSuccessRatioHistogramFailure, | |
114 kSuccessRatioHistogramMaxValue); | |
115 callback_.Run(HTTP_UNAUTHORIZED, std::string()); | |
116 } | |
117 delete this; | |
118 } | |
119 | |
120 } // namespace | |
121 | |
122 AuthService::AuthService( | |
123 OAuth2TokenService* oauth2_token_service, | |
124 const std::string& account_id, | |
125 net::URLRequestContextGetter* url_request_context_getter, | |
126 const std::vector<std::string>& scopes) | |
127 : oauth2_token_service_(oauth2_token_service), | |
128 account_id_(account_id), | |
129 url_request_context_getter_(url_request_context_getter), | |
130 scopes_(scopes), | |
131 weak_ptr_factory_(this) { | |
132 DCHECK(oauth2_token_service); | |
133 | |
134 // Get OAuth2 refresh token (if we have any) and register for its updates. | |
135 oauth2_token_service_->AddObserver(this); | |
136 has_refresh_token_ = oauth2_token_service_->RefreshTokenIsAvailable( | |
137 account_id_); | |
138 } | |
139 | |
140 AuthService::~AuthService() { | |
141 oauth2_token_service_->RemoveObserver(this); | |
142 } | |
143 | |
144 void AuthService::StartAuthentication(const AuthStatusCallback& callback) { | |
145 DCHECK(thread_checker_.CalledOnValidThread()); | |
146 scoped_refptr<base::MessageLoopProxy> relay_proxy( | |
147 base::MessageLoopProxy::current()); | |
148 | |
149 if (HasAccessToken()) { | |
150 // We already have access token. Give it back to the caller asynchronously. | |
151 relay_proxy->PostTask(FROM_HERE, | |
152 base::Bind(callback, HTTP_SUCCESS, access_token_)); | |
153 } else if (HasRefreshToken()) { | |
154 // We have refresh token, let's get an access token. | |
155 new AuthRequest(oauth2_token_service_, | |
156 account_id_, | |
157 url_request_context_getter_, | |
158 base::Bind(&AuthService::OnAuthCompleted, | |
159 weak_ptr_factory_.GetWeakPtr(), | |
160 callback), | |
161 scopes_); | |
162 } else { | |
163 relay_proxy->PostTask(FROM_HERE, | |
164 base::Bind(callback, GDATA_NOT_READY, std::string())); | |
165 } | |
166 } | |
167 | |
168 bool AuthService::HasAccessToken() const { | |
169 return !access_token_.empty(); | |
170 } | |
171 | |
172 bool AuthService::HasRefreshToken() const { | |
173 return has_refresh_token_; | |
174 } | |
175 | |
176 const std::string& AuthService::access_token() const { | |
177 return access_token_; | |
178 } | |
179 | |
180 void AuthService::ClearAccessToken() { | |
181 access_token_.clear(); | |
182 } | |
183 | |
184 void AuthService::ClearRefreshToken() { | |
185 has_refresh_token_ = false; | |
186 | |
187 FOR_EACH_OBSERVER(AuthServiceObserver, | |
188 observers_, | |
189 OnOAuth2RefreshTokenChanged()); | |
190 } | |
191 | |
192 void AuthService::OnAuthCompleted(const AuthStatusCallback& callback, | |
193 GDataErrorCode error, | |
194 const std::string& access_token) { | |
195 DCHECK(thread_checker_.CalledOnValidThread()); | |
196 DCHECK(!callback.is_null()); | |
197 | |
198 if (error == HTTP_SUCCESS) { | |
199 access_token_ = access_token; | |
200 } else if (error == HTTP_UNAUTHORIZED) { | |
201 // Refreshing access token using the refresh token is failed with 401 error | |
202 // (HTTP_UNAUTHORIZED). This means the current refresh token is invalid for | |
203 // Drive, hence we clear the refresh token here to make HasRefreshToken() | |
204 // false, thus the invalidness is clearly observable. | |
205 // This is not for triggering refetch of the refresh token. UI should | |
206 // show some message to encourage user to log-off and log-in again in order | |
207 // to fetch new valid refresh token. | |
208 ClearRefreshToken(); | |
209 } | |
210 | |
211 callback.Run(error, access_token); | |
212 } | |
213 | |
214 void AuthService::AddObserver(AuthServiceObserver* observer) { | |
215 observers_.AddObserver(observer); | |
216 } | |
217 | |
218 void AuthService::RemoveObserver(AuthServiceObserver* observer) { | |
219 observers_.RemoveObserver(observer); | |
220 } | |
221 | |
222 void AuthService::OnRefreshTokenAvailable(const std::string& account_id) { | |
223 OnHandleRefreshToken(true); | |
224 } | |
225 | |
226 void AuthService::OnRefreshTokenRevoked(const std::string& account_id) { | |
227 OnHandleRefreshToken(false); | |
228 } | |
229 | |
230 void AuthService::OnHandleRefreshToken(bool has_refresh_token) { | |
231 access_token_.clear(); | |
232 has_refresh_token_ = has_refresh_token; | |
233 | |
234 FOR_EACH_OBSERVER(AuthServiceObserver, | |
235 observers_, | |
236 OnOAuth2RefreshTokenChanged()); | |
237 } | |
238 | |
239 } // namespace google_apis | |
OLD | NEW |