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/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"
20 using net::ResponseCookies
;
21 using net::URLFetcher
;
22 using net::URLFetcherDelegate
;
23 using net::URLRequestContextGetter
;
24 using net::URLRequestStatus
;
27 static const char kAuthorizationHeaderFormat
[] =
28 "Authorization: Bearer %s";
30 static std::string
MakeAuthorizationHeader(const std::string
& auth_token
) {
31 return base::StringPrintf(kAuthorizationHeaderFormat
, auth_token
.c_str());
35 OAuth2ApiCallFlow::OAuth2ApiCallFlow(
36 net::URLRequestContextGetter
* context
,
37 const std::string
& refresh_token
,
38 const std::string
& access_token
,
39 const std::vector
<std::string
>& scopes
)
41 refresh_token_(refresh_token
),
42 access_token_(access_token
),
44 chrome_client_id_(GaiaUrls::GetInstance()->oauth2_chrome_client_id()),
45 chrome_client_secret_(
46 GaiaUrls::GetInstance()->oauth2_chrome_client_secret()),
48 tried_mint_access_token_(false) {
51 OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {}
53 void OAuth2ApiCallFlow::Start() {
57 #if defined(OS_CHROMEOS)
58 void OAuth2ApiCallFlow::SetChromeOAuthClientInfo(
59 const std::string
& chrome_client_id
,
60 const std::string
& chrome_client_secret
) {
61 chrome_client_id_
= chrome_client_id
;
62 chrome_client_secret_
= chrome_client_secret
;
66 void OAuth2ApiCallFlow::BeginApiCall() {
67 CHECK(state_
== INITIAL
|| state_
== MINT_ACCESS_TOKEN_DONE
);
69 // If the access token is empty then directly try to mint one.
70 if (access_token_
.empty()) {
71 BeginMintAccessToken();
73 state_
= API_CALL_STARTED
;
74 url_fetcher_
.reset(CreateURLFetcher());
75 url_fetcher_
->Start(); // OnURLFetchComplete will be called.
79 void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher
* source
) {
80 CHECK_EQ(API_CALL_STARTED
, state_
);
81 state_
= API_CALL_DONE
;
83 URLRequestStatus status
= source
->GetStatus();
84 if (!status
.is_success()) {
86 ProcessApiCallFailure(source
);
90 // If the response code is 401 Unauthorized then access token may have
91 // expired. So try generating a new access token.
92 if (source
->GetResponseCode() == net::HTTP_UNAUTHORIZED
) {
93 // If we already tried minting a new access token, don't do it again.
94 if (tried_mint_access_token_
) {
96 ProcessApiCallFailure(source
);
98 BeginMintAccessToken();
104 if (source
->GetResponseCode() != net::HTTP_OK
) {
105 state_
= ERROR_STATE
;
106 ProcessApiCallFailure(source
);
110 ProcessApiCallSuccess(source
);
113 void OAuth2ApiCallFlow::BeginMintAccessToken() {
114 CHECK(state_
== INITIAL
|| state_
== API_CALL_DONE
);
115 CHECK(!tried_mint_access_token_
);
116 state_
= MINT_ACCESS_TOKEN_STARTED
;
117 tried_mint_access_token_
= true;
119 oauth2_access_token_fetcher_
.reset(CreateAccessTokenFetcher());
120 oauth2_access_token_fetcher_
->Start(
122 chrome_client_secret_
,
127 void OAuth2ApiCallFlow::EndMintAccessToken(
128 const GoogleServiceAuthError
* error
) {
129 CHECK_EQ(MINT_ACCESS_TOKEN_STARTED
, state_
);
132 state_
= MINT_ACCESS_TOKEN_DONE
;
135 state_
= ERROR_STATE
;
136 ProcessMintAccessTokenFailure(*error
);
140 OAuth2AccessTokenFetcher
* OAuth2ApiCallFlow::CreateAccessTokenFetcher() {
141 return new OAuth2AccessTokenFetcher(this, context_
);
144 void OAuth2ApiCallFlow::OnURLFetchComplete(const net::URLFetcher
* source
) {
146 CHECK_EQ(API_CALL_STARTED
, state_
);
150 void OAuth2ApiCallFlow::OnGetTokenSuccess(const std::string
& access_token
,
151 const base::Time
& expiration_time
) {
152 access_token_
= access_token
;
153 EndMintAccessToken(NULL
);
156 void OAuth2ApiCallFlow::OnGetTokenFailure(
157 const GoogleServiceAuthError
& error
) {
158 EndMintAccessToken(&error
);
161 URLFetcher
* OAuth2ApiCallFlow::CreateURLFetcher() {
162 std::string body
= CreateApiCallBody();
163 bool empty_body
= body
.empty();
164 URLFetcher
* result
= net::URLFetcher::Create(
167 empty_body
? URLFetcher::GET
: URLFetcher::POST
,
170 result
->SetRequestContext(context_
);
171 result
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
172 net::LOAD_DO_NOT_SAVE_COOKIES
);
173 result
->AddExtraRequestHeader(MakeAuthorizationHeader(access_token_
));
174 // Fetchers are sometimes cancelled because a network change was detected,
175 // especially at startup and after sign-in on ChromeOS. Retrying once should
176 // be enough in those cases; let the fetcher retry up to 3 times just in case.
177 // http://crbug.com/163710
178 result
->SetAutomaticallyRetryOnNetworkChanges(3);
181 result
->SetUploadData("application/x-www-form-urlencoded", body
);