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::TimeDelta::FromMilliseconds(kRequestRestartDelay
),
73 base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification
,
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
82 DelayNetworkCall(base::TimeDelta::FromMilliseconds(kRequestRestartDelay
),
83 base::Bind(&OAuth2LoginVerifier::VerifyProfileTokensImpl
,
84 AsWeakPtr(), profile
));
87 void OAuth2LoginVerifier::VerifyProfileTokensImpl(Profile
* profile
) {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
91 if (access_token_
.empty()) {
92 // Fetch /OAuthLogin scoped access token.
93 StartFetchingOAuthLoginAccessToken(profile
);
95 // If OAuthLogin-scoped access token already exists (if it's generated
96 // together with freshly minted refresh token), then fetch GAIA uber token.
97 StartOAuthLoginForUberToken();
101 void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile
* profile
) {
102 OAuth2TokenService::ScopeSet scopes
;
103 scopes
.insert(GaiaConstants::kOAuth1LoginScope
);
104 ProfileOAuth2TokenService
* token_service
=
105 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
);
106 SigninManagerBase
* signin_manager
=
107 SigninManagerFactory::GetForProfile(profile
);
108 login_token_request_
= token_service
->StartRequestWithContext(
109 signin_manager
->GetAuthenticatedAccountId(),
110 system_request_context_
.get(),
115 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
116 // No service will fetch us uber auth token.
118 new GaiaAuthFetcher(this,
119 std::string(GaiaConstants::kChromeOSSource
),
120 user_request_context_
.get()));
121 gaia_fetcher_
->StartTokenFetchForUberAuthExchange(access_token_
);
125 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
126 const std::string
& uber_token
) {
127 VLOG(1) << "OAuthLogin(uber_token) successful!";
129 gaia_token_
= uber_token
;
133 void OAuth2LoginVerifier::OnUberAuthTokenFailure(
134 const GoogleServiceAuthError
& error
) {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
136 LOG(WARNING
) << "OAuthLogin(uber_token) failed,"
137 << " error: " << error
.state();
138 RetryOnError("OAuthLoginUberToken", error
,
139 base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken
,
141 base::Bind(&Delegate::OnSessionMergeFailure
,
142 base::Unretained(delegate_
)));
145 void OAuth2LoginVerifier::StartMergeSession() {
146 DCHECK(!gaia_token_
.empty());
148 new GaiaAuthFetcher(this,
149 std::string(GaiaConstants::kChromeOSSource
),
150 user_request_context_
.get()));
151 gaia_fetcher_
->StartMergeSession(gaia_token_
, std::string());
154 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string
& data
) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
156 VLOG(1) << "MergeSession successful.";
157 delegate_
->OnSessionMergeSuccess();
158 // Schedule post-merge verification to analyze how many LSID/SID overruns
159 // were created by the session restore.
160 SchedulePostMergeVerification();
163 void OAuth2LoginVerifier::SchedulePostMergeVerification() {
164 BrowserThread::PostDelayedTask(
168 &OAuth2LoginVerifier::StartAuthCookiesVerification
, AsWeakPtr()),
169 base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay
));
172 void OAuth2LoginVerifier::StartAuthCookiesVerification() {
174 new GaiaAuthFetcher(this,
175 std::string(GaiaConstants::kChromeOSSource
),
176 user_request_context_
.get()));
177 gaia_fetcher_
->StartListAccounts();
180 void OAuth2LoginVerifier::OnMergeSessionFailure(
181 const GoogleServiceAuthError
& error
) {
182 LOG(WARNING
) << "Failed MergeSession request," << " error: " << error
.state();
183 // If MergeSession from GAIA service token fails, retry the session restore
184 // from OAuth2 refresh token. If that failed too, signal the delegate.
188 base::Bind(&OAuth2LoginVerifier::StartMergeSession
,
190 base::Bind(&Delegate::OnSessionMergeFailure
,
191 base::Unretained(delegate_
)));
194 void OAuth2LoginVerifier::OnGetTokenSuccess(
195 const OAuth2TokenService::Request
* request
,
196 const std::string
& access_token
,
197 const base::Time
& expiration_time
) {
198 DCHECK_EQ(login_token_request_
.get(), request
);
199 login_token_request_
.reset();
201 VLOG(1) << "Got OAuth2 access token!";
203 access_token_
= access_token
;
204 StartOAuthLoginForUberToken();
207 void OAuth2LoginVerifier::OnGetTokenFailure(
208 const OAuth2TokenService::Request
* request
,
209 const GoogleServiceAuthError
& error
) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
211 DCHECK_EQ(login_token_request_
.get(), request
);
212 login_token_request_
.reset();
214 LOG(WARNING
) << "Failed to get OAuth2 access token, "
215 << " error: " << error
.state();
216 UMA_HISTOGRAM_ENUMERATION(
217 base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
219 GoogleServiceAuthError::NUM_STATES
);
220 delegate_
->OnSessionMergeFailure(IsConnectionOrServiceError(error
));
223 void OAuth2LoginVerifier::OnListAccountsSuccess(
224 const std::string
& data
) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
226 VLOG(1) << "ListAccounts successful.";
227 delegate_
->OnListAccountsSuccess(data
);
230 void OAuth2LoginVerifier::OnListAccountsFailure(
231 const GoogleServiceAuthError
& error
) {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
233 LOG(WARNING
) << "Failed to get list of session accounts, "
234 << " error: " << error
.state();
238 base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification
,
240 base::Bind(&Delegate::OnListAccountsFailure
,
241 base::Unretained(delegate_
)));
244 void OAuth2LoginVerifier::RetryOnError(const char* operation_id
,
245 const GoogleServiceAuthError
& error
,
246 const base::Closure
& task_to_retry
,
247 const ErrorHandler
& error_handler
) {
248 if (IsConnectionOrServiceError(error
) &&
249 retry_count_
< kMaxRequestAttemptCount
) {
251 UMA_HISTOGRAM_ENUMERATION(
252 base::StringPrintf("OAuth2Login.%sRetry", operation_id
),
254 GoogleServiceAuthError::NUM_STATES
);
255 BrowserThread::PostDelayedTask(
256 BrowserThread::UI
, FROM_HERE
, task_to_retry
,
257 base::TimeDelta::FromMilliseconds(kRequestRestartDelay
));
261 LOG(WARNING
) << "Unrecoverable error or retry count max reached for "
263 UMA_HISTOGRAM_ENUMERATION(
264 base::StringPrintf("OAuth2Login.%sFailure", operation_id
),
266 GoogleServiceAuthError::NUM_STATES
);
268 error_handler
.Run(IsConnectionOrServiceError(error
));
271 } // namespace chromeos