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.
5 #include "google_apis/drive/auth_service.h"
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"
18 namespace google_apis
{
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.
30 // OAuth2 authorization token retrieval request.
31 class AuthRequest
: public OAuth2TokenService::Consumer
{
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();
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
;
48 AuthStatusCallback callback_
;
49 scoped_ptr
<OAuth2TokenService::Request
> request_
;
50 base::ThreadChecker thread_checker_
;
52 DISALLOW_COPY_AND_ASSIGN(AuthRequest
);
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 : OAuth2TokenService::Consumer("auth_service"),
63 DCHECK(!callback_
.is_null());
64 request_
= oauth2_token_service
->
65 StartRequestWithContext(
67 url_request_context_getter
,
68 OAuth2TokenService::ScopeSet(scopes
.begin(), scopes
.end()),
72 AuthRequest::~AuthRequest() {}
74 // Callback for OAuth2AccessTokenFetcher on success. |access_token| is the token
75 // used to start fetching user data.
76 void AuthRequest::OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
77 const std::string
& access_token
,
78 const base::Time
& expiration_time
) {
79 DCHECK(thread_checker_
.CalledOnValidThread());
81 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
82 kSuccessRatioHistogramSuccess
,
83 kSuccessRatioHistogramMaxValue
);
85 callback_
.Run(HTTP_SUCCESS
, access_token
);
89 // Callback for OAuth2AccessTokenFetcher on failure.
90 void AuthRequest::OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
91 const GoogleServiceAuthError
& error
) {
92 DCHECK(thread_checker_
.CalledOnValidThread());
94 LOG(WARNING
) << "AuthRequest: token request using refresh token failed: "
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());
112 // Permanent auth error.
113 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
114 kSuccessRatioHistogramFailure
,
115 kSuccessRatioHistogramMaxValue
);
116 callback_
.Run(HTTP_UNAUTHORIZED
, std::string());
123 AuthService::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
),
132 weak_ptr_factory_(this) {
133 DCHECK(oauth2_token_service
);
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(
141 AuthService::~AuthService() {
142 oauth2_token_service_
->RemoveObserver(this);
145 void AuthService::StartAuthentication(const AuthStatusCallback
& callback
) {
146 DCHECK(thread_checker_
.CalledOnValidThread());
147 scoped_refptr
<base::MessageLoopProxy
> relay_proxy(
148 base::MessageLoopProxy::current());
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_
,
158 url_request_context_getter_
,
159 base::Bind(&AuthService::OnAuthCompleted
,
160 weak_ptr_factory_
.GetWeakPtr(),
164 relay_proxy
->PostTask(FROM_HERE
,
165 base::Bind(callback
, GDATA_NOT_READY
, std::string()));
169 bool AuthService::HasAccessToken() const {
170 return !access_token_
.empty();
173 bool AuthService::HasRefreshToken() const {
174 return has_refresh_token_
;
177 const std::string
& AuthService::access_token() const {
178 return access_token_
;
181 void AuthService::ClearAccessToken() {
182 access_token_
.clear();
185 void AuthService::ClearRefreshToken() {
186 has_refresh_token_
= false;
188 FOR_EACH_OBSERVER(AuthServiceObserver
,
190 OnOAuth2RefreshTokenChanged());
193 void AuthService::OnAuthCompleted(const AuthStatusCallback
& callback
,
194 GDataErrorCode error
,
195 const std::string
& access_token
) {
196 DCHECK(thread_checker_
.CalledOnValidThread());
197 DCHECK(!callback
.is_null());
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.
212 callback
.Run(error
, access_token
);
215 void AuthService::AddObserver(AuthServiceObserver
* observer
) {
216 observers_
.AddObserver(observer
);
219 void AuthService::RemoveObserver(AuthServiceObserver
* observer
) {
220 observers_
.RemoveObserver(observer
);
223 void AuthService::OnRefreshTokenAvailable(const std::string
& account_id
) {
224 OnHandleRefreshToken(true);
227 void AuthService::OnRefreshTokenRevoked(const std::string
& account_id
) {
228 OnHandleRefreshToken(false);
231 void AuthService::OnHandleRefreshToken(bool has_refresh_token
) {
232 access_token_
.clear();
233 has_refresh_token_
= has_refresh_token
;
235 FOR_EACH_OBSERVER(AuthServiceObserver
,
237 OnOAuth2RefreshTokenChanged());
240 } // namespace google_apis