1// Copyright 2014 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/signin/oauth2_token_fetcher.h"
6
7#include "base/logging.h"
8#include "base/strings/string_util.h"
9#include "chromeos/network/network_handler.h"
10#include "chromeos/network/network_state.h"
11#include "chromeos/network/network_state_handler.h"
12#include "content/public/browser/browser_thread.h"
13#include "google_apis/gaia/gaia_constants.h"
14#include "google_apis/gaia/google_service_auth_error.h"
15#include "third_party/cros_system_api/dbus/service_constants.h"
16
17using content::BrowserThread;
18
19namespace {
20
21// OAuth token request max retry count.
22const int kMaxRequestAttemptCount = 5;
23// OAuth token request retry delay in milliseconds.
24const int kRequestRestartDelay = 3000;
25
26}  // namespace
27
28namespace chromeos {
29
30OAuth2TokenFetcher::OAuth2TokenFetcher(
31    OAuth2TokenFetcher::Delegate* delegate,
32    net::URLRequestContextGetter* context_getter)
33    : delegate_(delegate),
34      auth_fetcher_(this, GaiaConstants::kChromeSource, context_getter),
35      retry_count_(0) {
36  DCHECK(delegate);
37}
38
39OAuth2TokenFetcher::~OAuth2TokenFetcher() {
40}
41
42void OAuth2TokenFetcher::StartExchangeFromCookies(
43    const std::string& session_index,
44    const std::string& signin_scoped_device_id) {
45  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
46  session_index_ = session_index;
47  signin_scoped_device_id_ = signin_scoped_device_id;
48  // Delay the verification if the network is not connected or on a captive
49  // portal.
50  const NetworkState* default_network =
51      NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
52  if (!default_network ||
53      default_network->connection_state() == shill::kStatePortal) {
54    // If network is offline, defer the token fetching until online.
55    VLOG(1) << "Network is offline.  Deferring OAuth2 token fetch.";
56    BrowserThread::PostDelayedTask(
57        BrowserThread::UI,
58        FROM_HERE,
59        base::Bind(&OAuth2TokenFetcher::StartExchangeFromCookies,
60                   AsWeakPtr(),
61                   session_index,
62                   signin_scoped_device_id),
63        base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
64    return;
65  }
66  auth_fetcher_.StartCookieForOAuthLoginTokenExchangeWithDeviceId(
67      session_index, signin_scoped_device_id);
68}
69
70void OAuth2TokenFetcher::StartExchangeFromAuthCode(
71    const std::string& auth_code) {
72  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73  auth_code_ = auth_code;
74  // Delay the verification if the network is not connected or on a captive
75  // portal.
76  const NetworkState* default_network =
77      NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
78  if (!default_network ||
79      default_network->connection_state() == shill::kStatePortal) {
80    // If network is offline, defer the token fetching until online.
81    VLOG(1) << "Network is offline.  Deferring OAuth2 token fetch.";
82    BrowserThread::PostDelayedTask(
83        BrowserThread::UI, FROM_HERE,
84        base::Bind(&OAuth2TokenFetcher::StartExchangeFromAuthCode,
85                   AsWeakPtr(),
86                   auth_code),
87        base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
88    return;
89  }
90  auth_fetcher_.StartAuthCodeForOAuth2TokenExchange(auth_code);
91}
92
93void OAuth2TokenFetcher::OnClientOAuthSuccess(
94    const GaiaAuthConsumer::ClientOAuthResult& oauth_tokens) {
95  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96  VLOG(1) << "Got OAuth2 tokens!";
97  retry_count_ = 0;
98  oauth_tokens_ = oauth_tokens;
99  delegate_->OnOAuth2TokensAvailable(oauth_tokens_);
100}
101
102void OAuth2TokenFetcher::OnClientOAuthFailure(
103    const GoogleServiceAuthError& error) {
104  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105  RetryOnError(error,
106               auth_code_.empty()
107                   ? base::Bind(&OAuth2TokenFetcher::StartExchangeFromCookies,
108                                AsWeakPtr(),
109                                session_index_,
110                                signin_scoped_device_id_)
111                   : base::Bind(&OAuth2TokenFetcher::StartExchangeFromAuthCode,
112                                AsWeakPtr(),
113                                auth_code_),
114               base::Bind(&Delegate::OnOAuth2TokensFetchFailed,
115                          base::Unretained(delegate_)));
116}
117
118void OAuth2TokenFetcher::RetryOnError(const GoogleServiceAuthError& error,
119                                      const base::Closure& task,
120                                      const base::Closure& error_handler) {
121  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
122  if ((error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
123       error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
124       error.state() == GoogleServiceAuthError::REQUEST_CANCELED) &&
125      retry_count_ < kMaxRequestAttemptCount) {
126    retry_count_++;
127    BrowserThread::PostDelayedTask(
128        BrowserThread::UI, FROM_HERE, task,
129        base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
130    return;
131  }
132  LOG(ERROR) << "Unrecoverable error or retry count max reached. State: "
133             << error.state() << ", network error: " << error.network_error()
134             << ", message: " << error.error_message();
135  error_handler.Run();
136}
137
138}  // namespace chromeos
139