1 // Copyright 2013 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 "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/values.h"
12 #include "google_apis/gaia/gaia_constants.h"
13 #include "google_apis/gaia/gaia_oauth_client.h"
14 #include "google_apis/gaia/gaia_urls.h"
15 #include "google_apis/gaia/google_service_auth_error.h"
16 #include "google_apis/gaia/oauth2_api_call_flow.h"
17 #include "google_apis/gaia/oauth2_token_service.h"
18 #include "net/base/escape.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/net_errors.h"
21 #include "net/http/http_status_code.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_request_status.h"
26 using gaia::GaiaOAuthClient
;
27 using GaiaConstants::kChromeSyncManagedOAuth2Scope
;
28 using net::URLFetcher
;
29 using net::URLFetcherDelegate
;
30 using net::URLRequestContextGetter
;
34 const int kNumRetries
= 1;
36 static const char kIssueTokenBodyFormat
[] =
43 static const char kAuthorizationHeaderFormat
[] =
44 "Authorization: Bearer %s";
46 static const char kCodeKey
[] = "code";
48 class ManagedUserRefreshTokenFetcherImpl
49 : public ManagedUserRefreshTokenFetcher
,
50 public OAuth2TokenService::Consumer
,
51 public URLFetcherDelegate
,
52 public GaiaOAuthClient::Delegate
{
54 ManagedUserRefreshTokenFetcherImpl(OAuth2TokenService
* oauth2_token_service
,
55 const std::string
& account_id
,
56 URLRequestContextGetter
* context
);
57 virtual ~ManagedUserRefreshTokenFetcherImpl();
59 // ManagedUserRefreshTokenFetcher implementation:
60 virtual void Start(const std::string
& managed_user_id
,
61 const std::string
& device_name
,
62 const TokenCallback
& callback
) OVERRIDE
;
65 // OAuth2TokenService::Consumer implementation:
66 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
67 const std::string
& access_token
,
68 const Time
& expiration_time
) OVERRIDE
;
69 virtual void OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
70 const GoogleServiceAuthError
& error
) OVERRIDE
;
72 // net::URLFetcherDelegate implementation.
73 virtual void OnURLFetchComplete(const URLFetcher
* source
) OVERRIDE
;
75 // GaiaOAuthClient::Delegate implementation:
76 virtual void OnGetTokensResponse(const std::string
& refresh_token
,
77 const std::string
& access_token
,
78 int expires_in_seconds
) OVERRIDE
;
79 virtual void OnRefreshTokenResponse(const std::string
& access_token
,
80 int expires_in_seconds
) OVERRIDE
;
81 virtual void OnOAuthError() OVERRIDE
;
82 virtual void OnNetworkError(int response_code
) OVERRIDE
;
85 // Requests an access token, which is the first thing we need. This is where
86 // we restart when the returned access token has expired.
89 void DispatchNetworkError(int error_code
);
90 void DispatchGoogleServiceAuthError(const GoogleServiceAuthError
& error
,
91 const std::string
& token
);
92 OAuth2TokenService
* oauth2_token_service_
;
93 std::string account_id_
;
94 URLRequestContextGetter
* context_
;
96 std::string device_name_
;
97 std::string managed_user_id_
;
98 TokenCallback callback_
;
100 scoped_ptr
<OAuth2TokenService::Request
> access_token_request_
;
101 std::string access_token_
;
102 bool access_token_expired_
;
103 scoped_ptr
<URLFetcher
> url_fetcher_
;
104 scoped_ptr
<GaiaOAuthClient
> gaia_oauth_client_
;
107 ManagedUserRefreshTokenFetcherImpl::ManagedUserRefreshTokenFetcherImpl(
108 OAuth2TokenService
* oauth2_token_service
,
109 const std::string
& account_id
,
110 URLRequestContextGetter
* context
)
111 : OAuth2TokenService::Consumer("managed_user"),
112 oauth2_token_service_(oauth2_token_service
),
113 account_id_(account_id
),
115 access_token_expired_(false) {}
117 ManagedUserRefreshTokenFetcherImpl::~ManagedUserRefreshTokenFetcherImpl() {}
119 void ManagedUserRefreshTokenFetcherImpl::Start(
120 const std::string
& managed_user_id
,
121 const std::string
& device_name
,
122 const TokenCallback
& callback
) {
123 DCHECK(callback_
.is_null());
124 managed_user_id_
= managed_user_id
;
125 device_name_
= device_name
;
126 callback_
= callback
;
130 void ManagedUserRefreshTokenFetcherImpl::StartFetching() {
131 OAuth2TokenService::ScopeSet scopes
;
132 scopes
.insert(GaiaUrls::GetInstance()->oauth1_login_scope());
133 access_token_request_
= oauth2_token_service_
->StartRequest(
134 account_id_
, scopes
, this);
137 void ManagedUserRefreshTokenFetcherImpl::OnGetTokenSuccess(
138 const OAuth2TokenService::Request
* request
,
139 const std::string
& access_token
,
140 const Time
& expiration_time
) {
141 DCHECK_EQ(access_token_request_
.get(), request
);
142 access_token_
= access_token
;
144 GURL
url(GaiaUrls::GetInstance()->oauth2_issue_token_url());
145 // GaiaOAuthClient uses id 0, so we use 1 to distinguish the requests in
149 url_fetcher_
.reset(URLFetcher::Create(id
, url
, URLFetcher::POST
, this));
151 url_fetcher_
->SetRequestContext(context_
);
152 url_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
153 net::LOAD_DO_NOT_SAVE_COOKIES
);
154 url_fetcher_
->SetAutomaticallyRetryOnNetworkChanges(kNumRetries
);
155 url_fetcher_
->AddExtraRequestHeader(
156 base::StringPrintf(kAuthorizationHeaderFormat
, access_token
.c_str()));
158 std::string body
= base::StringPrintf(
159 kIssueTokenBodyFormat
,
160 net::EscapeUrlEncodedData(
161 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true).c_str(),
162 net::EscapeUrlEncodedData(kChromeSyncManagedOAuth2Scope
, true).c_str(),
163 net::EscapeUrlEncodedData(managed_user_id_
, true).c_str(),
164 net::EscapeUrlEncodedData(device_name_
, true).c_str());
165 url_fetcher_
->SetUploadData("application/x-www-form-urlencoded", body
);
167 url_fetcher_
->Start();
170 void ManagedUserRefreshTokenFetcherImpl::OnGetTokenFailure(
171 const OAuth2TokenService::Request
* request
,
172 const GoogleServiceAuthError
& error
) {
173 DCHECK_EQ(access_token_request_
.get(), request
);
174 callback_
.Run(error
, std::string());
178 void ManagedUserRefreshTokenFetcherImpl::OnURLFetchComplete(
179 const URLFetcher
* source
) {
180 const net::URLRequestStatus
& status
= source
->GetStatus();
181 if (!status
.is_success()) {
182 DispatchNetworkError(status
.error());
186 int response_code
= source
->GetResponseCode();
187 if (response_code
== net::HTTP_UNAUTHORIZED
&& !access_token_expired_
) {
188 access_token_expired_
= true;
189 oauth2_token_service_
->InvalidateToken(account_id_
,
190 OAuth2TokenService::ScopeSet(),
196 if (response_code
!= net::HTTP_OK
) {
197 // TODO(bauerb): We should return the HTTP response code somehow.
198 DLOG(WARNING
) << "HTTP error " << response_code
;
199 DispatchGoogleServiceAuthError(
200 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED
),
205 std::string response_body
;
206 source
->GetResponseAsString(&response_body
);
207 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response_body
));
208 base::DictionaryValue
* dict
= NULL
;
209 if (!value
.get() || !value
->GetAsDictionary(&dict
)) {
210 DispatchNetworkError(net::ERR_INVALID_RESPONSE
);
213 std::string auth_code
;
214 if (!dict
->GetString(kCodeKey
, &auth_code
)) {
215 DispatchNetworkError(net::ERR_INVALID_RESPONSE
);
219 gaia::OAuthClientInfo client_info
;
220 GaiaUrls
* urls
= GaiaUrls::GetInstance();
221 client_info
.client_id
= urls
->oauth2_chrome_client_id();
222 client_info
.client_secret
= urls
->oauth2_chrome_client_secret();
223 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(context_
));
224 gaia_oauth_client_
->GetTokensFromAuthCode(client_info
, auth_code
, kNumRetries
,
228 void ManagedUserRefreshTokenFetcherImpl::OnGetTokensResponse(
229 const std::string
& refresh_token
,
230 const std::string
& access_token
,
231 int expires_in_seconds
) {
232 // TODO(bauerb): It would be nice if we could pass the access token as well,
233 // so we don't need to fetch another one immediately.
234 DispatchGoogleServiceAuthError(GoogleServiceAuthError::AuthErrorNone(),
238 void ManagedUserRefreshTokenFetcherImpl::OnRefreshTokenResponse(
239 const std::string
& access_token
,
240 int expires_in_seconds
) {
244 void ManagedUserRefreshTokenFetcherImpl::OnOAuthError() {
245 DispatchGoogleServiceAuthError(
246 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED
),
250 void ManagedUserRefreshTokenFetcherImpl::OnNetworkError(int response_code
) {
251 // TODO(bauerb): We should return the HTTP response code somehow.
252 DLOG(WARNING
) << "HTTP error " << response_code
;
253 DispatchGoogleServiceAuthError(
254 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED
),
258 void ManagedUserRefreshTokenFetcherImpl::DispatchNetworkError(int error_code
) {
259 DispatchGoogleServiceAuthError(
260 GoogleServiceAuthError::FromConnectionError(error_code
), std::string());
263 void ManagedUserRefreshTokenFetcherImpl::DispatchGoogleServiceAuthError(
264 const GoogleServiceAuthError
& error
,
265 const std::string
& token
) {
266 callback_
.Run(error
, token
);
273 scoped_ptr
<ManagedUserRefreshTokenFetcher
>
274 ManagedUserRefreshTokenFetcher::Create(OAuth2TokenService
* oauth2_token_service
,
275 const std::string
& account_id
,
276 URLRequestContextGetter
* context
) {
277 scoped_ptr
<ManagedUserRefreshTokenFetcher
> fetcher(
278 new ManagedUserRefreshTokenFetcherImpl(oauth2_token_service
, account_id
,
280 return fetcher
.Pass();
283 ManagedUserRefreshTokenFetcher::~ManagedUserRefreshTokenFetcher() {}