Home | History | Annotate | Download | only in drive
      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