1 // Copyright 2013 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.
5 #include "components/autofill/content/browser/wallet/wallet_signin_helper.h"
7 #include "base/callback_helpers.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/rand_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "components/autofill/content/browser/wallet/wallet_service_url.h"
16 #include "components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "google_apis/gaia/google_service_auth_error.h"
19 #include "net/base/escape.h"
20 #include "net/cookies/canonical_cookie.h"
21 #include "net/cookies/cookie_monster.h"
22 #include "net/cookies/cookie_options.h"
23 #include "net/cookies/cookie_store.h"
24 #include "net/url_request/url_fetcher.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_getter.h"
33 // Toolbar::GetAccountInfo API URL (JSON).
34 const char kGetAccountInfoUrlFormat[] =
35 "https://clients1.google.com/tbproxy/getaccountinfo?key=%d&rv=2"
38 const char kWalletCookieName[] = "gdtoken";
40 // Callback for retrieving Google Wallet cookies. |callback| is passed the
41 // retrieved cookies and posted back to the UI thread. |cookies| is any Google
43 void GetGoogleCookiesCallback(
44 const base::Callback<void(const std::string&)>& callback,
45 const net::CookieList& cookies) {
46 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
48 // Cookies for parent domains will also be returned; we only want cookies with
49 // exact host matches.
50 std::string host = wallet::GetPassiveAuthUrl().host();
51 std::string wallet_cookie;
52 for (size_t i = 0; i < cookies.size(); ++i) {
53 if (LowerCaseEqualsASCII(cookies[i].Name(), kWalletCookieName) &&
54 LowerCaseEqualsASCII(cookies[i].Domain(), host.c_str())) {
55 wallet_cookie = cookies[i].Value();
59 content::BrowserThread::PostTask(content::BrowserThread::UI,
61 base::Bind(callback, wallet_cookie));
64 // Gets Google Wallet cookies. Must be called on the IO thread.
65 // |request_context_getter| is a getter for the current request context.
66 // |callback| is called when retrieving cookies is completed.
67 void GetGoogleCookies(
68 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
69 const base::Callback<void(const std::string&)>& callback) {
70 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
72 net::URLRequestContext* url_request_context =
73 request_context_getter->GetURLRequestContext();
74 net::CookieStore* cookie_store = url_request_context ?
75 url_request_context->cookie_store() : NULL;
76 net::CookieMonster* cookie_monster = cookie_store ?
77 cookie_store->GetCookieMonster() : NULL;
78 if (!cookie_monster) {
79 content::BrowserThread::PostTask(content::BrowserThread::UI,
81 base::Bind(callback, std::string()));
85 net::CookieOptions cookie_options;
86 cookie_options.set_include_httponly();
87 cookie_monster->GetAllCookiesForURLWithOptionsAsync(
88 wallet::GetPassiveAuthUrl().GetWithEmptyPath(),
90 base::Bind(&GetGoogleCookiesCallback, callback));
95 WalletSigninHelper::WalletSigninHelper(
96 WalletSigninHelperDelegate* delegate,
97 net::URLRequestContextGetter* getter)
98 : delegate_(delegate),
101 weak_ptr_factory_(this) {
105 WalletSigninHelper::~WalletSigninHelper() {
108 void WalletSigninHelper::StartPassiveSignin() {
109 DCHECK_EQ(IDLE, state_);
110 DCHECK(!url_fetcher_);
112 state_ = PASSIVE_EXECUTING_SIGNIN;
114 const GURL& url = wallet::GetPassiveAuthUrl();
115 url_fetcher_.reset(net::URLFetcher::Create(
116 0, url, net::URLFetcher::GET, this));
117 url_fetcher_->SetRequestContext(getter_);
118 url_fetcher_->Start();
121 void WalletSigninHelper::StartUserNameFetch() {
122 DCHECK_EQ(state_, IDLE);
123 DCHECK(!url_fetcher_);
125 state_ = USERNAME_FETCHING_USERINFO;
127 StartFetchingUserNameFromSession();
130 void WalletSigninHelper::StartWalletCookieValueFetch() {
131 scoped_refptr<net::URLRequestContextGetter> request_context(getter_);
132 if (!request_context.get()) {
133 ReturnWalletCookieValue(std::string());
137 base::Callback<void(const std::string&)> callback = base::Bind(
138 &WalletSigninHelper::ReturnWalletCookieValue,
139 weak_ptr_factory_.GetWeakPtr());
141 base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
142 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task);
145 std::string WalletSigninHelper::GetGetAccountInfoUrlForTesting() const {
146 return base::StringPrintf(kGetAccountInfoUrlFormat, 0);
149 void WalletSigninHelper::OnServiceError(const GoogleServiceAuthError& error) {
150 const State state_with_error = state_;
152 url_fetcher_.reset();
154 switch(state_with_error) {
159 case PASSIVE_EXECUTING_SIGNIN: /*FALLTHROUGH*/
160 case PASSIVE_FETCHING_USERINFO:
161 delegate_->OnPassiveSigninFailure(error);
164 case USERNAME_FETCHING_USERINFO:
165 delegate_->OnUserNameFetchFailure(error);
170 void WalletSigninHelper::OnOtherError() {
171 OnServiceError(GoogleServiceAuthError::AuthErrorNone());
174 void WalletSigninHelper::OnURLFetchComplete(
175 const net::URLFetcher* fetcher) {
176 DCHECK_EQ(url_fetcher_.get(), fetcher);
177 if (!fetcher->GetStatus().is_success() ||
178 fetcher->GetResponseCode() < 200 ||
179 fetcher->GetResponseCode() >= 300) {
180 LOG(ERROR) << "URLFetchFailure: state=" << state_
181 << " r=" << fetcher->GetResponseCode()
182 << " s=" << fetcher->GetStatus().status()
183 << " e=" << fetcher->GetStatus().error();
189 case USERNAME_FETCHING_USERINFO: /*FALLTHROUGH*/
190 case PASSIVE_FETCHING_USERINFO:
191 ProcessGetAccountInfoResponseAndFinish();
194 case PASSIVE_EXECUTING_SIGNIN:
195 if (ParseSignInResponse()) {
196 url_fetcher_.reset();
197 state_ = PASSIVE_FETCHING_USERINFO;
198 StartFetchingUserNameFromSession();
203 NOTREACHED() << "unexpected state_=" << state_;
207 void WalletSigninHelper::StartFetchingUserNameFromSession() {
208 const int random_number = static_cast<int>(base::RandUint64() % INT_MAX);
210 net::URLFetcher::Create(
212 GURL(base::StringPrintf(kGetAccountInfoUrlFormat, random_number)),
213 net::URLFetcher::GET,
215 url_fetcher_->SetRequestContext(getter_);
216 url_fetcher_->Start(); // This will result in OnURLFetchComplete callback.
219 void WalletSigninHelper::ProcessGetAccountInfoResponseAndFinish() {
220 if (!ParseGetAccountInfoResponse(url_fetcher_.get(), &emails_)) {
221 LOG(ERROR) << "failed to get the user emails";
226 const State finishing_state = state_;
228 url_fetcher_.reset();
229 switch(finishing_state) {
230 case USERNAME_FETCHING_USERINFO:
231 delegate_->OnUserNameFetchSuccess(emails_);
234 case PASSIVE_FETCHING_USERINFO:
235 delegate_->OnPassiveSigninSuccess(emails_);
239 NOTREACHED() << "unexpected state_=" << finishing_state;
243 bool WalletSigninHelper::ParseSignInResponse() {
250 if (!url_fetcher_->GetResponseAsString(&data)) {
251 DVLOG(1) << "failed to GetResponseAsString";
256 if (!LowerCaseEqualsASCII(data, "yes")) {
258 GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP));
265 bool WalletSigninHelper::ParseGetAccountInfoResponse(
266 const net::URLFetcher* fetcher, std::vector<std::string>* emails) {
270 if (!fetcher->GetResponseAsString(&data)) {
271 DVLOG(1) << "failed to GetResponseAsString";
275 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
276 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) {
277 DVLOG(1) << "failed to parse JSON response";
281 DictionaryValue* dict = static_cast<base::DictionaryValue*>(value.get());
282 base::ListValue* user_info;
283 if (!dict->GetListWithoutPathExpansion("user_info", &user_info)) {
284 DVLOG(1) << "no user_info in JSON response";
288 if (user_info->empty()) {
289 DVLOG(1) << "empty list in JSON response";
293 // |user_info| will contain each signed in user in the cookie jar.
294 for (size_t i = 0; i < user_info->GetSize(); ++i) {
295 base::DictionaryValue* user_info_detail;
296 if (!user_info->GetDictionary(i, &user_info_detail)) {
297 DVLOG(1) << "malformed dictionary in JSON response";
302 if (!user_info_detail->GetStringWithoutPathExpansion("email", &email) ||
304 DVLOG(1) << "no email in JSON response";
308 emails->push_back(email);
314 void WalletSigninHelper::ReturnWalletCookieValue(
315 const std::string& cookie_value) {
316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
318 delegate_->OnDidFetchWalletCookieValue(cookie_value);
321 } // namespace wallet
322 } // namespace autofill