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 |