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.
5 #include "chrome/browser/chromeos/login/oauth2_login_verifier.h"
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"
25 using content::BrowserThread;
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;
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;
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),
55 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
58 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 // Delay the verification if the network is not connected or on a captive
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(
76 &OAuth2LoginVerifier::VerifyProfileTokens, AsWeakPtr(), profile),
77 base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
81 access_token_.clear();
83 StartFetchingOAuthLoginAccessToken(profile);
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(),
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_);
108 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
109 const std::string& uber_token) {
110 LOG(INFO) << "OAuthLogin(uber_token) successful!";
112 gaia_token_ = uber_token;
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,
124 base::Bind(&Delegate::OnOAuthLoginFailure,
125 base::Unretained(delegate_)));
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());
137 void OAuth2LoginVerifier::OnClientLoginSuccess(
138 const ClientLoginResult& gaia_credentials) {
139 LOG(INFO) << "OAuthLogin(SID+LSID) successful!";
141 delegate_->OnOAuthLoginSuccess(gaia_credentials);
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();
150 "OAuthLoginGaiaCred", error,
151 base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForGaiaCredentials,
153 base::Bind(&Delegate::OnOAuthLoginFailure,
154 base::Unretained(delegate_)));
157 void OAuth2LoginVerifier::StartMergeSession() {
158 DCHECK(!gaia_token_.empty());
160 new GaiaAuthFetcher(this,
161 std::string(GaiaConstants::kChromeOSSource),
162 user_request_context_.get()));
163 gaia_fetcher_->StartMergeSession(gaia_token_);
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();
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.
183 base::Bind(&OAuth2LoginVerifier::StartMergeSession,
185 base::Bind(&Delegate::OnSessionMergeFailure,
186 base::Unretained(delegate_)));
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();
196 LOG(INFO) << "Got OAuth2 access token!";
198 access_token_ = access_token;
199 StartOAuthLoginForUberToken();
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();
209 LOG(WARNING) << "Failed to get OAuth2 access token, "
210 << " error: " << error.state();
211 UMA_HISTOGRAM_ENUMERATION(
212 base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
214 GoogleServiceAuthError::NUM_STATES);
215 delegate_->OnOAuthLoginFailure(IsConnectionOrServiceError(error));
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) {
225 UMA_HISTOGRAM_ENUMERATION(
226 base::StringPrintf("OAuth2Login.%sRetry", operation_id),
228 GoogleServiceAuthError::NUM_STATES);
229 BrowserThread::PostDelayedTask(
230 BrowserThread::UI, FROM_HERE, task_to_retry,
231 base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
235 LOG(WARNING) << "Unrecoverable error or retry count max reached for "
237 UMA_HISTOGRAM_ENUMERATION(
238 base::StringPrintf("OAuth2Login.%sFailure", operation_id),
240 GoogleServiceAuthError::NUM_STATES);
242 error_handler.Run(IsConnectionOrServiceError(error));
245 } // namespace chromeos