• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/chromeos/login/signin/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 
IsConnectionOrServiceError(const GoogleServiceAuthError & error)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 
OAuth2LoginVerifier(OAuth2LoginVerifier::Delegate * delegate,net::URLRequestContextGetter * system_request_context,net::URLRequestContextGetter * user_request_context,const std::string & oauthlogin_access_token)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 
~OAuth2LoginVerifier()63 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
64 }
65 
VerifyUserCookies(Profile * profile)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 
VerifyProfileTokens(Profile * profile)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 
VerifyProfileTokensImpl(Profile * profile)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 
StartFetchingOAuthLoginAccessToken(Profile * profile)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 
StartOAuthLoginForUberToken()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 
OnUberAuthTokenSuccess(const std::string & uber_token)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 
OnUberAuthTokenFailure(const GoogleServiceAuthError & error)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 
StartMergeSession()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 
OnMergeSessionSuccess(const std::string & data)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 
SchedulePostMergeVerification()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 
StartAuthCookiesVerification()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 
OnMergeSessionFailure(const GoogleServiceAuthError & error)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 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)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 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)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 
OnListAccountsSuccess(const std::string & data)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 
OnListAccountsFailure(const GoogleServiceAuthError & error)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 
RetryOnError(const char * operation_id,const GoogleServiceAuthError & error,const base::Closure & task_to_retry,const ErrorHandler & error_handler)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
273