| OLD | NEW |
| (Empty) |
| 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/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_); | |
| 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 | |
| OLD | NEW |