auth_service.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 18namespace google_apis { 19 20namespace { 21 22// Used for success ratio histograms. 0 for failure, 1 for success, 23// 2 for no connection (likely offline). 24const int kSuccessRatioHistogramFailure = 0; 25const int kSuccessRatioHistogramSuccess = 1; 26const int kSuccessRatioHistogramNoConnection = 2; 27const int kSuccessRatioHistogramTemporaryFailure = 3; 28const int kSuccessRatioHistogramMaxValue = 4; // The max value is exclusive. 29 30// OAuth2 authorization token retrieval request. 31class 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 55AuthRequest::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 72AuthRequest::~AuthRequest() {} 73 74// Callback for OAuth2AccessTokenFetcher on success. |access_token| is the token 75// used to start fetching user data. 76void 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. 90void 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 123AuthService::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 141AuthService::~AuthService() { 142 oauth2_token_service_->RemoveObserver(this); 143} 144 145void 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 169bool AuthService::HasAccessToken() const { 170 return !access_token_.empty(); 171} 172 173bool AuthService::HasRefreshToken() const { 174 return has_refresh_token_; 175} 176 177const std::string& AuthService::access_token() const { 178 return access_token_; 179} 180 181void AuthService::ClearAccessToken() { 182 access_token_.clear(); 183} 184 185void AuthService::ClearRefreshToken() { 186 has_refresh_token_ = false; 187 188 FOR_EACH_OBSERVER(AuthServiceObserver, 189 observers_, 190 OnOAuth2RefreshTokenChanged()); 191} 192 193void 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 215void AuthService::AddObserver(AuthServiceObserver* observer) { 216 observers_.AddObserver(observer); 217} 218 219void AuthService::RemoveObserver(AuthServiceObserver* observer) { 220 observers_.RemoveObserver(observer); 221} 222 223void AuthService::OnRefreshTokenAvailable(const std::string& account_id) { 224 OnHandleRefreshToken(true); 225} 226 227void AuthService::OnRefreshTokenRevoked(const std::string& account_id) { 228 OnHandleRefreshToken(false); 229} 230 231void 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