// Copyright 2014 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 "chromeos/login/auth/online_attempt.h" #include <string> #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop_proxy.h" #include "chromeos/login/auth/auth_attempt_state.h" #include "chromeos/login/auth/auth_attempt_state_resolver.h" #include "chromeos/login/auth/key.h" #include "chromeos/login/auth/user_context.h" #include "components/user_manager/user_type.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_constants.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" namespace chromeos { // static const int OnlineAttempt::kClientLoginTimeoutMs = 10000; OnlineAttempt::OnlineAttempt(AuthAttemptState* current_attempt, AuthAttemptStateResolver* callback) : message_loop_(base::MessageLoopProxy::current()), attempt_(current_attempt), resolver_(callback), try_again_(true), weak_factory_(this) { DCHECK(attempt_->user_type == user_manager::USER_TYPE_REGULAR); } OnlineAttempt::~OnlineAttempt() { // Just to be sure. if (client_fetcher_.get()) client_fetcher_->CancelRequest(); } void OnlineAttempt::Initiate(net::URLRequestContextGetter* request_context) { client_fetcher_.reset(new GaiaAuthFetcher( this, GaiaConstants::kChromeOSSource, request_context)); message_loop_->PostTask( FROM_HERE, base::Bind(&OnlineAttempt::TryClientLogin, weak_factory_.GetWeakPtr())); } void OnlineAttempt::OnClientLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& unused) { VLOG(1) << "Online login successful!"; weak_factory_.InvalidateWeakPtrs(); 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(AuthFailure::AuthFailureNone()); } void OnlineAttempt::OnClientLoginFailure(const GoogleServiceAuthError& error) { weak_factory_.InvalidateWeakPtrs(); 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(AuthFailure::FromNetworkAuthFailure( GoogleServiceAuthError(GoogleServiceAuthError::HOSTED_NOT_ALLOWED))); return; } if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { LOG(WARNING) << "Two factor authenticated. Sync will not work."; TriggerResolve(AuthFailure::AuthFailureNone()); return; } VLOG(2) << "ClientLogin attempt failed with " << error.state(); TriggerResolve(AuthFailure::FromNetworkAuthFailure(error)); } void OnlineAttempt::TryClientLogin() { message_loop_->PostDelayedTask( FROM_HERE, base::Bind(&OnlineAttempt::CancelClientLogin, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kClientLoginTimeoutMs)); client_fetcher_->StartClientLogin( attempt_->user_context.GetUserID(), attempt_->user_context.GetKey()->GetSecret(), GaiaConstants::kSyncService, attempt_->login_token, attempt_->login_captcha, attempt_->hosted_policy()); } bool OnlineAttempt::HasPendingFetch() { return client_fetcher_->HasPendingFetch(); } void OnlineAttempt::CancelRequest() { weak_factory_.InvalidateWeakPtrs(); } void OnlineAttempt::CancelClientLogin() { if (HasPendingFetch()) { LOG(WARNING) << "Canceling ClientLogin attempt."; CancelRequest(); TriggerResolve(AuthFailure(AuthFailure::LOGIN_TIMED_OUT)); } } void OnlineAttempt::TriggerResolve(const AuthFailure& outcome) { attempt_->RecordOnlineLoginStatus(outcome); client_fetcher_.reset(NULL); resolver_->Resolve(); } } // namespace chromeos