Home | History | Annotate | Download | only in wallet
      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.
      4 
      5 #include "components/autofill/content/browser/wallet/wallet_signin_helper.h"
      6 
      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"
     27 
     28 namespace autofill {
     29 namespace wallet {
     30 
     31 namespace {
     32 
     33 // Toolbar::GetAccountInfo API URL (JSON).
     34 const char kGetAccountInfoUrlFormat[] =
     35     "https://clients1.google.com/tbproxy/getaccountinfo?key=%d&rv=2&requestor=chrome";
     36 
     37 const char kWalletCookieName[] = "gdtoken";
     38 
     39 // Callback for retrieving Google Wallet cookies. |callback| is passed the
     40 // retrieved cookies and posted back to the UI thread. |cookies| is any Google
     41 // Wallet cookies.
     42 void GetGoogleCookiesCallback(
     43     const base::Callback<void(const std::string&)>& callback,
     44     const net::CookieList& cookies) {
     45   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
     46 
     47   std::string wallet_cookie;
     48   for (size_t i = 0; i < cookies.size(); ++i) {
     49     if (LowerCaseEqualsASCII(cookies[i].Name(), kWalletCookieName)) {
     50       wallet_cookie = cookies[i].Value();
     51       break;
     52     }
     53   }
     54   content::BrowserThread::PostTask(content::BrowserThread::UI,
     55                                    FROM_HERE,
     56                                    base::Bind(callback, wallet_cookie));
     57 }
     58 
     59 // Gets Google Wallet cookies. Must be called on the IO thread.
     60 // |request_context_getter| is a getter for the current request context.
     61 // |callback| is called when retrieving cookies is completed.
     62 void GetGoogleCookies(
     63     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     64     const base::Callback<void(const std::string&)>& callback) {
     65   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
     66 
     67   net::URLRequestContext* url_request_context =
     68       request_context_getter->GetURLRequestContext();
     69   if (!url_request_context) {
     70     content::BrowserThread::PostTask(content::BrowserThread::UI,
     71                                      FROM_HERE,
     72                                      base::Bind(callback, std::string()));
     73     return;
     74   }
     75 
     76   net::CookieStore* cookie_store = url_request_context->cookie_store();
     77   if (!cookie_store) {
     78     content::BrowserThread::PostTask(content::BrowserThread::UI,
     79                                      FROM_HERE,
     80                                      base::Bind(callback, std::string()));
     81     return;
     82   }
     83 
     84   net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
     85   if (!cookie_monster) {
     86     content::BrowserThread::PostTask(content::BrowserThread::UI,
     87                                      FROM_HERE,
     88                                      base::Bind(callback, std::string()));
     89     return;
     90   }
     91 
     92   net::CookieOptions cookie_options;
     93   cookie_options.set_include_httponly();
     94   cookie_monster->GetAllCookiesForURLWithOptionsAsync(
     95       wallet::GetPassiveAuthUrl().GetWithEmptyPath(),
     96       cookie_options,
     97       base::Bind(&GetGoogleCookiesCallback, callback));
     98 }
     99 
    100 }  // namespace
    101 
    102 WalletSigninHelper::WalletSigninHelper(
    103     WalletSigninHelperDelegate* delegate,
    104     net::URLRequestContextGetter* getter)
    105     : delegate_(delegate),
    106       getter_(getter),
    107       state_(IDLE),
    108       weak_ptr_factory_(this) {
    109   DCHECK(delegate_);
    110 }
    111 
    112 WalletSigninHelper::~WalletSigninHelper() {
    113 }
    114 
    115 void WalletSigninHelper::StartPassiveSignin() {
    116   DCHECK_EQ(IDLE, state_);
    117   DCHECK(!url_fetcher_);
    118 
    119   state_ = PASSIVE_EXECUTING_SIGNIN;
    120   username_.clear();
    121   const GURL& url = wallet::GetPassiveAuthUrl();
    122   url_fetcher_.reset(net::URLFetcher::Create(
    123       0, url, net::URLFetcher::GET, this));
    124   url_fetcher_->SetRequestContext(getter_);
    125   url_fetcher_->Start();
    126 }
    127 
    128 void WalletSigninHelper::StartUserNameFetch() {
    129   DCHECK_EQ(state_, IDLE);
    130   DCHECK(!url_fetcher_);
    131 
    132   state_ = USERNAME_FETCHING_USERINFO;
    133   username_.clear();
    134   StartFetchingUserNameFromSession();
    135 }
    136 
    137 void WalletSigninHelper::StartWalletCookieValueFetch() {
    138   scoped_refptr<net::URLRequestContextGetter> request_context(getter_);
    139   if (!request_context.get()) {
    140     ReturnWalletCookieValue(std::string());
    141     return;
    142   }
    143 
    144   base::Callback<void(const std::string&)> callback = base::Bind(
    145       &WalletSigninHelper::ReturnWalletCookieValue,
    146       weak_ptr_factory_.GetWeakPtr());
    147 
    148   base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
    149   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task);
    150 }
    151 
    152 std::string WalletSigninHelper::GetGetAccountInfoUrlForTesting() const {
    153   return base::StringPrintf(kGetAccountInfoUrlFormat, 0);
    154 }
    155 
    156 void WalletSigninHelper::OnServiceError(const GoogleServiceAuthError& error) {
    157   const State state_with_error = state_;
    158   state_ = IDLE;
    159   url_fetcher_.reset();
    160 
    161   switch(state_with_error) {
    162     case IDLE:
    163       NOTREACHED();
    164       break;
    165 
    166     case PASSIVE_EXECUTING_SIGNIN:  /*FALLTHROUGH*/
    167     case PASSIVE_FETCHING_USERINFO:
    168       delegate_->OnPassiveSigninFailure(error);
    169       break;
    170 
    171     case USERNAME_FETCHING_USERINFO:
    172       delegate_->OnUserNameFetchFailure(error);
    173       break;
    174   }
    175 }
    176 
    177 void WalletSigninHelper::OnOtherError() {
    178   OnServiceError(GoogleServiceAuthError::AuthErrorNone());
    179 }
    180 
    181 void WalletSigninHelper::OnURLFetchComplete(
    182     const net::URLFetcher* fetcher) {
    183   DCHECK_EQ(url_fetcher_.get(), fetcher);
    184   if (!fetcher->GetStatus().is_success() ||
    185       fetcher->GetResponseCode() < 200 ||
    186       fetcher->GetResponseCode() >= 300) {
    187     LOG(ERROR) << "URLFetchFailure: state=" << state_
    188                << " r=" << fetcher->GetResponseCode()
    189                << " s=" << fetcher->GetStatus().status()
    190                << " e=" << fetcher->GetStatus().error();
    191     OnOtherError();
    192     return;
    193   }
    194 
    195   switch (state_) {
    196     case USERNAME_FETCHING_USERINFO:  /*FALLTHROUGH*/
    197     case PASSIVE_FETCHING_USERINFO:
    198       ProcessGetAccountInfoResponseAndFinish();
    199       break;
    200 
    201     case PASSIVE_EXECUTING_SIGNIN:
    202       if (ParseSignInResponse()) {
    203         url_fetcher_.reset();
    204         state_ = PASSIVE_FETCHING_USERINFO;
    205         StartFetchingUserNameFromSession();
    206       }
    207       break;
    208 
    209     default:
    210       NOTREACHED() << "unexpected state_=" << state_;
    211   }
    212 }
    213 
    214 void WalletSigninHelper::StartFetchingUserNameFromSession() {
    215   const int random_number = static_cast<int>(base::RandUint64() % INT_MAX);
    216   url_fetcher_.reset(
    217       net::URLFetcher::Create(
    218           0,
    219           GURL(base::StringPrintf(kGetAccountInfoUrlFormat, random_number)),
    220           net::URLFetcher::GET,
    221           this));
    222   url_fetcher_->SetRequestContext(getter_);
    223   url_fetcher_->Start();  // This will result in OnURLFetchComplete callback.
    224 }
    225 
    226 void WalletSigninHelper::ProcessGetAccountInfoResponseAndFinish() {
    227   std::string email;
    228   if (!ParseGetAccountInfoResponse(url_fetcher_.get(), &email)) {
    229     LOG(ERROR) << "failed to get the user email";
    230     OnOtherError();
    231     return;
    232   }
    233 
    234   username_ = email;
    235   const State finishing_state = state_;
    236   state_ = IDLE;
    237   url_fetcher_.reset();
    238   switch(finishing_state) {
    239     case USERNAME_FETCHING_USERINFO:
    240       delegate_->OnUserNameFetchSuccess(username_);
    241       break;
    242 
    243     case PASSIVE_FETCHING_USERINFO:
    244       delegate_->OnPassiveSigninSuccess(username_);
    245       break;
    246 
    247     default:
    248       NOTREACHED() << "unexpected state_=" << finishing_state;
    249   }
    250 }
    251 
    252 bool WalletSigninHelper::ParseSignInResponse() {
    253   if (!url_fetcher_) {
    254     NOTREACHED();
    255     return false;
    256   }
    257 
    258   std::string data;
    259   if (!url_fetcher_->GetResponseAsString(&data)) {
    260     DVLOG(1) << "failed to GetResponseAsString";
    261     OnOtherError();
    262     return false;
    263   }
    264 
    265   if (!LowerCaseEqualsASCII(data, "yes")) {
    266     OnServiceError(
    267         GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP));
    268     return false;
    269   }
    270 
    271   return true;
    272 }
    273 
    274 bool WalletSigninHelper::ParseGetAccountInfoResponse(
    275     const net::URLFetcher* fetcher, std::string* email) {
    276   DCHECK(email);
    277 
    278   std::string data;
    279   if (!fetcher->GetResponseAsString(&data)) {
    280     DVLOG(1) << "failed to GetResponseAsString";
    281     return false;
    282   }
    283 
    284   scoped_ptr<base::Value> value(base::JSONReader::Read(data));
    285   if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) {
    286     DVLOG(1) << "failed to parse JSON response";
    287     return false;
    288   }
    289 
    290   DictionaryValue* dict = static_cast<base::DictionaryValue*>(value.get());
    291   base::ListValue* user_info;
    292   if (!dict->GetListWithoutPathExpansion("user_info", &user_info)) {
    293     DVLOG(1) << "no user_info in JSON response";
    294     return false;
    295   }
    296 
    297   // |user_info| will contain each signed in user in the cookie jar.
    298   // We only support the first user at the moment. http://crbug.com/259543
    299   // will change that.
    300   base::DictionaryValue* user_info_detail;
    301   if (!user_info->GetDictionary(0, &user_info_detail)) {
    302     DVLOG(1) << "empty list in JSON response";
    303     return false;
    304   }
    305 
    306   if (!user_info_detail->GetStringWithoutPathExpansion("email", email)) {
    307     DVLOG(1) << "no email in JSON response";
    308     return false;
    309   }
    310 
    311   return !email->empty();
    312 }
    313 
    314 void WalletSigninHelper::ReturnWalletCookieValue(
    315     const std::string& cookie_value) {
    316   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    317 
    318   delegate_->OnDidFetchWalletCookieValue(cookie_value);
    319 }
    320 
    321 }  // namespace wallet
    322 }  // namespace autofill
    323