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/bind.h"
11 #include "base/strings/stringprintf.h"
12 #include "google_apis/gaia/gaia_auth_util.h"
13 #include "google_apis/gaia/gaia_urls.h"
14 #include "net/base/escape.h"
15 #include "net/base/load_flags.h"
16 #include "net/http/http_status_code.h"
17 #include "services/network/public/cpp/resource_request.h"
18 #include "services/network/public/cpp/shared_url_loader_factory.h"
19 #include "services/network/public/cpp/simple_url_loader.h"
20 #include "services/network/public/mojom/url_response_head.mojom.h"
21
22 namespace {
23 static const char kAuthorizationValueFormat[] = "Bearer %s";
24
MakeAuthorizationValue(const std::string & auth_token)25 static std::string MakeAuthorizationValue(const std::string& auth_token) {
26 return base::StringPrintf(kAuthorizationValueFormat, auth_token.c_str());
27 }
28 } // namespace
29
OAuth2ApiCallFlow()30 OAuth2ApiCallFlow::OAuth2ApiCallFlow() : state_(INITIAL) {
31 }
32
~OAuth2ApiCallFlow()33 OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {}
34
Start(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,const std::string & access_token)35 void OAuth2ApiCallFlow::Start(
36 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
37 const std::string& access_token) {
38 CHECK(state_ == INITIAL);
39 state_ = API_CALL_STARTED;
40
41 url_loader_ = CreateURLLoader(access_token);
42 url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
43 url_loader_factory.get(),
44 base::BindOnce(&OAuth2ApiCallFlow::OnURLLoadComplete,
45 base::Unretained(this)));
46 }
47
EndApiCall(std::unique_ptr<std::string> body)48 void OAuth2ApiCallFlow::EndApiCall(std::unique_ptr<std::string> body) {
49 CHECK_EQ(API_CALL_STARTED, state_);
50 std::unique_ptr<network::SimpleURLLoader> source = std::move(url_loader_);
51
52 int status_code = 0;
53 if (source->ResponseInfo() && source->ResponseInfo()->headers)
54 status_code = source->ResponseInfo()->headers->response_code();
55 if (source->NetError() != net::OK ||
56 (status_code != net::HTTP_OK && status_code != net::HTTP_NO_CONTENT)) {
57 state_ = ERROR_STATE;
58 ProcessApiCallFailure(source->NetError(), source->ResponseInfo(),
59 std::move(body));
60 } else {
61 state_ = API_CALL_DONE;
62 ProcessApiCallSuccess(source->ResponseInfo(), std::move(body));
63 }
64 }
65
CreateApiCallBodyContentType()66 std::string OAuth2ApiCallFlow::CreateApiCallBodyContentType() {
67 return "application/x-www-form-urlencoded";
68 }
69
GetRequestTypeForBody(const std::string & body)70 std::string OAuth2ApiCallFlow::GetRequestTypeForBody(const std::string& body) {
71 return body.empty() ? "GET" : "POST";
72 }
73
OnURLLoadComplete(std::unique_ptr<std::string> body)74 void OAuth2ApiCallFlow::OnURLLoadComplete(std::unique_ptr<std::string> body) {
75 CHECK_EQ(API_CALL_STARTED, state_);
76 EndApiCall(std::move(body));
77 }
78
CreateURLLoader(const std::string & access_token)79 std::unique_ptr<network::SimpleURLLoader> OAuth2ApiCallFlow::CreateURLLoader(
80 const std::string& access_token) {
81 std::string body = CreateApiCallBody();
82 std::string request_type = GetRequestTypeForBody(body);
83 net::NetworkTrafficAnnotationTag traffic_annotation =
84 CompleteNetworkTrafficAnnotation("oauth2_api_call_flow",
85 GetNetworkTrafficAnnotationTag(), R"(
86 policy {
87 cookies_allowed: NO
88 })");
89
90 auto request = std::make_unique<network::ResourceRequest>();
91 request->url = CreateApiCallUrl();
92 request->method = request_type;
93 request->credentials_mode = network::mojom::CredentialsMode::kOmit;
94 request->headers.SetHeader("Authorization",
95 MakeAuthorizationValue(access_token));
96 std::unique_ptr<network::SimpleURLLoader> result =
97 network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
98
99 // Fetchers are sometimes cancelled because a network change was detected,
100 // especially at startup and after sign-in on ChromeOS. Retrying once should
101 // be enough in those cases; let the fetcher retry up to 3 times just in case.
102 // http://crbug.com/163710
103 result->SetRetryOptions(3, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
104 result->SetAllowHttpErrorResults(true);
105
106 // Even if the the body is empty, we still set the Content-Type because an
107 // empty string may be a meaningful value. For example, a Protocol Buffer
108 // message with only default values will be serialized as an empty string.
109 if (request_type != "GET")
110 result->AttachStringForUpload(body, CreateApiCallBodyContentType());
111
112 return result;
113 }
114