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.
5 #include "google_apis/gaia/oauth2_api_call_flow.h"
10 #include "base/basictypes.h"
11 #include "base/strings/stringprintf.h"
12 #include "google_apis/gaia/gaia_urls.h"
13 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
14 #include "net/base/escape.h"
15 #include "net/base/load_flags.h"
16 #include "net/http/http_status_code.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_context_getter.h"
19 #include "net/url_request/url_request_status.h"
21 using net::ResponseCookies
;
22 using net::URLFetcher
;
23 using net::URLFetcherDelegate
;
24 using net::URLRequestContextGetter
;
25 using net::URLRequestStatus
;
28 static const char kAuthorizationHeaderFormat
[] =
29 "Authorization: Bearer %s";
31 static std::string
MakeAuthorizationHeader(const std::string
& auth_token
) {
32 return base::StringPrintf(kAuthorizationHeaderFormat
, auth_token
.c_str());
36 OAuth2ApiCallFlow::OAuth2ApiCallFlow(
37 net::URLRequestContextGetter
* context
,
38 const std::string
& refresh_token
,
39 const std::string
& access_token
,
40 const std::vector
<std::string
>& scopes
)
42 refresh_token_(refresh_token
),
43 access_token_(access_token
),
46 tried_mint_access_token_(false) {
49 OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {}
51 void OAuth2ApiCallFlow::Start() {
55 void OAuth2ApiCallFlow::BeginApiCall() {
56 CHECK(state_
== INITIAL
|| state_
== MINT_ACCESS_TOKEN_DONE
);
58 // If the access token is empty then directly try to mint one.
59 if (access_token_
.empty()) {
60 BeginMintAccessToken();
62 state_
= API_CALL_STARTED
;
63 url_fetcher_
.reset(CreateURLFetcher());
64 url_fetcher_
->Start(); // OnURLFetchComplete will be called.
68 void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher
* source
) {
69 CHECK_EQ(API_CALL_STARTED
, state_
);
70 state_
= API_CALL_DONE
;
72 URLRequestStatus status
= source
->GetStatus();
73 if (!status
.is_success()) {
75 ProcessApiCallFailure(source
);
79 // If the response code is 401 Unauthorized then access token may have
80 // expired. So try generating a new access token.
81 if (source
->GetResponseCode() == net::HTTP_UNAUTHORIZED
) {
82 // If we already tried minting a new access token, don't do it again.
83 if (tried_mint_access_token_
) {
85 ProcessApiCallFailure(source
);
87 BeginMintAccessToken();
93 if (source
->GetResponseCode() != net::HTTP_OK
&&
94 source
->GetResponseCode() != net::HTTP_NO_CONTENT
) {
96 ProcessApiCallFailure(source
);
100 ProcessApiCallSuccess(source
);
103 void OAuth2ApiCallFlow::BeginMintAccessToken() {
104 CHECK(state_
== INITIAL
|| state_
== API_CALL_DONE
);
105 CHECK(!tried_mint_access_token_
);
106 state_
= MINT_ACCESS_TOKEN_STARTED
;
107 tried_mint_access_token_
= true;
109 oauth2_access_token_fetcher_
.reset(CreateAccessTokenFetcher());
110 oauth2_access_token_fetcher_
->Start(
111 GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
112 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
116 void OAuth2ApiCallFlow::EndMintAccessToken(
117 const GoogleServiceAuthError
* error
) {
118 CHECK_EQ(MINT_ACCESS_TOKEN_STARTED
, state_
);
121 state_
= MINT_ACCESS_TOKEN_DONE
;
124 state_
= ERROR_STATE
;
125 ProcessMintAccessTokenFailure(*error
);
129 std::string
OAuth2ApiCallFlow::CreateApiCallBodyContentType() {
130 return "application/x-www-form-urlencoded";
133 OAuth2AccessTokenFetcher
* OAuth2ApiCallFlow::CreateAccessTokenFetcher() {
134 return new OAuth2AccessTokenFetcherImpl(this, context_
, refresh_token_
);
137 void OAuth2ApiCallFlow::OnURLFetchComplete(const net::URLFetcher
* source
) {
139 CHECK_EQ(API_CALL_STARTED
, state_
);
143 void OAuth2ApiCallFlow::OnGetTokenSuccess(const std::string
& access_token
,
144 const base::Time
& expiration_time
) {
145 access_token_
= access_token
;
146 EndMintAccessToken(NULL
);
149 void OAuth2ApiCallFlow::OnGetTokenFailure(
150 const GoogleServiceAuthError
& error
) {
151 EndMintAccessToken(&error
);
154 URLFetcher
* OAuth2ApiCallFlow::CreateURLFetcher() {
155 std::string body
= CreateApiCallBody();
156 bool empty_body
= body
.empty();
157 URLFetcher
* result
= net::URLFetcher::Create(
160 empty_body
? URLFetcher::GET
: URLFetcher::POST
,
163 result
->SetRequestContext(context_
);
164 result
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
165 net::LOAD_DO_NOT_SAVE_COOKIES
);
166 result
->AddExtraRequestHeader(MakeAuthorizationHeader(access_token_
));
167 // Fetchers are sometimes cancelled because a network change was detected,
168 // especially at startup and after sign-in on ChromeOS. Retrying once should
169 // be enough in those cases; let the fetcher retry up to 3 times just in case.
170 // http://crbug.com/163710
171 result
->SetAutomaticallyRetryOnNetworkChanges(3);
174 result
->SetUploadData(CreateApiCallBodyContentType(), body
);