Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "chrome/common/net/gaia/gaia_authenticator2.h" | 5 #include "chrome/common/net/gaia/gaia_authenticator2.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/string_split.h" | 9 #include "base/string_split.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 // static | 27 // static |
| 28 const char GaiaAuthenticator2::kClientLoginCaptchaFormat[] = | 28 const char GaiaAuthenticator2::kClientLoginCaptchaFormat[] = |
| 29 "Email=%s&" | 29 "Email=%s&" |
| 30 "Passwd=%s&" | 30 "Passwd=%s&" |
| 31 "PersistentCookie=%s&" | 31 "PersistentCookie=%s&" |
| 32 "accountType=%s&" | 32 "accountType=%s&" |
| 33 "source=%s&" | 33 "source=%s&" |
| 34 "service=%s&" | 34 "service=%s&" |
| 35 "logintoken=%s&" | 35 "logintoken=%s&" |
| 36 "logincaptcha=%s"; | 36 "logincaptcha=%s"; |
| 37 // static | |
| 38 const char GaiaAuthenticator2::kIssueAuthTokenFormat[] = | |
| 39 "SID=%s&" | |
| 40 "LSID=%s&" | |
| 41 "service=%s&" | |
| 42 "Session=true"; | |
| 37 | 43 |
| 38 // static | 44 // static |
| 39 const char GaiaAuthenticator2::kCookiePersistence[] = "true"; | 45 const char GaiaAuthenticator2::kCookiePersistence[] = "true"; |
| 40 // static | 46 // static |
| 41 const char GaiaAuthenticator2::kAccountType[] = "HOSTED_OR_GOOGLE"; | 47 const char GaiaAuthenticator2::kAccountType[] = "HOSTED_OR_GOOGLE"; |
| 42 // static | 48 // static |
| 43 const char GaiaAuthenticator2::kChromeOSSource[] = "chromeos"; | 49 const char GaiaAuthenticator2::kChromeOSSource[] = "chromeos"; |
| 44 // static | 50 // static |
| 45 // Service name for Gaia Contacts API. API is used to get user's image. | 51 // Service name for Gaia Contacts API. API is used to get user's image. |
| 46 const char GaiaAuthenticator2::kContactsService[] = "cp"; | 52 const char GaiaAuthenticator2::kContactsService[] = "cp"; |
| 47 // static | 53 // static |
| 48 const char GaiaAuthenticator2::kSecondFactor[] = "Info=InvalidSecondFactor"; | 54 const char GaiaAuthenticator2::kSecondFactor[] = "Info=InvalidSecondFactor"; |
| 49 | 55 |
| 50 // TODO(chron): These urls are also in auth_response_handler.h. | 56 // TODO(chron): These urls are also in auth_response_handler.h. |
| 51 // The URLs for different calls in the Google Accounts programmatic login API. | 57 // The URLs for different calls in the Google Accounts programmatic login API. |
| 52 const char GaiaAuthenticator2::kClientLoginUrl[] = | 58 const char GaiaAuthenticator2::kClientLoginUrl[] = |
| 53 "https://www.google.com/accounts/ClientLogin"; | 59 "https://www.google.com/accounts/ClientLogin"; |
| 54 const char GaiaAuthenticator2::kIssueAuthTokenUrl[] = | 60 const char GaiaAuthenticator2::kIssueAuthTokenUrl[] = |
| 55 "https://www.google.com/accounts/IssueAuthToken"; | 61 "https://www.google.com/accounts/IssueAuthToken"; |
| 56 // TODO(chron): Fix this URL not to hardcode source | |
| 57 // TODO(cmasone): make sure that using an http:// URL in the "continue" | |
| 58 // parameter here doesn't open the system up to attack long-term. | |
| 59 const char GaiaAuthenticator2::kTokenAuthUrl[] = | |
| 60 "https://www.google.com/accounts/TokenAuth?" | |
| 61 "continue=http://www.google.com/webhp&source=chromeos&auth="; | |
| 62 | 62 |
| 63 GaiaAuthenticator2::GaiaAuthenticator2(GaiaAuthConsumer* consumer, | 63 GaiaAuthenticator2::GaiaAuthenticator2(GaiaAuthConsumer* consumer, |
| 64 const std::string& source, | 64 const std::string& source, |
| 65 URLRequestContextGetter* getter) | 65 URLRequestContextGetter* getter) |
| 66 : consumer_(consumer), | 66 : consumer_(consumer), |
| 67 getter_(getter), | 67 getter_(getter), |
| 68 source_(source), | 68 source_(source), |
| 69 client_login_gurl_(kClientLoginUrl), | 69 client_login_gurl_(kClientLoginUrl), |
| 70 fetch_pending_(false){} | 70 issue_auth_token_gurl_(kIssueAuthTokenUrl), |
| 71 fetch_pending_(false) {} | |
| 71 | 72 |
| 72 GaiaAuthenticator2::~GaiaAuthenticator2() {} | 73 GaiaAuthenticator2::~GaiaAuthenticator2() {} |
| 73 | 74 |
| 74 bool GaiaAuthenticator2::HasPendingFetch() { | 75 bool GaiaAuthenticator2::HasPendingFetch() { |
| 75 return fetch_pending_; | 76 return fetch_pending_; |
| 76 } | 77 } |
| 77 | 78 |
| 78 void GaiaAuthenticator2::CancelRequest() { | 79 void GaiaAuthenticator2::CancelRequest() { |
| 79 fetcher_.reset(); | 80 fetcher_.reset(); |
| 80 fetch_pending_ = false; | 81 fetch_pending_ = false; |
| 81 } | 82 } |
| 82 | 83 |
| 83 // static | 84 // static |
| 84 URLFetcher* GaiaAuthenticator2::CreateClientLoginFetcher( | 85 URLFetcher* GaiaAuthenticator2::CreateGaiaFetcher( |
| 85 URLRequestContextGetter* getter, | 86 URLRequestContextGetter* getter, |
| 86 const std::string& body, | 87 const std::string& body, |
| 87 const GURL& client_login_gurl, | 88 const GURL& gaia_gurl, |
| 88 URLFetcher::Delegate* delegate) { | 89 URLFetcher::Delegate* delegate) { |
| 89 | 90 |
| 90 URLFetcher* to_return = | 91 URLFetcher* to_return = |
| 91 URLFetcher::Create(0, | 92 URLFetcher::Create(0, |
| 92 client_login_gurl, | 93 gaia_gurl, |
| 93 URLFetcher::POST, | 94 URLFetcher::POST, |
| 94 delegate); | 95 delegate); |
| 95 to_return->set_request_context(getter); | 96 to_return->set_request_context(getter); |
| 96 to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); | 97 to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES); |
| 97 to_return->set_upload_data("application/x-www-form-urlencoded", body); | 98 to_return->set_upload_data("application/x-www-form-urlencoded", body); |
| 98 return to_return; | 99 return to_return; |
| 99 } | 100 } |
| 100 | 101 |
| 101 std::string GaiaAuthenticator2::GenerateRequestBody( | 102 // static |
| 103 std::string GaiaAuthenticator2::MakeClientLoginBody( | |
| 102 const std::string& username, | 104 const std::string& username, |
| 103 const std::string& password, | 105 const std::string& password, |
| 104 const std::string& source, | 106 const std::string& source, |
| 105 const char* service, | 107 const char* service, |
| 106 const std::string& login_token, | 108 const std::string& login_token, |
| 107 const std::string& login_captcha) { | 109 const std::string& login_captcha) { |
| 108 | 110 |
| 109 if (login_token.empty() || login_captcha.empty()) { | 111 if (login_token.empty() || login_captcha.empty()) { |
| 110 return StringPrintf(kClientLoginFormat, | 112 return StringPrintf(kClientLoginFormat, |
| 111 UrlEncodeString(username).c_str(), | 113 UrlEncodeString(username).c_str(), |
| 112 UrlEncodeString(password).c_str(), | 114 UrlEncodeString(password).c_str(), |
| 113 kCookiePersistence, | 115 kCookiePersistence, |
| 114 kAccountType, | 116 kAccountType, |
| 115 source.c_str(), | 117 source.c_str(), |
| 116 service); | 118 service); |
| 117 } | 119 } |
| 118 | 120 |
| 119 return StringPrintf(kClientLoginCaptchaFormat, | 121 return StringPrintf(kClientLoginCaptchaFormat, |
| 120 UrlEncodeString(username).c_str(), | 122 UrlEncodeString(username).c_str(), |
| 121 UrlEncodeString(password).c_str(), | 123 UrlEncodeString(password).c_str(), |
| 122 kCookiePersistence, | 124 kCookiePersistence, |
| 123 kAccountType, | 125 kAccountType, |
| 124 source.c_str(), | 126 source.c_str(), |
| 125 service, | 127 service, |
| 126 UrlEncodeString(login_token).c_str(), | 128 UrlEncodeString(login_token).c_str(), |
| 127 UrlEncodeString(login_captcha).c_str()); | 129 UrlEncodeString(login_captcha).c_str()); |
| 128 | 130 |
| 129 } | 131 } |
| 130 | 132 |
| 131 // Helper method that extracts tokens from a successful reply, and saves them | 133 // static |
| 132 // in the right fields. | 134 std::string GaiaAuthenticator2::MakeIssueAuthTokenBody( |
| 135 const std::string& sid, | |
| 136 const std::string& lsid, | |
| 137 const char* const service) { | |
| 138 | |
| 139 return StringPrintf(kIssueAuthTokenFormat, | |
| 140 UrlEncodeString(sid).c_str(), | |
| 141 UrlEncodeString(lsid).c_str(), | |
| 142 service); | |
| 143 } | |
| 144 | |
| 145 // Helper method that extracts tokens from a successful reply. | |
| 133 // static | 146 // static |
| 134 void GaiaAuthenticator2::ParseClientLoginResponse(const std::string& data, | 147 void GaiaAuthenticator2::ParseClientLoginResponse(const std::string& data, |
| 135 std::string* sid, | 148 std::string* sid, |
| 136 std::string* lsid, | 149 std::string* lsid, |
| 137 std::string* token) { | 150 std::string* token) { |
| 138 using std::vector; | 151 using std::vector; |
| 139 using std::pair; | 152 using std::pair; |
| 140 using std::string; | 153 using std::string; |
| 141 | 154 |
| 142 vector<pair<string, string> > tokens; | 155 vector<pair<string, string> > tokens; |
| 143 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); | 156 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); |
| 144 for (vector<pair<string, string> >::iterator i = tokens.begin(); | 157 for (vector<pair<string, string> >::iterator i = tokens.begin(); |
| 145 i != tokens.end(); ++i) { | 158 i != tokens.end(); ++i) { |
| 146 if (i->first == "SID") { | 159 if (i->first == "SID") { |
| 147 sid->assign(i->second); | 160 sid->assign(i->second); |
| 148 } else if (i->first == "LSID") { | 161 } else if (i->first == "LSID") { |
| 149 lsid->assign(i->second); | 162 lsid->assign(i->second); |
| 150 } else if (i->first == "Auth") { | 163 } else if (i->first == "Auth") { |
| 151 token->assign(i->second); | 164 token->assign(i->second); |
| 152 } | 165 } |
| 153 } | 166 } |
| 154 } | 167 } |
| 155 | 168 |
| 156 void GaiaAuthenticator2::StartClientLogin(const std::string& username, | 169 void GaiaAuthenticator2::StartClientLogin(const std::string& username, |
| 157 const std::string& password, | 170 const std::string& password, |
| 158 const char* service, | 171 const char* const service, |
| 159 const std::string& login_token, | 172 const std::string& login_token, |
| 160 const std::string& login_captcha) { | 173 const std::string& login_captcha) { |
| 161 | 174 |
| 175 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; | |
| 176 | |
| 162 // This class is thread agnostic, so be sure to call this only on the | 177 // This class is thread agnostic, so be sure to call this only on the |
| 163 // same thread each time. | 178 // same thread each time. |
| 164 LOG(INFO) << "Starting new ClientLogin fetch."; | 179 LOG(INFO) << "Starting new ClientLogin fetch for:" << username; |
| 165 | 180 |
| 166 // Must outlive fetcher_. | 181 // Must outlive fetcher_. |
| 167 request_body_ = GenerateRequestBody(username, | 182 request_body_ = MakeClientLoginBody(username, |
| 168 password, | 183 password, |
| 169 source_, | 184 source_, |
| 170 service, | 185 service, |
| 171 login_token, | 186 login_token, |
| 172 login_captcha); | 187 login_captcha); |
| 173 | 188 fetcher_.reset(CreateGaiaFetcher(getter_, |
| 174 fetcher_.reset(CreateClientLoginFetcher(getter_, | 189 request_body_, |
| 175 request_body_, | 190 client_login_gurl_, |
| 176 client_login_gurl_, | 191 this)); |
| 177 this)); | |
| 178 fetch_pending_ = true; | 192 fetch_pending_ = true; |
| 179 fetcher_->Start(); | 193 fetcher_->Start(); |
| 180 } | 194 } |
| 181 | 195 |
| 182 void GaiaAuthenticator2::OnClientLoginFetched(const std::string& data, | 196 void GaiaAuthenticator2::StartIssueAuthToken(const std::string& sid, |
| 183 const URLRequestStatus& status, | 197 const std::string& lsid, |
| 184 int response_code) { | 198 const char* const service) { |
| 185 | 199 |
| 186 if (status.is_success() && response_code == RC_REQUEST_OK) { | 200 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
| 187 LOG(INFO) << "ClientLogin successful!"; | |
| 188 std::string sid; | |
| 189 std::string lsid; | |
| 190 std::string token; | |
| 191 ParseClientLoginResponse(data, &sid, &lsid, &token); | |
| 192 consumer_->OnClientLoginSuccess( | |
| 193 GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data)); | |
| 194 return; | |
| 195 } | |
| 196 | 201 |
| 197 GaiaAuthConsumer::ClientLoginError error; | 202 LOG(INFO) << "Starting IssueAuthToken for: " << service; |
| 203 requested_service_ = service; | |
| 204 request_body_ = MakeIssueAuthTokenBody(sid, lsid, service); | |
| 205 fetcher_.reset(CreateGaiaFetcher(getter_, | |
| 206 request_body_, | |
| 207 issue_auth_token_gurl_, | |
| 208 this)); | |
| 209 fetch_pending_ = true; | |
| 210 fetcher_->Start(); | |
| 211 } | |
| 212 | |
| 213 GaiaAuthConsumer::GaiaAuthError GaiaAuthenticator2::GenerateAuthError( | |
| 214 const std::string& data, | |
| 215 const URLRequestStatus& status) { | |
| 216 | |
| 217 GaiaAuthConsumer::GaiaAuthError error; | |
| 198 error.data = data; | 218 error.data = data; |
| 199 | 219 |
| 200 if (!status.is_success()) { | 220 if (!status.is_success()) { |
| 201 if (status.status() == URLRequestStatus::CANCELED) { | 221 if (status.status() == URLRequestStatus::CANCELED) { |
| 202 error.code = GaiaAuthConsumer::REQUEST_CANCELED; | 222 error.code = GaiaAuthConsumer::REQUEST_CANCELED; |
| 203 } else { | 223 } else { |
| 204 error.code = GaiaAuthConsumer::NETWORK_ERROR; | 224 error.code = GaiaAuthConsumer::NETWORK_ERROR; |
| 205 error.network_error = status.os_error(); | 225 error.network_error = status.os_error(); |
| 206 LOG(WARNING) << "Could not reach Google Accounts servers: errno " | 226 LOG(WARNING) << "Could not reach Google Accounts servers: errno " |
| 207 << status.os_error(); | 227 << status.os_error(); |
| 208 } | 228 } |
| 209 } else { | 229 } else { |
| 210 if (IsSecondFactorSuccess(data)) { | 230 if (IsSecondFactorSuccess(data)) { |
| 211 error.code = GaiaAuthConsumer::TWO_FACTOR; | 231 error.code = GaiaAuthConsumer::TWO_FACTOR; |
| 212 } else { | 232 } else { |
| 213 error.code = GaiaAuthConsumer::PERMISSION_DENIED; | 233 error.code = GaiaAuthConsumer::PERMISSION_DENIED; |
| 214 } | 234 } |
| 215 } | 235 } |
| 216 | 236 |
| 217 consumer_->OnClientLoginFailure(error); | 237 return error; |
| 238 } | |
| 239 | |
| 240 void GaiaAuthenticator2::OnClientLoginFetched(const std::string& data, | |
| 241 const URLRequestStatus& status, | |
| 242 int response_code) { | |
| 243 | |
| 244 if (status.is_success() && response_code == RC_REQUEST_OK) { | |
| 245 LOG(INFO) << "ClientLogin successful!"; | |
| 246 std::string sid; | |
| 247 std::string lsid; | |
| 248 std::string token; | |
| 249 ParseClientLoginResponse(data, &sid, &lsid, &token); | |
| 250 consumer_->OnClientLoginSuccess( | |
| 251 GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data)); | |
| 252 return; | |
| 253 } | |
|
Zachary Kuznia
2010/07/06 03:33:23
Perhaps make this an "} else {" to make the flow o
| |
| 254 | |
| 255 consumer_->OnClientLoginFailure(GenerateAuthError(data, status)); | |
| 256 } | |
| 257 | |
| 258 void GaiaAuthenticator2::OnIssueAuthTokenFetched( | |
| 259 const std::string& data, | |
| 260 const URLRequestStatus& status, | |
| 261 int response_code) { | |
| 262 if (status.is_success() && response_code == RC_REQUEST_OK) { | |
| 263 // Only the bare token is returned in the body of this Gaia call | |
| 264 // without any padding. | |
| 265 consumer_->OnIssueAuthTokenSuccess(requested_service_, data); | |
| 266 } else { | |
| 267 consumer_->OnIssueAuthTokenFailure(requested_service_, | |
| 268 GenerateAuthError(data, status)); | |
| 269 } | |
| 218 } | 270 } |
| 219 | 271 |
| 220 void GaiaAuthenticator2::OnURLFetchComplete(const URLFetcher* source, | 272 void GaiaAuthenticator2::OnURLFetchComplete(const URLFetcher* source, |
| 221 const GURL& url, | 273 const GURL& url, |
| 222 const URLRequestStatus& status, | 274 const URLRequestStatus& status, |
| 223 int response_code, | 275 int response_code, |
| 224 const ResponseCookies& cookies, | 276 const ResponseCookies& cookies, |
| 225 const std::string& data) { | 277 const std::string& data) { |
| 226 fetch_pending_ = false; | 278 fetch_pending_ = false; |
| 227 if (url == client_login_gurl_) { | 279 if (url == client_login_gurl_) { |
| 228 OnClientLoginFetched(data, status, response_code); | 280 OnClientLoginFetched(data, status, response_code); |
| 229 return; | 281 } else if (url == issue_auth_token_gurl_) { |
| 282 OnIssueAuthTokenFetched(data, status, response_code); | |
| 283 } else { | |
| 284 NOTREACHED(); | |
| 230 } | 285 } |
| 231 NOTREACHED(); | |
| 232 } | 286 } |
| 233 | 287 |
| 234 // static | 288 // static |
| 235 bool GaiaAuthenticator2::IsSecondFactorSuccess( | 289 bool GaiaAuthenticator2::IsSecondFactorSuccess( |
| 236 const std::string& alleged_error) { | 290 const std::string& alleged_error) { |
| 237 return alleged_error.find(kSecondFactor) != | 291 return alleged_error.find(kSecondFactor) != |
| 238 std::string::npos; | 292 std::string::npos; |
| 239 } | 293 } |
| OLD | NEW |