online_attempt.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
25namespace chromeos {
26
27// static
28const int OnlineAttempt::kClientLoginTimeoutMs = 10000;
29
30OnlineAttempt::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
39OnlineAttempt::~OnlineAttempt() {
40  // Just to be sure.
41  if (gaia_authenticator_.get())
42    gaia_authenticator_->CancelRequest();
43}
44
45void 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
55void 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
80void 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
124void 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
139void 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
151void 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