1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <algorithm>
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <string>
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <vector>
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/json/json_reader.h"
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/metrics/histogram.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/metrics/sparse_histogram.h"
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/string_util.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/stringprintf.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/time/time.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/values.h"
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/google_service_auth_error.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/base/escape.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/base/load_flags.h"
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/http/http_status_code.h"
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/url_request/url_request_status.h"
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using net::ResponseCookies;
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using net::URLFetcher;
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using net::URLFetcherDelegate;
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using net::URLRequestContextGetter;
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using net::URLRequestStatus;
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace {
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static const char kGetAccessTokenBodyFormat[] =
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "client_id=%s&"
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "client_secret=%s&"
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "grant_type=refresh_token&"
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "refresh_token=%s";
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static const char kGetAccessTokenBodyWithScopeFormat[] =
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "client_id=%s&"
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "client_secret=%s&"
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "grant_type=refresh_token&"
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "refresh_token=%s&"
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "scope=%s";
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static const char kAccessTokenKey[] = "access_token";
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static const char kExpiresInKey[] = "expires_in";
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static const char kErrorKey[] = "error";
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Enumerated constants for logging server responses on 400 errors, matching
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// RFC 6749.
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)enum OAuth2ErrorCodesForHistogram {
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_INVALID_REQUEST = 0,
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_INVALID_CLIENT,
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_INVALID_GRANT,
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_UNAUTHORIZED_CLIENT,
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_UNSUPPORTED_GRANT_TYPE,
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_INVALID_SCOPE,
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_UNKNOWN,
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OAUTH2_ACCESS_ERROR_COUNT
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)OAuth2ErrorCodesForHistogram OAuth2ErrorToHistogramValue(
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& error) {
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (error == "invalid_request")
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_INVALID_REQUEST;
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (error == "invalid_client")
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_INVALID_CLIENT;
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (error == "invalid_grant")
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_INVALID_GRANT;
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (error == "unauthorized_client")
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_UNAUTHORIZED_CLIENT;
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (error == "unsupported_grant_type")
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_UNSUPPORTED_GRANT_TYPE;
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (error == "invalid_scope")
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return OAUTH2_ACCESS_ERROR_INVALID_SCOPE;
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return OAUTH2_ACCESS_ERROR_UNKNOWN;
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) {
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(!status.is_success());
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (status.status() == URLRequestStatus::CANCELED) {
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DLOG(WARNING) << "Could not reach Google Accounts servers: errno "
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                  << status.error();
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return GoogleServiceAuthError::FromConnectionError(status.error());
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static URLFetcher* CreateFetcher(URLRequestContextGetter* getter,
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 const GURL& url,
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 const std::string& body,
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 URLFetcherDelegate* delegate) {
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool empty_body = body.empty();
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  URLFetcher* result = net::URLFetcher::Create(
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      0, url, empty_body ? URLFetcher::GET : URLFetcher::POST, delegate);
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  result->SetRequestContext(getter);
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                       net::LOAD_DO_NOT_SAVE_COOKIES);
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Fetchers are sometimes cancelled because a network change was detected,
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // especially at startup and after sign-in on ChromeOS. Retrying once should
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // be enough in those cases; let the fetcher retry up to 3 times just in case.
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // http://crbug.com/163710
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  result->SetAutomaticallyRetryOnNetworkChanges(3);
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!empty_body)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    result->SetUploadData("application/x-www-form-urlencoded", body);
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return result;
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)scoped_ptr<base::DictionaryValue> ParseGetAccessTokenResponse(
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const net::URLFetcher* source) {
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(source);
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string data;
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  source->GetResponseAsString(&data);
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_ptr<base::Value> value(base::JSONReader::Read(data));
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    value.reset();
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return scoped_ptr<base::DictionaryValue>(
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      static_cast<base::DictionaryValue*>(value.release()));
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)OAuth2AccessTokenFetcherImpl::OAuth2AccessTokenFetcherImpl(
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    OAuth2AccessTokenConsumer* consumer,
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    net::URLRequestContextGetter* getter,
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& refresh_token)
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : OAuth2AccessTokenFetcher(consumer),
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      getter_(getter),
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      refresh_token_(refresh_token),
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      state_(INITIAL) {}
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)OAuth2AccessTokenFetcherImpl::~OAuth2AccessTokenFetcherImpl() {}
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::CancelRequest() { fetcher_.reset(); }
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::Start(
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_id,
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_secret,
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::vector<std::string>& scopes) {
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  client_id_ = client_id;
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  client_secret_ = client_secret;
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scopes_ = scopes;
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  StartGetAccessToken();
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::StartGetAccessToken() {
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK_EQ(INITIAL, state_);
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = GET_ACCESS_TOKEN_STARTED;
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  fetcher_.reset(
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      CreateFetcher(getter_,
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    MakeGetAccessTokenUrl(),
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    MakeGetAccessTokenBody(
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        client_id_, client_secret_, refresh_token_, scopes_),
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    this));
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  fetcher_->Start();  // OnURLFetchComplete will be called.
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::EndGetAccessToken(
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const net::URLFetcher* source) {
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_);
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = GET_ACCESS_TOKEN_DONE;
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  URLRequestStatus status = source->GetStatus();
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int histogram_value =
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      status.is_success() ? source->GetResponseCode() : status.error();
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UMA_HISTOGRAM_SPARSE_SLOWLY("Gaia.ResponseCodesForOAuth2AccessToken",
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              histogram_value);
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!status.is_success()) {
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    OnGetTokenFailure(CreateAuthError(status));
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  switch (source->GetResponseCode()) {
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case net::HTTP_OK:
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      break;
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case net::HTTP_FORBIDDEN:
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
18703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // '403 Rate Limit Exeeded.'
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      OnGetTokenFailure(
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    case net::HTTP_BAD_REQUEST: {
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // HTTP_BAD_REQUEST (400) usually contains error as per
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // http://tools.ietf.org/html/rfc6749#section-5.2.
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      std::string gaia_error;
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (!ParseGetAccessTokenFailureResponse(source, &gaia_error)) {
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        OnGetTokenFailure(
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return;
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      OAuth2ErrorCodesForHistogram access_error(
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          OAuth2ErrorToHistogramValue(gaia_error));
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      UMA_HISTOGRAM_ENUMERATION("Gaia.BadRequestTypeForOAuth2AccessToken",
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                access_error,
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                OAUTH2_ACCESS_ERROR_COUNT);
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      OnGetTokenFailure(
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          access_error == OAUTH2_ACCESS_ERROR_INVALID_GRANT
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              ? GoogleServiceAuthError(
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              : GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
21403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    default: {
21503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (source->GetResponseCode() >= net::HTTP_INTERNAL_SERVER_ERROR) {
21603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        // 5xx is always treated as transient.
21703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        OnGetTokenFailure(GoogleServiceAuthError(
21803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            GoogleServiceAuthError::SERVICE_UNAVAILABLE));
21903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      } else {
22003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        // The other errors are treated as permanent error.
22103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        DLOG(ERROR) << "Unexpected persistent error: http_status="
22203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    << source->GetResponseCode();
22303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        OnGetTokenFailure(GoogleServiceAuthError(
22403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
22503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
22703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    }
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The request was successfully fetched and it returned OK.
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Parse out the access token and the expiration time.
232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string access_token;
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int expires_in;
234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!ParseGetAccessTokenSuccessResponse(source, &access_token, &expires_in)) {
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DLOG(WARNING) << "Response doesn't match expected format";
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    OnGetTokenFailure(
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The token will expire in |expires_in| seconds. Take a 10% error margin to
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // prevent reusing a token too close to its expiration date.
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OnGetTokenSuccess(
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      access_token,
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Time::Now() + base::TimeDelta::FromSeconds(9 * expires_in / 10));
245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::OnGetTokenSuccess(
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& access_token,
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const base::Time& expiration_time) {
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FireOnGetTokenSuccess(access_token, expiration_time);
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::OnGetTokenFailure(
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const GoogleServiceAuthError& error) {
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = ERROR_STATE;
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FireOnGetTokenFailure(error);
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void OAuth2AccessTokenFetcherImpl::OnURLFetchComplete(
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const net::URLFetcher* source) {
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(source);
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(state_ == GET_ACCESS_TOKEN_STARTED);
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  EndGetAccessToken(source);
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)GURL OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenUrl() {
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return GaiaUrls::GetInstance()->oauth2_token_url();
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_id,
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& client_secret,
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& refresh_token,
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::vector<std::string>& scopes) {
277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string enc_client_id = net::EscapeUrlEncodedData(client_id, true);
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string enc_client_secret =
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      net::EscapeUrlEncodedData(client_secret, true);
280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string enc_refresh_token =
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      net::EscapeUrlEncodedData(refresh_token, true);
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (scopes.empty()) {
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return base::StringPrintf(kGetAccessTokenBodyFormat,
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              enc_client_id.c_str(),
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              enc_client_secret.c_str(),
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                              enc_refresh_token.c_str());
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string scopes_string = JoinString(scopes, ' ');
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return base::StringPrintf(
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        kGetAccessTokenBodyWithScopeFormat,
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        enc_client_id.c_str(),
292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        enc_client_secret.c_str(),
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        enc_refresh_token.c_str(),
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        net::EscapeUrlEncodedData(scopes_string, true).c_str());
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const net::URLFetcher* source,
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string* access_token,
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int* expires_in) {
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(access_token);
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> value = ParseGetAccessTokenResponse(source);
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (value.get() == NULL)
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return value->GetString(kAccessTokenKey, access_token) &&
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         value->GetInteger(kExpiresInKey, expires_in);
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const net::URLFetcher* source,
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string* error) {
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(error);
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> value = ParseGetAccessTokenResponse(source);
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (value.get() == NULL)
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return value->GetString(kErrorKey, error);
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
322