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.
5 #include "chrome/browser/chromeos/login/signin/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/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"
22 using content::BrowserThread;
26 // OAuth token request max retry count.
27 const int kMaxRequestAttemptCount = 5;
29 // OAuth token request retry delay in milliseconds.
30 const int kRequestRestartDelay = 3000;
32 // Post merge session verification delay.
34 const int kPostResoreVerificationDelay = 1000;
36 const int kPostResoreVerificationDelay = 1000*60*3;
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;
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"),
56 system_request_context_(system_request_context),
57 user_request_context_(user_request_context),
58 access_token_(oauthlogin_access_token),
63 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
66 void OAuth2LoginVerifier::VerifyUserCookies(Profile* profile) {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
69 // Delay the verification if the network is not connected or on a captive
72 base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification,
74 base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
77 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
80 // Delay the verification if the network is not connected or on a captive
84 &OAuth2LoginVerifier::VerifyProfileTokensImpl, AsWeakPtr(), profile),
85 base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
88 void OAuth2LoginVerifier::VerifyProfileTokensImpl(Profile* profile) {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92 if (access_token_.empty()) {
93 // Fetch /OAuthLogin scoped access token.
94 StartFetchingOAuthLoginAccessToken(profile);
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();
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(),
116 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
117 // No service will fetch us uber auth token.
119 new GaiaAuthFetcher(this,
120 std::string(GaiaConstants::kChromeOSSource),
121 user_request_context_.get()));
122 gaia_fetcher_->StartTokenFetchForUberAuthExchange(access_token_);
126 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
127 const std::string& uber_token) {
128 VLOG(1) << "OAuthLogin(uber_token) successful!";
130 gaia_token_ = uber_token;
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,
142 base::Bind(&Delegate::OnSessionMergeFailure,
143 base::Unretained(delegate_)));
146 void OAuth2LoginVerifier::StartMergeSession() {
147 DCHECK(!gaia_token_.empty());
149 new GaiaAuthFetcher(this,
150 std::string(GaiaConstants::kChromeOSSource),
151 user_request_context_.get()));
152 gaia_fetcher_->StartMergeSession(gaia_token_, std::string());
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();
164 void OAuth2LoginVerifier::SchedulePostMergeVerification() {
165 BrowserThread::PostDelayedTask(
169 &OAuth2LoginVerifier::StartAuthCookiesVerification, AsWeakPtr()),
170 base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay));
173 void OAuth2LoginVerifier::StartAuthCookiesVerification() {
175 new GaiaAuthFetcher(this,
176 std::string(GaiaConstants::kChromeOSSource),
177 user_request_context_.get()));
178 gaia_fetcher_->StartListAccounts();
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.
189 base::Bind(&OAuth2LoginVerifier::StartMergeSession,
191 base::Bind(&Delegate::OnSessionMergeFailure,
192 base::Unretained(delegate_)));
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();
202 VLOG(1) << "Got OAuth2 access token!";
204 access_token_ = access_token;
205 StartOAuthLoginForUberToken();
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();
215 LOG(WARNING) << "Failed to get OAuth2 access token, "
216 << " error: " << error.state();
217 UMA_HISTOGRAM_ENUMERATION(
218 base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
220 GoogleServiceAuthError::NUM_STATES);
221 delegate_->OnSessionMergeFailure(IsConnectionOrServiceError(error));
224 void OAuth2LoginVerifier::OnListAccountsSuccess(
225 const std::string& data) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 VLOG(1) << "ListAccounts successful.";
228 delegate_->OnListAccountsSuccess(data);
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();
239 base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification,
241 base::Bind(&Delegate::OnListAccountsFailure,
242 base::Unretained(delegate_)));
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) {
252 UMA_HISTOGRAM_ENUMERATION(
253 base::StringPrintf("OAuth2Login.%sRetry", operation_id),
255 GoogleServiceAuthError::NUM_STATES);
256 BrowserThread::PostDelayedTask(
257 BrowserThread::UI, FROM_HERE, task_to_retry,
258 base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
262 LOG(WARNING) << "Unrecoverable error or retry count max reached for "
264 UMA_HISTOGRAM_ENUMERATION(
265 base::StringPrintf("OAuth2Login.%sFailure", operation_id),
267 GoogleServiceAuthError::NUM_STATES);
269 error_handler.Run(IsConnectionOrServiceError(error));
272 } // namespace chromeos