// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/login/online_attempt.h" #include <string> #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/auth_attempt_state.h" #include "chrome/browser/chromeos/login/auth_attempt_state_resolver.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" #include "chrome/common/net/gaia/gaia_auth_fetcher.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "content/browser/browser_thread.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" #include "third_party/libjingle/source/talk/base/urlencode.h" namespace chromeos { // static const int OnlineAttempt::kClientLoginTimeoutMs = 10000; OnlineAttempt::OnlineAttempt(AuthAttemptState* current_attempt, AuthAttemptStateResolver* callback) : attempt_(current_attempt), resolver_(callback), fetch_canceler_(NULL), try_again_(true) { CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); } OnlineAttempt::~OnlineAttempt() { // Just to be sure. if (gaia_authenticator_.get()) gaia_authenticator_->CancelRequest(); } void OnlineAttempt::Initiate(Profile* profile) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); gaia_authenticator_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeOSSource, profile->GetRequestContext())); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &OnlineAttempt::TryClientLogin)); } void OnlineAttempt::OnClientLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& credentials) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); VLOG(1) << "Online login successful!"; if (fetch_canceler_) { fetch_canceler_->Cancel(); fetch_canceler_ = NULL; } if (attempt_->hosted_policy() == GaiaAuthFetcher::HostedAccountsAllowed && attempt_->is_first_time_user()) { // First time user, and we don't know if the account is HOSTED or not. // Since we don't allow HOSTED accounts to log in, we need to try // again, without allowing HOSTED accounts. // // NOTE: we used to do this in the opposite order, so that we'd only // try the HOSTED pathway if GOOGLE-only failed. This breaks CAPTCHA // handling, though. attempt_->DisableHosted(); TryClientLogin(); return; } TriggerResolve(credentials, LoginFailure::None()); } void OnlineAttempt::OnClientLoginFailure( const GoogleServiceAuthError& error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (fetch_canceler_) { fetch_canceler_->Cancel(); fetch_canceler_ = NULL; } if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) { if (try_again_) { try_again_ = false; // TODO(cmasone): add UMA tracking for this to see if we can remove it. LOG(ERROR) << "Login attempt canceled!?!? Trying again."; TryClientLogin(); return; } LOG(ERROR) << "Login attempt canceled again? Already retried..."; } if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS && attempt_->is_first_time_user() && attempt_->hosted_policy() != GaiaAuthFetcher::HostedAccountsAllowed) { // This was a first-time login, we already tried allowing HOSTED accounts // and succeeded. That we've failed with INVALID_GAIA_CREDENTIALS now // indicates that the account is HOSTED. LOG(WARNING) << "Rejecting valid HOSTED account."; TriggerResolve(GaiaAuthConsumer::ClientLoginResult(), LoginFailure::FromNetworkAuthFailure( GoogleServiceAuthError( GoogleServiceAuthError::HOSTED_NOT_ALLOWED))); return; } if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { LOG(WARNING) << "Two factor authenticated. Sync will not work."; TriggerResolve(GaiaAuthConsumer::ClientLoginResult(), LoginFailure::None()); return; } VLOG(2) << "ClientLogin attempt failed with " << error.state(); TriggerResolve(GaiaAuthConsumer::ClientLoginResult(), LoginFailure::FromNetworkAuthFailure(error)); } void OnlineAttempt::TryClientLogin() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); fetch_canceler_ = NewRunnableMethod(this, &OnlineAttempt::CancelClientLogin); BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE, fetch_canceler_, kClientLoginTimeoutMs); gaia_authenticator_->StartClientLogin( attempt_->username, attempt_->password, GaiaConstants::kContactsService, attempt_->login_token, attempt_->login_captcha, attempt_->hosted_policy()); } void OnlineAttempt::CancelClientLogin() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (gaia_authenticator_->HasPendingFetch()) { LOG(WARNING) << "Canceling ClientLogin attempt."; gaia_authenticator_->CancelRequest(); fetch_canceler_ = NULL; TriggerResolve(GaiaAuthConsumer::ClientLoginResult(), LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); } } void OnlineAttempt::TriggerResolve( const GaiaAuthConsumer::ClientLoginResult& credentials, const LoginFailure& outcome) { attempt_->RecordOnlineLoginStatus(credentials, outcome); gaia_authenticator_.reset(NULL); resolver_->Resolve(); } } // namespace chromeos