Index: chrome/common/net/gaia/gaia_auth_fetcher.cc |
=================================================================== |
--- chrome/common/net/gaia/gaia_auth_fetcher.cc (revision 110361) |
+++ chrome/common/net/gaia/gaia_auth_fetcher.cc (working copy) |
@@ -4,13 +4,16 @@ |
#include "chrome/common/net/gaia/gaia_auth_fetcher.h" |
+#include <algorithm> |
#include <string> |
#include <utility> |
#include <vector> |
+#include "base/json/json_reader.h" |
#include "base/string_split.h" |
#include "base/string_util.h" |
#include "base/stringprintf.h" |
+#include "base/values.h" |
#include "chrome/common/net/gaia/gaia_auth_consumer.h" |
#include "chrome/common/net/gaia/gaia_constants.h" |
#include "chrome/common/net/gaia/gaia_urls.h" |
@@ -22,6 +25,13 @@ |
#include "net/url_request/url_request_context_getter.h" |
#include "net/url_request/url_request_status.h" |
+namespace { |
+static bool CookiePartsContains(const std::vector<std::string>& parts, |
+ const char* part) { |
+ return std::find(parts.begin(), parts.end(), part) != parts.end(); |
+} |
+} // namespace |
+ |
// TODO(chron): Add sourceless version of this formatter. |
// static |
const char GaiaAuthFetcher::kClientLoginFormat[] = |
@@ -48,6 +58,16 @@ |
"service=%s&" |
"Session=%s"; |
// static |
+const char GaiaAuthFetcher::kGetAuthCodeBodyFormat[] = |
+ "scope=%s&client_id=%s"; |
+// static |
+const char GaiaAuthFetcher::kGetTokenPairBodyFormat[] = |
+ "scope=%s&" |
+ "grant_type=authorization_code&" |
+ "client_id=%s&" |
+ "client_secret=%s&" |
+ "code=%s"; |
+// static |
const char GaiaAuthFetcher::kGetUserInfoFormat[] = |
"LSID=%s"; |
// static |
@@ -100,6 +120,25 @@ |
// static |
const char GaiaAuthFetcher::kSecondFactor[] = "Info=InvalidSecondFactor"; |
+// static |
+const char GaiaAuthFetcher::kAuthHeaderFormat[] = |
+ "Authorization: GoogleLogin auth=%s"; |
+// static |
+const char GaiaAuthFetcher::kAuthCodeCookiePartSecure[] = "Secure"; |
+// static |
+const char GaiaAuthFetcher::kAuthCodeCookiePartHttpOnly[] = "HttpOnly"; |
+// static |
+const char GaiaAuthFetcher::kAuthCodeCookiePartCodePrefix[] = "oauth_code="; |
+// static |
+const int GaiaAuthFetcher::kAuthCodeCookiePartCodePrefixLength = |
+ arraysize(GaiaAuthFetcher::kAuthCodeCookiePartCodePrefix) - 1; |
+// static |
+const char GaiaAuthFetcher::kRefreshTokenKey[] = "refresh_token"; |
+// static |
+const char GaiaAuthFetcher::kAccessTokenKey[] = "access_token"; |
+// static |
+const char GaiaAuthFetcher::kExpiresInKey[] = "expires_in"; |
+ |
GaiaAuthFetcher::GaiaAuthFetcher(GaiaAuthConsumer* consumer, |
const std::string& source, |
net::URLRequestContextGetter* getter) |
@@ -108,6 +147,9 @@ |
source_(source), |
client_login_gurl_(GaiaUrls::GetInstance()->client_login_url()), |
issue_auth_token_gurl_(GaiaUrls::GetInstance()->issue_auth_token_url()), |
+ get_auth_code_gurl_( |
+ GaiaUrls::GetInstance()->client_login_to_oauth2_url()), |
+ get_token_pair_gurl_(GaiaUrls::GetInstance()->oauth2_token_url()), |
get_user_info_gurl_(GaiaUrls::GetInstance()->get_user_info_url()), |
token_auth_gurl_(GaiaUrls::GetInstance()->token_auth_url()), |
merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()), |
@@ -128,6 +170,7 @@ |
content::URLFetcher* GaiaAuthFetcher::CreateGaiaFetcher( |
net::URLRequestContextGetter* getter, |
const std::string& body, |
+ const std::string& headers, |
const GURL& gaia_gurl, |
bool send_cookies, |
content::URLFetcherDelegate* delegate) { |
@@ -141,8 +184,12 @@ |
// maintain a separation between the user's browsing and Chrome's internal |
// services. Where such mixing is desired (MergeSession), it will be done |
// explicitly. |
- if (!send_cookies) |
- to_return->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES); |
+ if (!send_cookies) { |
+ to_return->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
+ net::LOAD_DO_NOT_SAVE_COOKIES); |
+ } |
+ if (!headers.empty()) |
+ to_return->SetExtraRequestHeaders(headers); |
return to_return; |
} |
@@ -209,6 +256,34 @@ |
} |
// static |
+std::string GaiaAuthFetcher::MakeGetAuthCodeBody() { |
+ std::string encoded_scope = net::EscapeUrlEncodedData( |
+ GaiaUrls::GetInstance()->oauth1_login_scope(), true); |
+ std::string encoded_client_id = net::EscapeUrlEncodedData( |
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true); |
+ return StringPrintf(kGetAuthCodeBodyFormat, |
+ encoded_scope.c_str(), |
+ encoded_client_id.c_str()); |
+} |
+ |
+// static |
+std::string GaiaAuthFetcher::MakeGetTokenPairBody( |
+ const std::string& auth_code) { |
+ std::string encoded_scope = net::EscapeUrlEncodedData( |
+ GaiaUrls::GetInstance()->oauth1_login_scope(), true); |
+ std::string encoded_client_id = net::EscapeUrlEncodedData( |
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true); |
+ std::string encoded_client_secret = net::EscapeUrlEncodedData( |
+ GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), true); |
+ std::string encoded_auth_code = net::EscapeUrlEncodedData(auth_code, true); |
+ return StringPrintf(kGetTokenPairBodyFormat, |
+ encoded_scope.c_str(), |
+ encoded_client_id.c_str(), |
+ encoded_client_secret.c_str(), |
+ encoded_auth_code.c_str()); |
+} |
+ |
+// static |
std::string GaiaAuthFetcher::MakeGetUserInfoBody(const std::string& lsid) { |
std::string encoded_lsid = net::EscapeUrlEncodedData(lsid, true); |
return base::StringPrintf(kGetUserInfoFormat, encoded_lsid.c_str()); |
@@ -243,6 +318,12 @@ |
encoded_source.c_str()); |
} |
+// static |
+std::string GaiaAuthFetcher::MakeGetAuthCodeHeader( |
+ const std::string& auth_token) { |
+ return StringPrintf(kAuthHeaderFormat, auth_token.c_str()); |
+} |
+ |
// Helper method that extracts tokens from a successful reply. |
// static |
void GaiaAuthFetcher::ParseClientLoginResponse(const std::string& data, |
@@ -293,6 +374,70 @@ |
} |
} |
+// static |
+bool GaiaAuthFetcher::ParseGetAuthCodeResponse( |
+ const net::ResponseCookies& cookies, |
+ std::string* auth_code) { |
+ DCHECK(auth_code); |
+ net::ResponseCookies::const_iterator iter; |
+ for (iter = cookies.begin(); iter != cookies.end(); ++iter) { |
+ if (ParseCookieToAuthCode(*iter, auth_code)) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// static |
+bool GaiaAuthFetcher::ParseGetTokenPairResponse(const std::string& data, |
+ std::string* refresh_token, |
+ std::string* access_token, |
+ int* expires_in_secs) { |
+ DCHECK(refresh_token); |
+ DCHECK(access_token); |
+ base::JSONReader reader; |
+ scoped_ptr<base::Value> value(reader.Read(data, false)); |
+ if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) |
+ return false; |
+ |
+ DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); |
+ std::string rt; |
+ std::string at; |
+ int exp; |
+ |
+ if (!dict->GetStringWithoutPathExpansion(kRefreshTokenKey, &rt) || |
+ !dict->GetStringWithoutPathExpansion(kAccessTokenKey, &at) || |
+ !dict->GetIntegerWithoutPathExpansion(kExpiresInKey, &exp)) { |
+ return false; |
+ } |
+ |
+ refresh_token->assign(rt); |
+ access_token->assign(at); |
+ *expires_in_secs = exp; |
+ return true; |
+} |
+ |
+// static |
+bool GaiaAuthFetcher::ParseCookieToAuthCode(const std::string& cookie, |
+ std::string* auth_code) { |
+ std::vector<std::string> parts; |
+ base::SplitString(cookie, ';', &parts); |
+ // Per documentation, the cookie should have Secure and HttpOnly. |
+ if (!CookiePartsContains(parts, kAuthCodeCookiePartSecure) || |
+ !CookiePartsContains(parts, kAuthCodeCookiePartHttpOnly)) { |
+ return false; |
+ } |
+ |
+ std::vector<std::string>::const_iterator iter; |
+ for (iter = parts.begin(); iter != parts.end(); ++iter) { |
+ const std::string& part = *iter; |
+ if (StartsWithASCII(part, kAuthCodeCookiePartCodePrefix, false)) { |
+ auth_code->assign(part.substr(kAuthCodeCookiePartCodePrefixLength)); |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
void GaiaAuthFetcher::StartClientLogin( |
const std::string& username, |
const std::string& password, |
@@ -317,6 +462,7 @@ |
allow_hosted_accounts); |
fetcher_.reset(CreateGaiaFetcher(getter_, |
request_body_, |
+ "", |
client_login_gurl_, |
false, |
this)); |
@@ -334,6 +480,7 @@ |
request_body_ = MakeIssueAuthTokenBody(sid, lsid, service); |
fetcher_.reset(CreateGaiaFetcher(getter_, |
request_body_, |
+ "", |
issue_auth_token_gurl_, |
false, |
this)); |
@@ -341,6 +488,22 @@ |
fetcher_->Start(); |
} |
+void GaiaAuthFetcher::StartOAuthLoginTokenFetch( |
+ const std::string& auth_token) { |
+ DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
+ |
+ DVLOG(1) << "Starting OAuth login token fetch"; |
+ request_body_ = MakeGetAuthCodeBody(); |
+ fetcher_.reset(CreateGaiaFetcher(getter_, |
+ request_body_, |
+ MakeGetAuthCodeHeader(auth_token), |
+ get_auth_code_gurl_, |
+ false, |
+ this)); |
+ fetch_pending_ = true; |
+ fetcher_->Start(); |
+} |
+ |
void GaiaAuthFetcher::StartGetUserInfo(const std::string& lsid, |
const std::string& info_key) { |
DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
@@ -349,6 +512,7 @@ |
request_body_ = MakeGetUserInfoBody(lsid); |
fetcher_.reset(CreateGaiaFetcher(getter_, |
request_body_, |
+ "", |
get_user_info_gurl_, |
false, |
this)); |
@@ -369,6 +533,7 @@ |
request_body_ = MakeTokenAuthBody(auth_token, continue_url, source_); |
fetcher_.reset(CreateGaiaFetcher(getter_, |
request_body_, |
+ "", |
token_auth_gurl_, |
false, |
this)); |
@@ -393,6 +558,7 @@ |
request_body_ = MakeMergeSessionBody(auth_token, continue_url, source_); |
fetcher_.reset(CreateGaiaFetcher(getter_, |
request_body_, |
+ "", |
merge_session_gurl_, |
true, |
this)); |
@@ -536,6 +702,61 @@ |
} |
} |
+void GaiaAuthFetcher::OnGetAuthCodeFetched( |
+ const std::string& data, |
+ const net::ResponseCookies& cookies, |
+ const net::URLRequestStatus& status, |
+ int response_code) { |
+ if (status.is_success() && response_code == RC_REQUEST_OK) { |
+ std::string auth_code; |
+ if (!ParseGetAuthCodeResponse(cookies, &auth_code)) { |
+ consumer_->OnOAuthLoginTokenFailure(GoogleServiceAuthError( |
+ GoogleServiceAuthError::UNEXPECTED_RESPONSE)); |
+ } else { |
+ // Start GetTokenPair. |
+ StartGetTokenPair(auth_code); |
+ } |
+ } else { |
+ consumer_->OnOAuthLoginTokenFailure(GenerateAuthError(data, status)); |
+ } |
+} |
+ |
+void GaiaAuthFetcher::StartGetTokenPair(const std::string& auth_code) { |
+ DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
+ |
+ DVLOG(1) << "Starting OAuth token pair fetch"; |
+ request_body_ = MakeGetTokenPairBody(auth_code); |
+ fetcher_.reset(CreateGaiaFetcher(getter_, |
+ request_body_, |
+ "", |
+ get_token_pair_gurl_, |
+ false, |
+ this)); |
+ fetch_pending_ = true; |
+ fetcher_->Start(); |
+} |
+ |
+void GaiaAuthFetcher::OnGetTokenPairFetched( |
+ const std::string& data, |
+ const net::URLRequestStatus& status, |
+ int response_code) { |
+ if (status.is_success() && response_code == RC_REQUEST_OK) { |
+ std::string refresh_token; |
+ std::string access_token; |
+ int expires_in_secs; |
+ if (ParseGetTokenPairResponse( |
+ data, &refresh_token, &access_token, &expires_in_secs)) { |
+ consumer_->OnOAuthLoginTokenSuccess( |
+ refresh_token, access_token, expires_in_secs); |
+ } else { |
+ consumer_->OnOAuthLoginTokenFailure(GoogleServiceAuthError( |
+ GoogleServiceAuthError::UNEXPECTED_RESPONSE)); |
+ } |
+ } else { |
+ consumer_->OnOAuthLoginTokenFailure(GenerateAuthError(data, status)); |
+ } |
+} |
+ |
void GaiaAuthFetcher::OnGetUserInfoFetched( |
const std::string& data, |
const net::URLRequestStatus& status, |
@@ -591,6 +812,10 @@ |
OnClientLoginFetched(data, status, response_code); |
} else if (url == issue_auth_token_gurl_) { |
OnIssueAuthTokenFetched(data, status, response_code); |
+ } else if (url == get_auth_code_gurl_) { |
+ OnGetAuthCodeFetched(data, source->GetCookies(), status, response_code); |
+ } else if (url == get_token_pair_gurl_) { |
+ OnGetTokenPairFetched(data, status, response_code); |
} else if (url == get_user_info_gurl_) { |
OnGetUserInfoFetched(data, status, response_code); |
} else if (url == token_auth_gurl_) { |