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 32 // OAuth token request retry delay in milliseconds. 33 const int kRequestRestartDelay = 3000; 34 35 // Post merge session verification delay. 36 #ifndef NDEBUG 37 const int kPostResoreVerificationDelay = 1000; 38 #else 39 const int kPostResoreVerificationDelay = 1000*60*3; 40 #endif 41 42 bool IsConnectionOrServiceError(const GoogleServiceAuthError& error) { 43 return error.state() == GoogleServiceAuthError::CONNECTION_FAILED || 44 error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE || 45 error.state() == GoogleServiceAuthError::REQUEST_CANCELED; 46 } 47 48 } // namespace 49 50 namespace chromeos { 51 52 OAuth2LoginVerifier::OAuth2LoginVerifier( 53 OAuth2LoginVerifier::Delegate* delegate, 54 net::URLRequestContextGetter* system_request_context, 55 net::URLRequestContextGetter* user_request_context) 56 : delegate_(delegate), 57 system_request_context_(system_request_context), 58 user_request_context_(user_request_context), 59 retry_count_(0) { 60 DCHECK(delegate); 61 } 62 63 OAuth2LoginVerifier::~OAuth2LoginVerifier() { 64 } 65 66 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) { 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 68 69 // Delay the verification if the network is not connected or on a captive 70 // portal. 71 const NetworkState* default_network = 72 NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); 73 NetworkPortalDetector* detector = NetworkPortalDetector::Get(); 74 if (!default_network || 75 default_network->connection_state() == shill::kStatePortal || 76 (detector && detector->GetCaptivePortalState(default_network).status != 77 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) { 78 // If network is offline, defer the token fetching until online. 79 VLOG(1) << "Network is offline. Deferring OAuth2 access token fetch."; 80 BrowserThread::PostDelayedTask( 81 BrowserThread::UI, 82 FROM_HERE, 83 base::Bind( 84 &OAuth2LoginVerifier::VerifyProfileTokens, AsWeakPtr(), profile), 85 base::TimeDelta::FromMilliseconds(kRequestRestartDelay)); 86 return; 87 } 88 89 access_token_.clear(); 90 gaia_token_.clear(); 91 StartFetchingOAuthLoginAccessToken(profile); 92 } 93 94 void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile* profile) { 95 OAuth2TokenService::ScopeSet scopes; 96 scopes.insert(GaiaUrls::GetInstance()->oauth1_login_scope()); 97 ProfileOAuth2TokenService* token_service = 98 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 99 login_token_request_ = token_service->StartRequestWithContext( 100 token_service->GetPrimaryAccountId(), 101 system_request_context_.get(), 102 scopes, 103 this); 104 } 105 106 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() { 107 // No service will fetch us uber auth token. 108 gaia_fetcher_.reset( 109 new GaiaAuthFetcher(this, 110 std::string(GaiaConstants::kChromeOSSource), 111 user_request_context_.get())); 112 gaia_fetcher_->StartTokenFetchForUberAuthExchange(access_token_); 113 } 114 115 116 void OAuth2LoginVerifier::OnUberAuthTokenSuccess( 117 const std::string& uber_token) { 118 VLOG(1) << "OAuthLogin(uber_token) successful!"; 119 retry_count_ = 0; 120 gaia_token_ = uber_token; 121 StartMergeSession(); 122 } 123 124 void OAuth2LoginVerifier::OnUberAuthTokenFailure( 125 const GoogleServiceAuthError& error) { 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 127 LOG(WARNING) << "OAuthLogin(uber_token) failed," 128 << " error: " << error.state(); 129 RetryOnError("OAuthLoginUberToken", error, 130 base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken, 131 AsWeakPtr()), 132 base::Bind(&Delegate::OnSessionMergeFailure, 133 base::Unretained(delegate_))); 134 } 135 136 void OAuth2LoginVerifier::StartMergeSession() { 137 DCHECK(!gaia_token_.empty()); 138 gaia_fetcher_.reset( 139 new GaiaAuthFetcher(this, 140 std::string(GaiaConstants::kChromeOSSource), 141 user_request_context_.get())); 142 gaia_fetcher_->StartMergeSession(gaia_token_); 143 } 144 145 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) { 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 147 VLOG(1) << "MergeSession successful."; 148 delegate_->OnSessionMergeSuccess(); 149 // Schedule post-merge verification to analyze how many LSID/SID overruns 150 // were created by the session restore. 151 SchedulePostMergeVerification(); 152 } 153 154 void OAuth2LoginVerifier::SchedulePostMergeVerification() { 155 BrowserThread::PostDelayedTask( 156 BrowserThread::UI, 157 FROM_HERE, 158 base::Bind( 159 &OAuth2LoginVerifier::StartPostRestoreVerification, AsWeakPtr()), 160 base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay)); 161 } 162 163 void OAuth2LoginVerifier::StartPostRestoreVerification() { 164 gaia_fetcher_.reset( 165 new GaiaAuthFetcher(this, 166 std::string(GaiaConstants::kChromeOSSource), 167 user_request_context_.get())); 168 gaia_fetcher_->StartListAccounts(); 169 } 170 171 void OAuth2LoginVerifier::OnMergeSessionFailure( 172 const GoogleServiceAuthError& error) { 173 LOG(WARNING) << "Failed MergeSession request," << " error: " << error.state(); 174 // If MergeSession from GAIA service token fails, retry the session restore 175 // from OAuth2 refresh token. If that failed too, signal the delegate. 176 RetryOnError( 177 "MergeSession", 178 error, 179 base::Bind(&OAuth2LoginVerifier::StartMergeSession, 180 AsWeakPtr()), 181 base::Bind(&Delegate::OnSessionMergeFailure, 182 base::Unretained(delegate_))); 183 } 184 185 void OAuth2LoginVerifier::OnGetTokenSuccess( 186 const OAuth2TokenService::Request* request, 187 const std::string& access_token, 188 const base::Time& expiration_time) { 189 DCHECK_EQ(login_token_request_.get(), request); 190 login_token_request_.reset(); 191 192 VLOG(1) << "Got OAuth2 access token!"; 193 retry_count_ = 0; 194 access_token_ = access_token; 195 StartOAuthLoginForUberToken(); 196 } 197 198 void OAuth2LoginVerifier::OnGetTokenFailure( 199 const OAuth2TokenService::Request* request, 200 const GoogleServiceAuthError& error) { 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 202 DCHECK_EQ(login_token_request_.get(), request); 203 login_token_request_.reset(); 204 205 LOG(WARNING) << "Failed to get OAuth2 access token, " 206 << " error: " << error.state(); 207 UMA_HISTOGRAM_ENUMERATION( 208 base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"), 209 error.state(), 210 GoogleServiceAuthError::NUM_STATES); 211 delegate_->OnSessionMergeFailure(IsConnectionOrServiceError(error)); 212 } 213 214 void OAuth2LoginVerifier::OnListAccountsSuccess( 215 const std::string& data) { 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 217 VLOG(1) << "ListAccounts successful."; 218 delegate_->OnListAccountsSuccess(data); 219 } 220 221 void OAuth2LoginVerifier::OnListAccountsFailure( 222 const GoogleServiceAuthError& error) { 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 LOG(WARNING) << "Failed to get list of session accounts, " 225 << " error: " << error.state(); 226 RetryOnError( 227 "ListAccounts", 228 error, 229 base::Bind(&OAuth2LoginVerifier::StartPostRestoreVerification, 230 AsWeakPtr()), 231 base::Bind(&Delegate::OnListAccountsFailure, 232 base::Unretained(delegate_))); 233 } 234 235 void OAuth2LoginVerifier::RetryOnError(const char* operation_id, 236 const GoogleServiceAuthError& error, 237 const base::Closure& task_to_retry, 238 const ErrorHandler& error_handler) { 239 if (IsConnectionOrServiceError(error) && 240 retry_count_ < kMaxRequestAttemptCount) { 241 retry_count_++; 242 UMA_HISTOGRAM_ENUMERATION( 243 base::StringPrintf("OAuth2Login.%sRetry", operation_id), 244 error.state(), 245 GoogleServiceAuthError::NUM_STATES); 246 BrowserThread::PostDelayedTask( 247 BrowserThread::UI, FROM_HERE, task_to_retry, 248 base::TimeDelta::FromMilliseconds(kRequestRestartDelay)); 249 return; 250 } 251 252 LOG(WARNING) << "Unrecoverable error or retry count max reached for " 253 << operation_id; 254 UMA_HISTOGRAM_ENUMERATION( 255 base::StringPrintf("OAuth2Login.%sFailure", operation_id), 256 error.state(), 257 GoogleServiceAuthError::NUM_STATES); 258 259 error_handler.Run(IsConnectionOrServiceError(error)); 260 } 261 262 } // namespace chromeos 263