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_api_call_flow.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_status_code.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::ResponseCookies;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLFetcher;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLFetcherDelegate;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLRequestContextGetter;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using net::URLRequestStatus;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kAuthorizationHeaderFormat[] =
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Authorization: Bearer %s";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static std::string MakeAuthorizationHeader(const std::string& auth_token) {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::StringPrintf(kAuthorizationHeaderFormat, auth_token.c_str());
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OAuth2ApiCallFlow::OAuth2ApiCallFlow(
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLRequestContextGetter* context,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& refresh_token,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& access_token,
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<std::string>& scopes)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : context_(context),
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      refresh_token_(refresh_token),
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      access_token_(access_token),
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scopes_(scopes),
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state_(INITIAL),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tried_mint_access_token_(false) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {}
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::Start() {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BeginApiCall();
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::BeginApiCall() {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(state_ == INITIAL || state_ == MINT_ACCESS_TOKEN_DONE);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the access token is empty then directly try to mint one.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (access_token_.empty()) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BeginMintAccessToken();
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = API_CALL_STARTED;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_fetcher_.reset(CreateURLFetcher());
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_fetcher_->Start();  // OnURLFetchComplete will be called.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher* source) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(API_CALL_STARTED, state_);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = API_CALL_DONE;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLRequestStatus status = source->GetStatus();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status.is_success()) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = ERROR_STATE;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProcessApiCallFailure(source);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the response code is 401 Unauthorized then access token may have
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // expired. So try generating a new access token.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we already tried minting a new access token, don't do it again.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tried_mint_access_token_) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state_ = ERROR_STATE;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ProcessApiCallFailure(source);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BeginMintAccessToken();
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (source->GetResponseCode() != net::HTTP_OK &&
946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      source->GetResponseCode() != net::HTTP_NO_CONTENT) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = ERROR_STATE;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProcessApiCallFailure(source);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProcessApiCallSuccess(source);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::BeginMintAccessToken() {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(state_ == INITIAL || state_ == API_CALL_DONE);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(!tried_mint_access_token_);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = MINT_ACCESS_TOKEN_STARTED;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tried_mint_access_token_ = true;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  oauth2_access_token_fetcher_.reset(CreateAccessTokenFetcher());
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  oauth2_access_token_fetcher_->Start(
111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
112a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scopes_);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::EndMintAccessToken(
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GoogleServiceAuthError* error) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(MINT_ACCESS_TOKEN_STARTED, state_);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!error) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = MINT_ACCESS_TOKEN_DONE;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BeginApiCall();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = ERROR_STATE;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProcessMintAccessTokenFailure(*error);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)std::string OAuth2ApiCallFlow::CreateApiCallBodyContentType() {
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return "application/x-www-form-urlencoded";
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OAuth2AccessTokenFetcher* OAuth2ApiCallFlow::CreateAccessTokenFetcher() {
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return new OAuth2AccessTokenFetcherImpl(this, context_, refresh_token_);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::OnURLFetchComplete(const net::URLFetcher* source) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(source);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(API_CALL_STARTED, state_);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndApiCall(source);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::OnGetTokenSuccess(const std::string& access_token,
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const base::Time& expiration_time) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  access_token_ = access_token;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndMintAccessToken(NULL);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OAuth2ApiCallFlow::OnGetTokenFailure(
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GoogleServiceAuthError& error) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndMintAccessToken(&error);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)URLFetcher* OAuth2ApiCallFlow::CreateURLFetcher() {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string body = CreateApiCallBody();
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool empty_body = body.empty();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLFetcher* result = net::URLFetcher::Create(
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateApiCallUrl(),
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      empty_body ? URLFetcher::GET : URLFetcher::POST,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->SetRequestContext(context_);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       net::LOAD_DO_NOT_SAVE_COOKIES);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->AddExtraRequestHeader(MakeAuthorizationHeader(access_token_));
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Fetchers are sometimes cancelled because a network change was detected,
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // especially at startup and after sign-in on ChromeOS. Retrying once should
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // be enough in those cases; let the fetcher retry up to 3 times just in case.
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // http://crbug.com/163710
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result->SetAutomaticallyRetryOnNetworkChanges(3);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!empty_body)
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    result->SetUploadData(CreateApiCallBodyContentType(), body);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
178