Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: google_apis/gaia/oauth2_access_token_fetcher.cc

Issue 57993006: signin: record http response codes and net errors in OAuth2AccessTokenFetcher. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "google_apis/gaia/oauth2_access_token_fetcher.h" 5 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
12 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h" 15 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h" 16 #include "base/time/time.h"
15 #include "base/values.h" 17 #include "base/values.h"
16 #include "google_apis/gaia/gaia_urls.h" 18 #include "google_apis/gaia/gaia_urls.h"
17 #include "google_apis/gaia/google_service_auth_error.h" 19 #include "google_apis/gaia/google_service_auth_error.h"
18 #include "net/base/escape.h" 20 #include "net/base/escape.h"
19 #include "net/base/load_flags.h" 21 #include "net/base/load_flags.h"
20 #include "net/http/http_status_code.h" 22 #include "net/http/http_status_code.h"
21 #include "net/url_request/url_fetcher.h" 23 #include "net/url_request/url_fetcher.h"
(...skipping 15 matching lines...) Expand all
37 39
38 static const char kGetAccessTokenBodyWithScopeFormat[] = 40 static const char kGetAccessTokenBodyWithScopeFormat[] =
39 "client_id=%s&" 41 "client_id=%s&"
40 "client_secret=%s&" 42 "client_secret=%s&"
41 "grant_type=refresh_token&" 43 "grant_type=refresh_token&"
42 "refresh_token=%s&" 44 "refresh_token=%s&"
43 "scope=%s"; 45 "scope=%s";
44 46
45 static const char kAccessTokenKey[] = "access_token"; 47 static const char kAccessTokenKey[] = "access_token";
46 static const char kExpiresInKey[] = "expires_in"; 48 static const char kExpiresInKey[] = "expires_in";
49 static const char kErrorKey[] = "error";
50
51 // Enumerated constants for logging server responses on 400 errors, matching
52 // RFC 6749.
53 enum OAuth2ErrorCodesForHistogram {
54 OAUTH2_ACCESS_ERROR_INVALID_REQUEST = 0,
55 OAUTH2_ACCESS_ERROR_INVALID_CLIENT,
56 OAUTH2_ACCESS_ERROR_INVALID_GRANT,
57 OAUTH2_ACCESS_ERROR_UNAUTHORIZED_CLIENT,
58 OAUTH2_ACCESS_ERROR_UNSUPPORTED_GRANT_TYPE,
59 OAUTH2_ACCESS_ERROR_INVALID_SCOPE,
60 OAUTH2_ACCESS_ERROR_UNKNOWN,
61 OAUTH2_ACCESS_ERROR_COUNT
62 };
63
64 OAuth2ErrorCodesForHistogram OAuth2ErrorToHistogramValue(
65 const std::string& error) {
66 if (error == "invalid_request")
67 return OAUTH2_ACCESS_ERROR_INVALID_REQUEST;
68 else if (error == "invalid_client")
69 return OAUTH2_ACCESS_ERROR_INVALID_CLIENT;
70 else if (error == "invalid_grant")
71 return OAUTH2_ACCESS_ERROR_INVALID_GRANT;
72 else if (error == "unauthorized_client")
73 return OAUTH2_ACCESS_ERROR_UNAUTHORIZED_CLIENT;
74 else if (error == "unsupported_grant_type")
75 return OAUTH2_ACCESS_ERROR_UNSUPPORTED_GRANT_TYPE;
76 else if (error == "invalid_scope")
77 return OAUTH2_ACCESS_ERROR_INVALID_SCOPE;
78
79 return OAUTH2_ACCESS_ERROR_UNKNOWN;
80 }
47 81
48 static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) { 82 static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) {
49 CHECK(!status.is_success()); 83 CHECK(!status.is_success());
50 if (status.status() == URLRequestStatus::CANCELED) { 84 if (status.status() == URLRequestStatus::CANCELED) {
51 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); 85 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
52 } else { 86 } else {
53 DLOG(WARNING) << "Could not reach Google Accounts servers: errno " 87 DLOG(WARNING) << "Could not reach Google Accounts servers: errno "
54 << status.error(); 88 << status.error();
55 return GoogleServiceAuthError::FromConnectionError(status.error()); 89 return GoogleServiceAuthError::FromConnectionError(status.error());
56 } 90 }
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 this)); 151 this));
118 fetcher_->Start(); // OnURLFetchComplete will be called. 152 fetcher_->Start(); // OnURLFetchComplete will be called.
119 } 153 }
120 154
121 void OAuth2AccessTokenFetcher::EndGetAccessToken( 155 void OAuth2AccessTokenFetcher::EndGetAccessToken(
122 const net::URLFetcher* source) { 156 const net::URLFetcher* source) {
123 CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_); 157 CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_);
124 state_ = GET_ACCESS_TOKEN_DONE; 158 state_ = GET_ACCESS_TOKEN_DONE;
125 159
126 URLRequestStatus status = source->GetStatus(); 160 URLRequestStatus status = source->GetStatus();
161 int histogram_value = status.is_success() ? source->GetResponseCode() :
162 status.error();
163 UMA_HISTOGRAM_SPARSE_SLOWLY("Gaia.ResponseCodesForOAuth2AccessToken",
164 histogram_value);
127 if (!status.is_success()) { 165 if (!status.is_success()) {
128 OnGetTokenFailure(CreateAuthError(status)); 166 OnGetTokenFailure(CreateAuthError(status));
129 return; 167 return;
130 } 168 }
131 169
132 // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be 170 // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
133 // '403 Rate Limit Exeeded.' 171 // '403 Rate Limit Exeeded.'
134 if (source->GetResponseCode() == net::HTTP_FORBIDDEN) { 172 if (source->GetResponseCode() == net::HTTP_FORBIDDEN) {
135 OnGetTokenFailure(GoogleServiceAuthError( 173 OnGetTokenFailure(GoogleServiceAuthError(
136 GoogleServiceAuthError::SERVICE_UNAVAILABLE)); 174 GoogleServiceAuthError::SERVICE_UNAVAILABLE));
137 return; 175 return;
138 } 176 }
139 177
178 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) {
179 // HTTP_BAD_REQUEST (400) usually contains error as per
180 // http://tools.ietf.org/html/rfc6749#section-5.2.
181 std::string gaia_error;
182 OAuth2ErrorCodesForHistogram access_error(OAUTH2_ACCESS_ERROR_UNKNOWN);
183 if (ParseGetAccessTokenFailureResponse(source, &gaia_error))
184 access_error = OAuth2ErrorToHistogramValue(gaia_error);
185 UMA_HISTOGRAM_ENUMERATION("Gaia.BadRequestTypeForOAuth2AccessToken",
186 access_error, OAUTH2_ACCESS_ERROR_COUNT);
187 OnGetTokenFailure(GoogleServiceAuthError(
188 GoogleServiceAuthError::SERVICE_ERROR));
pavely 2013/11/04 23:25:28 You still need to return INVALID_GAIA_CREDENTIALS
189 return;
190 }
191
140 // The other errors are treated as permanent error. 192 // The other errors are treated as permanent error.
141 if (source->GetResponseCode() != net::HTTP_OK) { 193 if (source->GetResponseCode() != net::HTTP_OK) {
142 OnGetTokenFailure(GoogleServiceAuthError( 194 OnGetTokenFailure(GoogleServiceAuthError(
143 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 195 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
144 return; 196 return;
145 } 197 }
146 198
147 // The request was successfully fetched and it returned OK. 199 // The request was successfully fetched and it returned OK.
148 // Parse out the access token and the expiration time. 200 // Parse out the access token and the expiration time.
149 std::string access_token; 201 std::string access_token;
150 int expires_in; 202 int expires_in;
151 if (!ParseGetAccessTokenResponse(source, &access_token, &expires_in)) { 203 if (!ParseGetAccessTokenSuccessResponse(
204 source, &access_token, &expires_in)) {
152 DLOG(WARNING) << "Response doesn't match expected format"; 205 DLOG(WARNING) << "Response doesn't match expected format";
153 OnGetTokenFailure( 206 OnGetTokenFailure(
154 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); 207 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
155 return; 208 return;
156 } 209 }
157 // The token will expire in |expires_in| seconds. Take a 10% error margin to 210 // The token will expire in |expires_in| seconds. Take a 10% error margin to
158 // prevent reusing a token too close to its expiration date. 211 // prevent reusing a token too close to its expiration date.
159 OnGetTokenSuccess( 212 OnGetTokenSuccess(
160 access_token, 213 access_token,
161 base::Time::Now() + base::TimeDelta::FromSeconds(9 * expires_in / 10)); 214 base::Time::Now() + base::TimeDelta::FromSeconds(9 * expires_in / 10));
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 std::string scopes_string = JoinString(scopes, ' '); 259 std::string scopes_string = JoinString(scopes, ' ');
207 return base::StringPrintf( 260 return base::StringPrintf(
208 kGetAccessTokenBodyWithScopeFormat, 261 kGetAccessTokenBodyWithScopeFormat,
209 enc_client_id.c_str(), 262 enc_client_id.c_str(),
210 enc_client_secret.c_str(), 263 enc_client_secret.c_str(),
211 enc_refresh_token.c_str(), 264 enc_refresh_token.c_str(),
212 net::EscapeUrlEncodedData(scopes_string, true).c_str()); 265 net::EscapeUrlEncodedData(scopes_string, true).c_str());
213 } 266 }
214 } 267 }
215 268
216 // static 269 scoped_ptr<base::DictionaryValue> ParseGetAccessTokenResponse(
217 bool OAuth2AccessTokenFetcher::ParseGetAccessTokenResponse( 270 const net::URLFetcher* source) {
218 const net::URLFetcher* source,
219 std::string* access_token,
220 int* expires_in) {
221 CHECK(source); 271 CHECK(source);
222 CHECK(access_token); 272
223 std::string data; 273 std::string data;
224 source->GetResponseAsString(&data); 274 source->GetResponseAsString(&data);
225 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 275 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
226 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) 276 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
277 value.reset();
278
279 return scoped_ptr<base::DictionaryValue>(
280 static_cast<base::DictionaryValue*>(value.release()));
281 }
282
283 // static
284 bool OAuth2AccessTokenFetcher::ParseGetAccessTokenSuccessResponse(
285 const net::URLFetcher* source,
286 std::string* access_token,
287 int* expires_in) {
288 CHECK(access_token);
289 scoped_ptr<base::DictionaryValue> value = ParseGetAccessTokenResponse(
290 source);
291 if (value.get() == NULL)
227 return false; 292 return false;
228 293
229 base::DictionaryValue* dict = 294 return value->GetString(kAccessTokenKey, access_token) &&
230 static_cast<base::DictionaryValue*>(value.get()); 295 value->GetInteger(kExpiresInKey, expires_in);
231 return dict->GetString(kAccessTokenKey, access_token) &&
232 dict->GetInteger(kExpiresInKey, expires_in);
233 } 296 }
297
298 // static
299 bool OAuth2AccessTokenFetcher::ParseGetAccessTokenFailureResponse(
300 const net::URLFetcher* source,
301 std::string* error) {
302 CHECK(error);
303 scoped_ptr<base::DictionaryValue> value = ParseGetAccessTokenResponse(
304 source);
305 if (value.get() == NULL)
306 return false;
307 return value->GetString(kErrorKey, error);
308 }
OLDNEW
« no previous file with comments | « google_apis/gaia/oauth2_access_token_fetcher.h ('k') | google_apis/gaia/oauth2_access_token_fetcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698