• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/network_portal_detector.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service.h"
16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17 #include "chromeos/network/network_handler.h"
18 #include "chromeos/network/network_state.h"
19 #include "chromeos/network/network_state_handler.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 #include "google_apis/gaia/gaia_urls.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
24 
25 using content::BrowserThread;
26 
27 namespace {
28 
29 // OAuth token request max retry count.
30 const int kMaxRequestAttemptCount = 5;
31 
32 // OAuth token request retry delay in milliseconds.
33 const int kRequestRestartDelay = 3000;
34 
35 // Post merge session verification delay.
36 #ifndef NDEBUG
37 const int kPostResoreVerificationDelay = 1000;
38 #else
39 const int kPostResoreVerificationDelay = 1000*60*3;
40 #endif
41 
IsConnectionOrServiceError(const GoogleServiceAuthError & error)42 bool IsConnectionOrServiceError(const GoogleServiceAuthError& error) {
43   return error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
44          error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
45          error.state() == GoogleServiceAuthError::REQUEST_CANCELED;
46 }
47 
48 }  // namespace
49 
50 namespace chromeos {
51 
OAuth2LoginVerifier(OAuth2LoginVerifier::Delegate * delegate,net::URLRequestContextGetter * system_request_context,net::URLRequestContextGetter * user_request_context)52 OAuth2LoginVerifier::OAuth2LoginVerifier(
53     OAuth2LoginVerifier::Delegate* delegate,
54     net::URLRequestContextGetter* system_request_context,
55     net::URLRequestContextGetter* user_request_context)
56     : delegate_(delegate),
57       system_request_context_(system_request_context),
58       user_request_context_(user_request_context),
59       retry_count_(0) {
60   DCHECK(delegate);
61 }
62 
~OAuth2LoginVerifier()63 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
64 }
65 
VerifyProfileTokens(Profile * profile)66 void OAuth2LoginVerifier::VerifyProfileTokens(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   const NetworkState* default_network =
72       NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
73   NetworkPortalDetector* detector = NetworkPortalDetector::Get();
74   if (!default_network ||
75       default_network->connection_state() == shill::kStatePortal ||
76       (detector && detector->GetCaptivePortalState(default_network).status !=
77            NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) {
78     // If network is offline, defer the token fetching until online.
79     VLOG(1) << "Network is offline.  Deferring OAuth2 access token fetch.";
80     BrowserThread::PostDelayedTask(
81         BrowserThread::UI,
82         FROM_HERE,
83         base::Bind(
84             &OAuth2LoginVerifier::VerifyProfileTokens, AsWeakPtr(), profile),
85         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
86     return;
87   }
88 
89   access_token_.clear();
90   gaia_token_.clear();
91   StartFetchingOAuthLoginAccessToken(profile);
92 }
93 
StartFetchingOAuthLoginAccessToken(Profile * profile)94 void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile* profile) {
95   OAuth2TokenService::ScopeSet scopes;
96   scopes.insert(GaiaUrls::GetInstance()->oauth1_login_scope());
97   ProfileOAuth2TokenService* token_service =
98       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
99   login_token_request_ = token_service->StartRequestWithContext(
100       token_service->GetPrimaryAccountId(),
101       system_request_context_.get(),
102       scopes,
103       this);
104 }
105 
StartOAuthLoginForUberToken()106 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
107   // No service will fetch us uber auth token.
108   gaia_fetcher_.reset(
109       new GaiaAuthFetcher(this,
110                           std::string(GaiaConstants::kChromeOSSource),
111                           user_request_context_.get()));
112   gaia_fetcher_->StartTokenFetchForUberAuthExchange(access_token_);
113 }
114 
115 
OnUberAuthTokenSuccess(const std::string & uber_token)116 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
117     const std::string& uber_token) {
118   VLOG(1) << "OAuthLogin(uber_token) successful!";
119   retry_count_ = 0;
120   gaia_token_ = uber_token;
121   StartMergeSession();
122 }
123 
OnUberAuthTokenFailure(const GoogleServiceAuthError & error)124 void OAuth2LoginVerifier::OnUberAuthTokenFailure(
125     const GoogleServiceAuthError& error) {
126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
127   LOG(WARNING) << "OAuthLogin(uber_token) failed,"
128                << " error: " << error.state();
129   RetryOnError("OAuthLoginUberToken", error,
130                base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken,
131                           AsWeakPtr()),
132                base::Bind(&Delegate::OnSessionMergeFailure,
133                           base::Unretained(delegate_)));
134 }
135 
StartMergeSession()136 void OAuth2LoginVerifier::StartMergeSession() {
137   DCHECK(!gaia_token_.empty());
138   gaia_fetcher_.reset(
139       new GaiaAuthFetcher(this,
140                           std::string(GaiaConstants::kChromeOSSource),
141                           user_request_context_.get()));
142   gaia_fetcher_->StartMergeSession(gaia_token_);
143 }
144 
OnMergeSessionSuccess(const std::string & data)145 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) {
146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147   VLOG(1) << "MergeSession successful.";
148   delegate_->OnSessionMergeSuccess();
149   // Schedule post-merge verification to analyze how many LSID/SID overruns
150   // were created by the session restore.
151   SchedulePostMergeVerification();
152 }
153 
SchedulePostMergeVerification()154 void OAuth2LoginVerifier::SchedulePostMergeVerification() {
155   BrowserThread::PostDelayedTask(
156       BrowserThread::UI,
157       FROM_HERE,
158       base::Bind(
159           &OAuth2LoginVerifier::StartPostRestoreVerification, AsWeakPtr()),
160       base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay));
161 }
162 
StartPostRestoreVerification()163 void OAuth2LoginVerifier::StartPostRestoreVerification() {
164   gaia_fetcher_.reset(
165       new GaiaAuthFetcher(this,
166                           std::string(GaiaConstants::kChromeOSSource),
167                           user_request_context_.get()));
168   gaia_fetcher_->StartListAccounts();
169 }
170 
OnMergeSessionFailure(const GoogleServiceAuthError & error)171 void OAuth2LoginVerifier::OnMergeSessionFailure(
172     const GoogleServiceAuthError& error) {
173   LOG(WARNING) << "Failed MergeSession request," << " error: " << error.state();
174   // If MergeSession from GAIA service token fails, retry the session restore
175   // from OAuth2 refresh token. If that failed too, signal the delegate.
176   RetryOnError(
177       "MergeSession",
178       error,
179       base::Bind(&OAuth2LoginVerifier::StartMergeSession,
180                  AsWeakPtr()),
181       base::Bind(&Delegate::OnSessionMergeFailure,
182                  base::Unretained(delegate_)));
183 }
184 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)185 void OAuth2LoginVerifier::OnGetTokenSuccess(
186     const OAuth2TokenService::Request* request,
187     const std::string& access_token,
188     const base::Time& expiration_time) {
189   DCHECK_EQ(login_token_request_.get(), request);
190   login_token_request_.reset();
191 
192   VLOG(1) << "Got OAuth2 access token!";
193   retry_count_ = 0;
194   access_token_ = access_token;
195   StartOAuthLoginForUberToken();
196 }
197 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)198 void OAuth2LoginVerifier::OnGetTokenFailure(
199     const OAuth2TokenService::Request* request,
200     const GoogleServiceAuthError& error) {
201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
202   DCHECK_EQ(login_token_request_.get(), request);
203   login_token_request_.reset();
204 
205   LOG(WARNING) << "Failed to get OAuth2 access token, "
206                << " error: " << error.state();
207   UMA_HISTOGRAM_ENUMERATION(
208       base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
209       error.state(),
210       GoogleServiceAuthError::NUM_STATES);
211   delegate_->OnSessionMergeFailure(IsConnectionOrServiceError(error));
212 }
213 
OnListAccountsSuccess(const std::string & data)214 void OAuth2LoginVerifier::OnListAccountsSuccess(
215     const std::string& data) {
216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
217   VLOG(1) << "ListAccounts successful.";
218   delegate_->OnListAccountsSuccess(data);
219 }
220 
OnListAccountsFailure(const GoogleServiceAuthError & error)221 void OAuth2LoginVerifier::OnListAccountsFailure(
222     const GoogleServiceAuthError& error) {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224   LOG(WARNING) << "Failed to get list of session accounts, "
225                << " error: " << error.state();
226   RetryOnError(
227       "ListAccounts",
228       error,
229       base::Bind(&OAuth2LoginVerifier::StartPostRestoreVerification,
230                  AsWeakPtr()),
231       base::Bind(&Delegate::OnListAccountsFailure,
232                  base::Unretained(delegate_)));
233 }
234 
RetryOnError(const char * operation_id,const GoogleServiceAuthError & error,const base::Closure & task_to_retry,const ErrorHandler & error_handler)235 void OAuth2LoginVerifier::RetryOnError(const char* operation_id,
236                                        const GoogleServiceAuthError& error,
237                                        const base::Closure& task_to_retry,
238                                        const ErrorHandler& error_handler) {
239   if (IsConnectionOrServiceError(error) &&
240       retry_count_ < kMaxRequestAttemptCount) {
241     retry_count_++;
242     UMA_HISTOGRAM_ENUMERATION(
243         base::StringPrintf("OAuth2Login.%sRetry", operation_id),
244         error.state(),
245         GoogleServiceAuthError::NUM_STATES);
246     BrowserThread::PostDelayedTask(
247         BrowserThread::UI, FROM_HERE, task_to_retry,
248         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
249     return;
250   }
251 
252   LOG(WARNING) << "Unrecoverable error or retry count max reached for "
253                << operation_id;
254   UMA_HISTOGRAM_ENUMERATION(
255       base::StringPrintf("OAuth2Login.%sFailure", operation_id),
256       error.state(),
257       GoogleServiceAuthError::NUM_STATES);
258 
259   error_handler.Run(IsConnectionOrServiceError(error));
260 }
261 
262 }  // namespace chromeos
263