- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / oauth2_login_verifier.cc
1 // Copyright (c) 2013 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/oauth2_login_verifier.h"
6
7 #include <vector>
8
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/chromeos/net/network_portal_detector.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service.h"
16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17 #include "chromeos/network/network_handler.h"
18 #include "chromeos/network/network_state.h"
19 #include "chromeos/network/network_state_handler.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 #include "google_apis/gaia/gaia_urls.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
24
25 using content::BrowserThread;
26
27 namespace {
28
29 // OAuth token request max retry count.
30 const int kMaxRequestAttemptCount = 5;
31 // OAuth token request retry delay in milliseconds.
32 const int kRequestRestartDelay = 3000;
33
34 bool IsConnectionOrServiceError(const GoogleServiceAuthError& error) {
35   return error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
36          error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
37          error.state() == GoogleServiceAuthError::REQUEST_CANCELED;
38 }
39
40 }  // namespace
41
42 namespace chromeos {
43
44 OAuth2LoginVerifier::OAuth2LoginVerifier(
45     OAuth2LoginVerifier::Delegate* delegate,
46     net::URLRequestContextGetter* system_request_context,
47     net::URLRequestContextGetter* user_request_context)
48     : delegate_(delegate),
49       system_request_context_(system_request_context),
50       user_request_context_(user_request_context),
51       retry_count_(0) {
52   DCHECK(delegate);
53 }
54
55 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
56 }
57
58 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
59   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60
61   // Delay the verification if the network is not connected or on a captive
62   // portal.
63   const NetworkState* default_network =
64       NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
65   NetworkPortalDetector* detector = NetworkPortalDetector::Get();
66   if (!default_network ||
67       default_network->connection_state() == shill::kStatePortal ||
68       (detector && detector->GetCaptivePortalState(default_network).status !=
69            NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) {
70     // If network is offline, defer the token fetching until online.
71     VLOG(1) << "Network is offline.  Deferring OAuth2 access token fetch.";
72     BrowserThread::PostDelayedTask(
73         BrowserThread::UI,
74         FROM_HERE,
75         base::Bind(
76             &OAuth2LoginVerifier::VerifyProfileTokens, AsWeakPtr(), profile),
77         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
78     return;
79   }
80
81   access_token_.clear();
82   gaia_token_.clear();
83   StartFetchingOAuthLoginAccessToken(profile);
84 }
85
86 void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile* profile) {
87   OAuth2TokenService::ScopeSet scopes;
88   scopes.insert(GaiaUrls::GetInstance()->oauth1_login_scope());
89   ProfileOAuth2TokenService* token_service =
90       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
91   login_token_request_ = token_service->StartRequestWithContext(
92       token_service->GetPrimaryAccountId(),
93       system_request_context_.get(),
94       scopes,
95       this);
96 }
97
98 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
99   // No service will fetch us uber auth token.
100   gaia_system_fetcher_.reset(
101       new GaiaAuthFetcher(this,
102                           std::string(GaiaConstants::kChromeOSSource),
103                           system_request_context_.get()));
104   gaia_system_fetcher_->StartTokenFetchForUberAuthExchange(access_token_);
105 }
106
107
108 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
109     const std::string& uber_token) {
110   LOG(INFO) << "OAuthLogin(uber_token) successful!";
111   retry_count_ = 0;
112   gaia_token_ = uber_token;
113   StartMergeSession();
114 }
115
116 void OAuth2LoginVerifier::OnUberAuthTokenFailure(
117     const GoogleServiceAuthError& error) {
118   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
119   LOG(WARNING) << "OAuthLogin(uber_token) failed,"
120              << " error: " << error.state();
121   RetryOnError("OAuthLoginUberToken", error,
122                base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken,
123                           AsWeakPtr()),
124                base::Bind(&Delegate::OnOAuthLoginFailure,
125                           base::Unretained(delegate_)));
126 }
127
128 void OAuth2LoginVerifier::StartOAuthLoginForGaiaCredentials() {
129   // No service will fetch us uber auth token.
130   gaia_system_fetcher_.reset(
131       new GaiaAuthFetcher(this,
132                           std::string(GaiaConstants::kChromeOSSource),
133                           system_request_context_.get()));
134   gaia_system_fetcher_->StartOAuthLogin(access_token_, EmptyString());
135 }
136
137 void OAuth2LoginVerifier::OnClientLoginSuccess(
138     const ClientLoginResult& gaia_credentials) {
139   LOG(INFO) << "OAuthLogin(SID+LSID) successful!";
140   retry_count_ = 0;
141   delegate_->OnOAuthLoginSuccess(gaia_credentials);
142 }
143
144 void OAuth2LoginVerifier::OnClientLoginFailure(
145     const GoogleServiceAuthError& error) {
146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147   LOG(WARNING) << "OAuthLogin(SID+LSID failed),"
148              << " error: " << error.state();
149   RetryOnError(
150       "OAuthLoginGaiaCred", error,
151       base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForGaiaCredentials,
152                  AsWeakPtr()),
153       base::Bind(&Delegate::OnOAuthLoginFailure,
154                  base::Unretained(delegate_)));
155 }
156
157 void OAuth2LoginVerifier::StartMergeSession() {
158   DCHECK(!gaia_token_.empty());
159   gaia_fetcher_.reset(
160       new GaiaAuthFetcher(this,
161                           std::string(GaiaConstants::kChromeOSSource),
162                           user_request_context_.get()));
163   gaia_fetcher_->StartMergeSession(gaia_token_);
164 }
165
166 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) {
167   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168   LOG(INFO) << "MergeSession successful.";
169   delegate_->OnSessionMergeSuccess();
170   // Get GAIA credentials needed to kick off TokenService and friends.
171   StartOAuthLoginForGaiaCredentials();
172 }
173
174 void OAuth2LoginVerifier::OnMergeSessionFailure(
175     const GoogleServiceAuthError& error) {
176   LOG(WARNING) << "Failed MergeSession request,"
177              << " error: " << error.state();
178   // If MergeSession from GAIA service token fails, retry the session restore
179   // from OAuth2 refresh token. If that failed too, signal the delegate.
180   RetryOnError(
181       "MergeSession",
182       error,
183       base::Bind(&OAuth2LoginVerifier::StartMergeSession,
184                  AsWeakPtr()),
185       base::Bind(&Delegate::OnSessionMergeFailure,
186                  base::Unretained(delegate_)));
187 }
188
189 void OAuth2LoginVerifier::OnGetTokenSuccess(
190     const OAuth2TokenService::Request* request,
191     const std::string& access_token,
192     const base::Time& expiration_time) {
193   DCHECK_EQ(login_token_request_.get(), request);
194   login_token_request_.reset();
195
196   LOG(INFO) << "Got OAuth2 access token!";
197   retry_count_ = 0;
198   access_token_ = access_token;
199   StartOAuthLoginForUberToken();
200 }
201
202 void OAuth2LoginVerifier::OnGetTokenFailure(
203     const OAuth2TokenService::Request* request,
204     const GoogleServiceAuthError& error) {
205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206   DCHECK_EQ(login_token_request_.get(), request);
207   login_token_request_.reset();
208
209   LOG(WARNING) << "Failed to get OAuth2 access token, "
210              << " error: " << error.state();
211   UMA_HISTOGRAM_ENUMERATION(
212       base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
213       error.state(),
214       GoogleServiceAuthError::NUM_STATES);
215   delegate_->OnOAuthLoginFailure(IsConnectionOrServiceError(error));
216 }
217
218 void OAuth2LoginVerifier::RetryOnError(const char* operation_id,
219                                        const GoogleServiceAuthError& error,
220                                        const base::Closure& task_to_retry,
221                                        const ErrorHandler& error_handler) {
222   if (IsConnectionOrServiceError(error) &&
223       retry_count_ < kMaxRequestAttemptCount) {
224     retry_count_++;
225     UMA_HISTOGRAM_ENUMERATION(
226         base::StringPrintf("OAuth2Login.%sRetry", operation_id),
227         error.state(),
228         GoogleServiceAuthError::NUM_STATES);
229     BrowserThread::PostDelayedTask(
230         BrowserThread::UI, FROM_HERE, task_to_retry,
231         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
232     return;
233   }
234
235   LOG(WARNING) << "Unrecoverable error or retry count max reached for "
236              << operation_id;
237   UMA_HISTOGRAM_ENUMERATION(
238       base::StringPrintf("OAuth2Login.%sFailure", operation_id),
239       error.state(),
240       GoogleServiceAuthError::NUM_STATES);
241
242   error_handler.Run(IsConnectionOrServiceError(error));
243 }
244
245 }  // namespace chromeos