oauth2_login_verifier.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 22using content::BrowserThread; 23 24namespace { 25 26// OAuth token request max retry count. 27const int kMaxRequestAttemptCount = 5; 28 29// OAuth token request retry delay in milliseconds. 30const int kRequestRestartDelay = 3000; 31 32// Post merge session verification delay. 33#ifndef NDEBUG 34const int kPostResoreVerificationDelay = 1000; 35#else 36const int kPostResoreVerificationDelay = 1000*60*3; 37#endif 38 39bool 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 47namespace chromeos { 48 49OAuth2LoginVerifier::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 63OAuth2LoginVerifier::~OAuth2LoginVerifier() { 64} 65 66void 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 77void 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 88void 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 102void 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 116void 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 126void 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 134void 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 146void 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 155void 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 164void 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 173void 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 181void 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 195void 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 208void 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 224void OAuth2LoginVerifier::OnListAccountsSuccess( 225 const std::string& data) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 VLOG(1) << "ListAccounts successful."; 228 delegate_->OnListAccountsSuccess(data); 229} 230 231void 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 245void 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