oauth2_access_token_fetcher.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
4a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "google_apis/gaia/oauth2_access_token_fetcher.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <algorithm>
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <string>
9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <vector>
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/json/json_reader.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/string_util.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/stringprintf.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/time.h"
157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/values.h"
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "google_apis/gaia/google_service_auth_error.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/escape.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/load_flags.h"
20a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "net/http/http_status_code.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_fetcher.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_request_status.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::ResponseCookies;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::URLFetcher;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::URLFetcherDelegate;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::URLRequestContextGetter;
297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)using net::URLRequestStatus;
307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace {
32a3f7b4e666c476898878fa745f637129375cd889Ben Murdochstatic const char kGetAccessTokenBodyFormat[] =
33ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    "client_id=%s&"
347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "client_secret=%s&"
357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "grant_type=refresh_token&"
367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "refresh_token=%s";
377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static const char kGetAccessTokenBodyWithScopeFormat[] =
397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "client_id=%s&"
407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "client_secret=%s&"
417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "grant_type=refresh_token&"
427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "refresh_token=%s&"
437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    "scope=%s";
447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static const char kAccessTokenKey[] = "access_token";
467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static const char kExpiresInKey[] = "expires_in";
477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) {
497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  CHECK(!status.is_success());
507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (status.status() == URLRequestStatus::CANCELED) {
517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
527d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  } else {
537d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    DLOG(WARNING) << "Could not reach Google Accounts servers: errno "
547d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                  << status.error();
557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return GoogleServiceAuthError::FromConnectionError(status.error());
567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochstatic URLFetcher* CreateFetcher(URLRequestContextGetter* getter,
607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                 const GURL& url,
617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                 const std::string& body,
627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                 URLFetcherDelegate* delegate) {
637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  bool empty_body = body.empty();
647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  URLFetcher* result = net::URLFetcher::Create(
657d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      0, url,
667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      empty_body ? URLFetcher::GET : URLFetcher::POST,
677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      delegate);
687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  result->SetRequestContext(getter);
707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                       net::LOAD_DO_NOT_SAVE_COOKIES);
727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Fetchers are sometimes cancelled because a network change was detected,
737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // especially at startup and after sign-in on ChromeOS. Retrying once should
74a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // be enough in those cases; let the fetcher retry up to 3 times just in case.
75a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // http://crbug.com/163710
767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  result->SetAutomaticallyRetryOnNetworkChanges(3);
777d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!empty_body)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    result->SetUploadData("application/x-www-form-urlencoded", body);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return result;
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)OAuth2AccessTokenFetcher::OAuth2AccessTokenFetcher(
867d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    OAuth2AccessTokenConsumer* consumer,
877d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    URLRequestContextGetter* getter)
887d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    : consumer_(consumer),
897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      getter_(getter),
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state_(INITIAL) { }
917d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
92ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben MurdochOAuth2AccessTokenFetcher::~OAuth2AccessTokenFetcher() { }
93ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
94ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvoid OAuth2AccessTokenFetcher::CancelRequest() {
95a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  fetcher_.reset();
967d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
97a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OAuth2AccessTokenFetcher::Start(const std::string& client_id,
997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                     const std::string& client_secret,
1007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                     const std::string& refresh_token,
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     const std::vector<std::string>& scopes) {
10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  client_id_ = client_id;
1037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  client_secret_ = client_secret;
1047d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  refresh_token_ = refresh_token;
1057d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  scopes_ = scopes;
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StartGetAccessToken();
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OAuth2AccessTokenFetcher::StartGetAccessToken() {
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK_EQ(INITIAL, state_);
1117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  state_ = GET_ACCESS_TOKEN_STARTED;
1127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  fetcher_.reset(CreateFetcher(
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      getter_,
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MakeGetAccessTokenUrl(),
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MakeGetAccessTokenBody(
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          client_id_, client_secret_, refresh_token_, scopes_),
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this));
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  fetcher_->Start();  // OnURLFetchComplete will be called.
11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
1207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OAuth2AccessTokenFetcher::EndGetAccessToken(
1227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const net::URLFetcher* source) {
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_);
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  state_ = GET_ACCESS_TOKEN_DONE;
1257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  URLRequestStatus status = source->GetStatus();
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!status.is_success()) {
1287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    OnGetTokenFailure(CreateAuthError(status));
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
1337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // '403 Rate Limit Exeeded.'
1347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (source->GetResponseCode() == net::HTTP_FORBIDDEN) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OnGetTokenFailure(GoogleServiceAuthError(
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GoogleServiceAuthError::SERVICE_UNAVAILABLE));
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
1397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // The other errors are treated as permanent error.
1417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (source->GetResponseCode() != net::HTTP_OK) {
142ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    OnGetTokenFailure(GoogleServiceAuthError(
143ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
144ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return;
145ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
146ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
147ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // The request was successfully fetched and it returned OK.
148ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Parse out the access token and the expiration time.
149ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  std::string access_token;
150ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  int expires_in;
151a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  if (!ParseGetAccessTokenResponse(source, &access_token, &expires_in)) {
152a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    DLOG(WARNING) << "Response doesn't match expected format";
153a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    OnGetTokenFailure(
154a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch        GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
155a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    return;
156a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  }
157a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // The token will expire in |expires_in| seconds. Take a 10% error margin to
158a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // prevent reusing a token too close to its expiration date.
159a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  OnGetTokenSuccess(
160a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      access_token,
161a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      base::Time::Now() + base::TimeDelta::FromSeconds(9 * expires_in / 10));
162a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
163a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
164a3f7b4e666c476898878fa745f637129375cd889Ben Murdochvoid OAuth2AccessTokenFetcher::OnGetTokenSuccess(
165a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const std::string& access_token,
166a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const base::Time& expiration_time) {
167a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  consumer_->OnGetTokenSuccess(access_token, expiration_time);
168a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
169a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
170a3f7b4e666c476898878fa745f637129375cd889Ben Murdochvoid OAuth2AccessTokenFetcher::OnGetTokenFailure(
171a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const GoogleServiceAuthError& error) {
172a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  state_ = ERROR_STATE;
173a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  consumer_->OnGetTokenFailure(error);
174a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
175a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
176a3f7b4e666c476898878fa745f637129375cd889Ben Murdochvoid OAuth2AccessTokenFetcher::OnURLFetchComplete(
177a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const net::URLFetcher* source) {
178a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  CHECK(source);
179a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  CHECK(state_ == GET_ACCESS_TOKEN_STARTED);
180a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  EndGetAccessToken(source);
181a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
182a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
183a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch// static
184a3f7b4e666c476898878fa745f637129375cd889Ben MurdochGURL OAuth2AccessTokenFetcher::MakeGetAccessTokenUrl() {
185a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  return GURL(GaiaUrls::GetInstance()->oauth2_token_url());
186a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
187a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
188a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch// static
189a3f7b4e666c476898878fa745f637129375cd889Ben Murdochstd::string OAuth2AccessTokenFetcher::MakeGetAccessTokenBody(
190a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const std::string& client_id,
191a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const std::string& client_secret,
192a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const std::string& refresh_token,
193a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    const std::vector<std::string>& scopes) {
194a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  std::string enc_client_id = net::EscapeUrlEncodedData(client_id, true);
195a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  std::string enc_client_secret =
196a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      net::EscapeUrlEncodedData(client_secret, true);
197a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  std::string enc_refresh_token =
198a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      net::EscapeUrlEncodedData(refresh_token, true);
199a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  if (scopes.empty()) {
200a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    return base::StringPrintf(
201a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch        kGetAccessTokenBodyFormat,
202a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch        enc_client_id.c_str(),
203ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        enc_client_secret.c_str(),
2047d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        enc_refresh_token.c_str());
2057d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  } else {
2067d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    std::string scopes_string = JoinString(scopes, ' ');
2077d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return base::StringPrintf(
2087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        kGetAccessTokenBodyWithScopeFormat,
209bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch        enc_client_id.c_str(),
210bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch        enc_client_secret.c_str(),
211bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch        enc_refresh_token.c_str(),
212bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch        net::EscapeUrlEncodedData(scopes_string, true).c_str());
213bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch  }
2147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
215bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch
216bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static
217bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbool OAuth2AccessTokenFetcher::ParseGetAccessTokenResponse(
218bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch    const net::URLFetcher* source,
2197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    std::string* access_token,
2207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    int* expires_in) {
2217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  CHECK(source);
2227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  CHECK(access_token);
2237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  std::string data;
2247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  source->GetResponseAsString(&data);
2257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  scoped_ptr<base::Value> value(base::JSONReader::Read(data));
2267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
2277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return false;
2287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
2297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
2307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return dict->GetString(kAccessTokenKey, access_token) &&
2317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      dict->GetInteger(kExpiresInKey, expires_in);
2327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
2337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)