summaryrefslogtreecommitdiffstats
path: root/chromeos/login/auth/online_attempt.cc
blob: b78c5cd9b30c1af36bfbe5ca79015ebb32fa71c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// 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 "base/bind.h"
#include "base/location.h"
#include "base/logging.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_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_EQ(user_manager::USER_TYPE_REGULAR,
            attempt_->user_context.GetUserType());
}

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