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