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