| 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 |