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