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