oauth2_api_call_flow.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "google_apis/gaia/oauth2_api_call_flow.h"
6
7#include <string>
8#include <vector>
9
10#include "base/basictypes.h"
11#include "base/stringprintf.h"
12#include "google_apis/gaia/gaia_urls.h"
13#include "net/base/escape.h"
14#include "net/base/load_flags.h"
15#include "net/http/http_status_code.h"
16#include "net/url_request/url_fetcher.h"
17#include "net/url_request/url_request_context_getter.h"
18#include "net/url_request/url_request_status.h"
19
20using net::ResponseCookies;
21using net::URLFetcher;
22using net::URLFetcherDelegate;
23using net::URLRequestContextGetter;
24using net::URLRequestStatus;
25
26namespace {
27static const char kAuthorizationHeaderFormat[] =
28    "Authorization: Bearer %s";
29
30static std::string MakeAuthorizationHeader(const std::string& auth_token) {
31  return StringPrintf(kAuthorizationHeaderFormat, auth_token.c_str());
32}
33}  // namespace
34
35OAuth2ApiCallFlow::OAuth2ApiCallFlow(
36    net::URLRequestContextGetter* context,
37    const std::string& refresh_token,
38    const std::string& access_token,
39    const std::vector<std::string>& scopes)
40    : context_(context),
41      refresh_token_(refresh_token),
42      access_token_(access_token),
43      scopes_(scopes),
44      state_(INITIAL),
45      tried_mint_access_token_(false) {
46}
47
48OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {}
49
50void OAuth2ApiCallFlow::Start() {
51  BeginApiCall();
52}
53
54void OAuth2ApiCallFlow::BeginApiCall() {
55  CHECK(state_ == INITIAL || state_ == MINT_ACCESS_TOKEN_DONE);
56
57  // If the access token is empty then directly try to mint one.
58  if (access_token_.empty()) {
59    BeginMintAccessToken();
60  } else {
61    state_ = API_CALL_STARTED;
62    url_fetcher_.reset(CreateURLFetcher());
63    url_fetcher_->Start();  // OnURLFetchComplete will be called.
64  }
65}
66
67void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher* source) {
68  CHECK_EQ(API_CALL_STARTED, state_);
69  state_ = API_CALL_DONE;
70
71  URLRequestStatus status = source->GetStatus();
72  if (!status.is_success()) {
73    state_ = ERROR_STATE;
74    ProcessApiCallFailure(source);
75    return;
76  }
77
78  // If the response code is 401 Unauthorized then access token may have
79  // expired. So try generating a new access token.
80  if (source->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
81    // If we already tried minting a new access token, don't do it again.
82    if (tried_mint_access_token_) {
83      state_ = ERROR_STATE;
84      ProcessApiCallFailure(source);
85    } else {
86      BeginMintAccessToken();
87    }
88
89    return;
90  }
91
92  if (source->GetResponseCode() != net::HTTP_OK) {
93    state_ = ERROR_STATE;
94    ProcessApiCallFailure(source);
95    return;
96  }
97
98  ProcessApiCallSuccess(source);
99}
100
101void OAuth2ApiCallFlow::BeginMintAccessToken() {
102  CHECK(state_ == INITIAL || state_ == API_CALL_DONE);
103  CHECK(!tried_mint_access_token_);
104  state_ = MINT_ACCESS_TOKEN_STARTED;
105  tried_mint_access_token_ = true;
106
107  oauth2_access_token_fetcher_.reset(CreateAccessTokenFetcher());
108  oauth2_access_token_fetcher_->Start(
109      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
110      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
111      refresh_token_,
112      scopes_);
113}
114
115void OAuth2ApiCallFlow::EndMintAccessToken(
116    const GoogleServiceAuthError* error) {
117  CHECK_EQ(MINT_ACCESS_TOKEN_STARTED, state_);
118
119  if (!error) {
120    state_ = MINT_ACCESS_TOKEN_DONE;
121    BeginApiCall();
122  } else {
123    state_ = ERROR_STATE;
124    ProcessMintAccessTokenFailure(*error);
125  }
126}
127
128OAuth2AccessTokenFetcher* OAuth2ApiCallFlow::CreateAccessTokenFetcher() {
129  return new OAuth2AccessTokenFetcher(this, context_);
130}
131
132void OAuth2ApiCallFlow::OnURLFetchComplete(const net::URLFetcher* source) {
133  CHECK(source);
134  CHECK_EQ(API_CALL_STARTED, state_);
135  EndApiCall(source);
136}
137
138void OAuth2ApiCallFlow::OnGetTokenSuccess(const std::string& access_token,
139                                          const base::Time& expiration_time) {
140  access_token_ = access_token;
141  EndMintAccessToken(NULL);
142}
143
144void OAuth2ApiCallFlow::OnGetTokenFailure(
145    const GoogleServiceAuthError& error) {
146  EndMintAccessToken(&error);
147}
148
149URLFetcher* OAuth2ApiCallFlow::CreateURLFetcher() {
150  std::string body = CreateApiCallBody();
151  bool empty_body = body.empty();
152  URLFetcher* result = net::URLFetcher::Create(
153      0,
154      CreateApiCallUrl(),
155      empty_body ? URLFetcher::GET : URLFetcher::POST,
156      this);
157
158  result->SetRequestContext(context_);
159  result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
160                       net::LOAD_DO_NOT_SAVE_COOKIES);
161  result->AddExtraRequestHeader(MakeAuthorizationHeader(access_token_));
162
163  if (!empty_body)
164    result->SetUploadData("application/x-www-form-urlencoded", body);
165
166  return result;
167}
168