oauth2_access_token_fetcher.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/oauth2_access_token_fetcher.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_reader.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stringprintf.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/time.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/google_service_auth_error.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_status_code.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::ResponseCookies;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLFetcher;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLFetcherDelegate;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLRequestContextGetter;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLRequestStatus;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kGetAccessTokenBodyFormat[] =
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "client_id=%s&"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "client_secret=%s&"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "grant_type=refresh_token&"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "refresh_token=%s";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kGetAccessTokenBodyWithScopeFormat[] =
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "client_id=%s&"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "client_secret=%s&"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "grant_type=refresh_token&"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "refresh_token=%s&"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "scope=%s";
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kAccessTokenKey[] = "access_token";
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kExpiresInKey[] = "expires_in";
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(!status.is_success());
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status.status() == URLRequestStatus::CANCELED) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(WARNING) << "Could not reach Google Accounts servers: errno "
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  << status.error();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return GoogleServiceAuthError::FromConnectionError(status.error());
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static URLFetcher* CreateFetcher(URLRequestContextGetter* getter,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const GURL& url,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const std::string& body,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 URLFetcherDelegate* delegate) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool empty_body = body.empty();
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLFetcher* result = net::URLFetcher::Create(
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0, url,
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      empty_body ? URLFetcher::GET : URLFetcher::POST,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delegate);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->SetRequestContext(getter);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       net::LOAD_DO_NOT_SAVE_COOKIES);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!empty_body)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result->SetUploadData("application/x-www-form-urlencoded", body);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OAuth2AccessTokenFetcher::OAuth2AccessTokenFetcher(
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OAuth2AccessTokenConsumer* consumer,
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    URLRequestContextGetter* getter)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : consumer_(consumer),
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getter_(getter),
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state_(INITIAL) { }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OAuth2AccessTokenFetcher::~OAuth2AccessTokenFetcher() { }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::CancelRequest() {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_.reset();
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::Start(const std::string& client_id,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::string& client_secret,
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::string& refresh_token,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::vector<std::string>& scopes) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  client_id_ = client_id;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  client_secret_ = client_secret;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  refresh_token_ = refresh_token;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scopes_ = scopes;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartGetAccessToken();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::StartGetAccessToken() {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(INITIAL, state_);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = GET_ACCESS_TOKEN_STARTED;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_.reset(CreateFetcher(
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      getter_,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MakeGetAccessTokenUrl(),
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MakeGetAccessTokenBody(
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          client_id_, client_secret_, refresh_token_, scopes_),
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this));
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->Start();  // OnURLFetchComplete will be called.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::EndGetAccessToken(
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = GET_ACCESS_TOKEN_DONE;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLRequestStatus status = source->GetStatus();
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status.is_success()) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnGetTokenFailure(CreateAuthError(status));
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source->GetResponseCode() != net::HTTP_OK) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnGetTokenFailure(GoogleServiceAuthError(
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The request was successfully fetched and it returned OK.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parse out the access token and the expiration time.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string access_token;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int expires_in;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ParseGetAccessTokenResponse(source, &access_token, &expires_in)) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(WARNING) << "Response doesn't match expected format";
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnGetTokenFailure(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The token will expire in |expires_in| seconds. Take a 10% error margin to
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // prevent reusing a token too close to its expiration date.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnGetTokenSuccess(
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      access_token,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Time::Now() + base::TimeDelta::FromSeconds(9 * expires_in / 10));
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::OnGetTokenSuccess(
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& access_token,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::Time& expiration_time) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  consumer_->OnGetTokenSuccess(access_token, expiration_time);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::OnGetTokenFailure(
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GoogleServiceAuthError& error) {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = ERROR_STATE;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  consumer_->OnGetTokenFailure(error);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2AccessTokenFetcher::OnURLFetchComplete(
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(source);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(state_ == GET_ACCESS_TOKEN_STARTED);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndGetAccessToken(source);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL OAuth2AccessTokenFetcher::MakeGetAccessTokenUrl() {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GURL(GaiaUrls::GetInstance()->oauth2_token_url());
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string OAuth2AccessTokenFetcher::MakeGetAccessTokenBody(
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& client_id,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& client_secret,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& refresh_token,
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<std::string>& scopes) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string enc_client_id = net::EscapeUrlEncodedData(client_id, true);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string enc_client_secret =
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::EscapeUrlEncodedData(client_secret, true);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string enc_refresh_token =
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::EscapeUrlEncodedData(refresh_token, true);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (scopes.empty()) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return StringPrintf(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kGetAccessTokenBodyFormat,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_client_id.c_str(),
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_client_secret.c_str(),
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_refresh_token.c_str());
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string scopes_string = JoinString(scopes, ' ');
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return StringPrintf(
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kGetAccessTokenBodyWithScopeFormat,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_client_id.c_str(),
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_client_secret.c_str(),
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enc_refresh_token.c_str(),
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        net::EscapeUrlEncodedData(scopes_string, true).c_str());
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool OAuth2AccessTokenFetcher::ParseGetAccessTokenResponse(
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string* access_token,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* expires_in) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(source);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(access_token);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string data;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source->GetResponseAsString(&data);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<base::Value> value(base::JSONReader::Read(data));
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dict->GetString(kAccessTokenKey, access_token) &&
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dict->GetInteger(kExpiresInKey, expires_in);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
219