Home | History | Annotate | Download | only in auth
      1 // Copyright 2014 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 "chrome/browser/chromeos/login/auth/online_attempt.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "chrome/browser/chromeos/login/auth/auth_attempt_state.h"
     14 #include "chrome/browser/chromeos/login/auth/auth_attempt_state_resolver.h"
     15 #include "chrome/browser/chromeos/login/auth/key.h"
     16 #include "chrome/browser/chromeos/login/auth/user_context.h"
     17 #include "chrome/browser/chromeos/login/users/user.h"
     18 #include "content/public/browser/browser_context.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "google_apis/gaia/gaia_auth_consumer.h"
     21 #include "google_apis/gaia/gaia_auth_fetcher.h"
     22 #include "google_apis/gaia/gaia_constants.h"
     23 #include "net/base/load_flags.h"
     24 #include "net/base/net_errors.h"
     25 #include "net/url_request/url_request_status.h"
     26 
     27 using content::BrowserThread;
     28 
     29 namespace chromeos {
     30 
     31 // static
     32 const int OnlineAttempt::kClientLoginTimeoutMs = 10000;
     33 
     34 OnlineAttempt::OnlineAttempt(AuthAttemptState* current_attempt,
     35                              AuthAttemptStateResolver* callback)
     36     : attempt_(current_attempt),
     37       resolver_(callback),
     38       weak_factory_(this),
     39       try_again_(true) {
     40   DCHECK(attempt_->user_type == User::USER_TYPE_REGULAR);
     41 }
     42 
     43 OnlineAttempt::~OnlineAttempt() {
     44   // Just to be sure.
     45   if (client_fetcher_.get())
     46     client_fetcher_->CancelRequest();
     47 }
     48 
     49 void OnlineAttempt::Initiate(content::BrowserContext* auth_context) {
     50   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     51   client_fetcher_.reset(
     52       new GaiaAuthFetcher(this, GaiaConstants::kChromeOSSource,
     53                           auth_context->GetRequestContext()));
     54   BrowserThread::PostTask(
     55       BrowserThread::UI, FROM_HERE,
     56       base::Bind(&OnlineAttempt::TryClientLogin, weak_factory_.GetWeakPtr()));
     57 }
     58 
     59 void OnlineAttempt::OnClientLoginSuccess(
     60     const GaiaAuthConsumer::ClientLoginResult& unused) {
     61   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     62   VLOG(1) << "Online login successful!";
     63 
     64   weak_factory_.InvalidateWeakPtrs();
     65 
     66   if (attempt_->hosted_policy() == GaiaAuthFetcher::HostedAccountsAllowed &&
     67       attempt_->is_first_time_user()) {
     68     // First time user, and we don't know if the account is HOSTED or not.
     69     // Since we don't allow HOSTED accounts to log in, we need to try
     70     // again, without allowing HOSTED accounts.
     71     //
     72     // NOTE: we used to do this in the opposite order, so that we'd only
     73     // try the HOSTED pathway if GOOGLE-only failed.  This breaks CAPTCHA
     74     // handling, though.
     75     attempt_->DisableHosted();
     76     TryClientLogin();
     77     return;
     78   }
     79   TriggerResolve(LoginFailure::LoginFailureNone());
     80 }
     81 
     82 void OnlineAttempt::OnClientLoginFailure(
     83     const GoogleServiceAuthError& error) {
     84   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     85 
     86   weak_factory_.InvalidateWeakPtrs();
     87 
     88   if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) {
     89     if (try_again_) {
     90       try_again_ = false;
     91       // TODO(cmasone): add UMA tracking for this to see if we can remove it.
     92       LOG(ERROR) << "Login attempt canceled!?!?  Trying again.";
     93       TryClientLogin();
     94       return;
     95     }
     96     LOG(ERROR) << "Login attempt canceled again?  Already retried...";
     97   }
     98 
     99   if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS &&
    100       attempt_->is_first_time_user() &&
    101       attempt_->hosted_policy() != GaiaAuthFetcher::HostedAccountsAllowed) {
    102     // This was a first-time login, we already tried allowing HOSTED accounts
    103     // and succeeded.  That we've failed with INVALID_GAIA_CREDENTIALS now
    104     // indicates that the account is HOSTED.
    105     LOG(WARNING) << "Rejecting valid HOSTED account.";
    106     TriggerResolve(LoginFailure::FromNetworkAuthFailure(
    107                        GoogleServiceAuthError(
    108                            GoogleServiceAuthError::HOSTED_NOT_ALLOWED)));
    109     return;
    110   }
    111 
    112   if (error.state() == GoogleServiceAuthError::TWO_FACTOR) {
    113     LOG(WARNING) << "Two factor authenticated. Sync will not work.";
    114     TriggerResolve(LoginFailure::LoginFailureNone());
    115 
    116     return;
    117   }
    118   VLOG(2) << "ClientLogin attempt failed with " << error.state();
    119   TriggerResolve(LoginFailure::FromNetworkAuthFailure(error));
    120 }
    121 
    122 void OnlineAttempt::TryClientLogin() {
    123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    124 
    125   BrowserThread::PostDelayedTask(
    126       BrowserThread::UI, FROM_HERE,
    127       base::Bind(&OnlineAttempt::CancelClientLogin, weak_factory_.GetWeakPtr()),
    128       base::TimeDelta::FromMilliseconds(kClientLoginTimeoutMs));
    129 
    130   client_fetcher_->StartClientLogin(
    131       attempt_->user_context.GetUserID(),
    132       attempt_->user_context.GetKey()->GetSecret(),
    133       GaiaConstants::kSyncService,
    134       attempt_->login_token,
    135       attempt_->login_captcha,
    136       attempt_->hosted_policy());
    137 }
    138 
    139 bool OnlineAttempt::HasPendingFetch() {
    140   return client_fetcher_->HasPendingFetch();
    141 }
    142 
    143 void OnlineAttempt::CancelRequest() {
    144   weak_factory_.InvalidateWeakPtrs();
    145 }
    146 
    147 void OnlineAttempt::CancelClientLogin() {
    148   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    149   if (HasPendingFetch()) {
    150     LOG(WARNING) << "Canceling ClientLogin attempt.";
    151     CancelRequest();
    152 
    153     TriggerResolve(LoginFailure(LoginFailure::LOGIN_TIMED_OUT));
    154   }
    155 }
    156 
    157 void OnlineAttempt::TriggerResolve(
    158     const LoginFailure& outcome) {
    159   attempt_->RecordOnlineLoginStatus(outcome);
    160   client_fetcher_.reset(NULL);
    161   resolver_->Resolve();
    162 }
    163 
    164 }  // namespace chromeos
    165