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