OLD | NEW |
| (Empty) |
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 "chrome/common/net/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 "chrome/common/net/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 | |
20 using net::ResponseCookies; | |
21 using net::URLFetcher; | |
22 using net::URLFetcherDelegate; | |
23 using net::URLRequestContextGetter; | |
24 using net::URLRequestStatus; | |
25 | |
26 namespace { | |
27 static const char kAuthorizationHeaderFormat[] = | |
28 "Authorization: Bearer %s"; | |
29 | |
30 static std::string MakeAuthorizationHeader(const std::string& auth_token) { | |
31 return StringPrintf(kAuthorizationHeaderFormat, auth_token.c_str()); | |
32 } | |
33 } // namespace | |
34 | |
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) | |
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 | |
48 OAuth2ApiCallFlow::~OAuth2ApiCallFlow() {} | |
49 | |
50 void OAuth2ApiCallFlow::Start() { | |
51 BeginApiCall(); | |
52 } | |
53 | |
54 void 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 | |
67 void 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 | |
101 void 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 | |
115 void 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 | |
128 OAuth2AccessTokenFetcher* OAuth2ApiCallFlow::CreateAccessTokenFetcher() { | |
129 return new OAuth2AccessTokenFetcher(this, context_); | |
130 } | |
131 | |
132 void OAuth2ApiCallFlow::OnURLFetchComplete(const net::URLFetcher* source) { | |
133 CHECK(source); | |
134 CHECK_EQ(API_CALL_STARTED, state_); | |
135 EndApiCall(source); | |
136 } | |
137 | |
138 void OAuth2ApiCallFlow::OnGetTokenSuccess(const std::string& access_token, | |
139 const base::Time& expiration_time) { | |
140 access_token_ = access_token; | |
141 EndMintAccessToken(NULL); | |
142 } | |
143 | |
144 void OAuth2ApiCallFlow::OnGetTokenFailure( | |
145 const GoogleServiceAuthError& error) { | |
146 EndMintAccessToken(&error); | |
147 } | |
148 | |
149 URLFetcher* 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 } | |
OLD | NEW |