• 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_manager.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_util.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/signin/chrome_signin_client_factory.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chromeos/chromeos_switches.h"
21 #include "components/signin/core/browser/profile_oauth2_token_service.h"
22 #include "components/signin/core/browser/signin_client.h"
23 #include "components/signin/core/browser/signin_manager.h"
24 #include "components/user_manager/user_manager.h"
25 #include "google_apis/gaia/gaia_auth_util.h"
26 #include "google_apis/gaia/gaia_constants.h"
27 #include "google_apis/gaia/gaia_urls.h"
28 #include "net/url_request/url_request_context_getter.h"
29 
30 namespace chromeos {
31 
32 namespace {
33 
34 static const char kServiceScopeGetUserInfo[] =
35     "https://www.googleapis.com/auth/userinfo.email";
36 static const int kMaxRetries = 5;
37 
38 }  // namespace
39 
OAuth2LoginManager(Profile * user_profile)40 OAuth2LoginManager::OAuth2LoginManager(Profile* user_profile)
41     : user_profile_(user_profile),
42       restore_strategy_(RESTORE_FROM_COOKIE_JAR),
43       state_(SESSION_RESTORE_NOT_STARTED) {
44   GetTokenService()->AddObserver(this);
45 
46   // For telemetry, we mark session restore completed to avoid warnings from
47   // MergeSessionThrottle.
48   if (CommandLine::ForCurrentProcess()->
49           HasSwitch(chromeos::switches::kDisableGaiaServices)) {
50     SetSessionRestoreState(SESSION_RESTORE_DONE);
51   }
52 }
53 
~OAuth2LoginManager()54 OAuth2LoginManager::~OAuth2LoginManager() {
55 }
56 
AddObserver(OAuth2LoginManager::Observer * observer)57 void OAuth2LoginManager::AddObserver(OAuth2LoginManager::Observer* observer) {
58   observer_list_.AddObserver(observer);
59 }
60 
RemoveObserver(OAuth2LoginManager::Observer * observer)61 void OAuth2LoginManager::RemoveObserver(
62     OAuth2LoginManager::Observer* observer) {
63   observer_list_.RemoveObserver(observer);
64 }
65 
RestoreSession(net::URLRequestContextGetter * auth_request_context,SessionRestoreStrategy restore_strategy,const std::string & oauth2_refresh_token,const std::string & auth_code)66 void OAuth2LoginManager::RestoreSession(
67     net::URLRequestContextGetter* auth_request_context,
68     SessionRestoreStrategy restore_strategy,
69     const std::string& oauth2_refresh_token,
70     const std::string& auth_code) {
71   DCHECK(user_profile_);
72   auth_request_context_ = auth_request_context;
73   restore_strategy_ = restore_strategy;
74   refresh_token_ = oauth2_refresh_token;
75   oauthlogin_access_token_ = std::string();
76   auth_code_ = auth_code;
77   session_restore_start_ = base::Time::Now();
78   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_PREPARING);
79   ContinueSessionRestore();
80 }
81 
ContinueSessionRestore()82 void OAuth2LoginManager::ContinueSessionRestore() {
83   if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR ||
84       restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
85     FetchOAuth2Tokens();
86     return;
87   }
88 
89   // Save passed OAuth2 refresh token.
90   if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
91     DCHECK(!refresh_token_.empty());
92     restore_strategy_ = RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
93     StoreOAuth2Token();
94     return;
95   }
96 
97   DCHECK(restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN);
98   RestoreSessionFromSavedTokens();
99 }
100 
RestoreSessionFromSavedTokens()101 void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
102   ProfileOAuth2TokenService* token_service = GetTokenService();
103   const std::string& primary_account_id = GetPrimaryAccountId();
104   if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
105     VLOG(1) << "OAuth2 refresh token is already loaded.";
106     VerifySessionCookies();
107   } else {
108     VLOG(1) << "Loading OAuth2 refresh token from database.";
109 
110     // Flag user with unknown token status in case there are no saved tokens
111     // and OnRefreshTokenAvailable is not called. Flagging it here would
112     // cause user to go through Gaia in next login to obtain a new refresh
113     // token.
114     user_manager::UserManager::Get()->SaveUserOAuthStatus(
115         primary_account_id, user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
116 
117     token_service->LoadCredentials(primary_account_id);
118   }
119 }
120 
Stop()121 void OAuth2LoginManager::Stop() {
122   oauth2_token_fetcher_.reset();
123   login_verifier_.reset();
124 }
125 
ShouldBlockTabLoading()126 bool OAuth2LoginManager::ShouldBlockTabLoading() {
127   return state_ == SESSION_RESTORE_PREPARING ||
128          state_ == SESSION_RESTORE_IN_PROGRESS;
129 }
130 
OnRefreshTokenAvailable(const std::string & account_id)131 void OAuth2LoginManager::OnRefreshTokenAvailable(
132     const std::string& account_id) {
133   VLOG(1) << "OnRefreshTokenAvailable";
134 
135   if (state_ == SESSION_RESTORE_NOT_STARTED)
136     return;
137 
138   // TODO(fgorski): Once ProfileOAuth2TokenService supports multi-login, make
139   // sure to restore session cookies in the context of the correct account_id.
140 
141   // Do not validate tokens for supervised users, as they don't actually have
142   // oauth2 token.
143   if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) {
144     VLOG(1) << "Logged in as supervised user, skip token validation.";
145     return;
146   }
147   // Only restore session cookies for the primary account in the profile.
148   if (GetPrimaryAccountId() == account_id) {
149     // Token is loaded. Undo the flagging before token loading.
150     user_manager::UserManager::Get()->SaveUserOAuthStatus(
151         account_id, user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
152     VerifySessionCookies();
153   }
154 }
155 
GetTokenService()156 ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() {
157   return ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_);
158 }
159 
GetPrimaryAccountId()160 const std::string& OAuth2LoginManager::GetPrimaryAccountId() {
161   SigninManagerBase* signin_manager =
162       SigninManagerFactory::GetForProfile(user_profile_);
163   return signin_manager->GetAuthenticatedAccountId();
164 }
165 
StoreOAuth2Token()166 void OAuth2LoginManager::StoreOAuth2Token() {
167   const std::string& primary_account_id = GetPrimaryAccountId();
168   if (primary_account_id.empty()) {
169     GetAccountIdOfRefreshToken(refresh_token_);
170     return;
171   }
172 
173   OnGetUserEmailResponse(primary_account_id);
174 }
175 
GetAccountIdOfRefreshToken(const std::string & refresh_token)176 void OAuth2LoginManager::GetAccountIdOfRefreshToken(
177     const std::string& refresh_token) {
178   gaia::OAuthClientInfo client_info;
179   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
180   client_info.client_id = gaia_urls->oauth2_chrome_client_id();
181   client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
182 
183   account_id_fetcher_.reset(new gaia::GaiaOAuthClient(
184       auth_request_context_.get()));
185   account_id_fetcher_->RefreshToken(client_info, refresh_token,
186       std::vector<std::string>(1, kServiceScopeGetUserInfo), kMaxRetries,
187       this);
188 }
189 
OnRefreshTokenResponse(const std::string & access_token,int expires_in_seconds)190 void OAuth2LoginManager::OnRefreshTokenResponse(
191     const std::string& access_token,
192     int expires_in_seconds) {
193   account_id_fetcher_->GetUserEmail(access_token, kMaxRetries, this);
194 }
195 
OnGetUserEmailResponse(const std::string & user_email)196 void OAuth2LoginManager::OnGetUserEmailResponse(
197     const std::string& user_email)  {
198   DCHECK(!refresh_token_.empty());
199   account_id_fetcher_.reset();
200   std::string canonicalized = gaia::CanonicalizeEmail(user_email);
201   GetTokenService()->UpdateCredentials(canonicalized, refresh_token_);
202 
203   FOR_EACH_OBSERVER(Observer, observer_list_,
204                     OnNewRefreshTokenAvaiable(user_profile_));
205 }
206 
OnOAuthError()207 void OAuth2LoginManager::OnOAuthError() {
208   account_id_fetcher_.reset();
209   LOG(ERROR) << "Account id fetch failed!";
210   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
211 }
212 
OnNetworkError(int response_code)213 void OAuth2LoginManager::OnNetworkError(int response_code) {
214   account_id_fetcher_.reset();
215   LOG(ERROR) << "Account id fetch failed! response_code=" << response_code;
216   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
217 }
218 
FetchOAuth2Tokens()219 void OAuth2LoginManager::FetchOAuth2Tokens() {
220   DCHECK(auth_request_context_.get());
221   // If we have authenticated cookie jar, get OAuth1 token first, then fetch
222   // SID/LSID cookies through OAuthLogin call.
223   if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR) {
224     SigninClient* signin_client =
225         ChromeSigninClientFactory::GetForProfile(user_profile_);
226     std::string signin_scoped_device_id =
227         signin_client->GetSigninScopedDeviceId();
228 
229     oauth2_token_fetcher_.reset(
230         new OAuth2TokenFetcher(this, auth_request_context_.get()));
231     oauth2_token_fetcher_->StartExchangeFromCookies(std::string(),
232                                                     signin_scoped_device_id);
233   } else if (restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
234     DCHECK(!auth_code_.empty());
235     oauth2_token_fetcher_.reset(
236         new OAuth2TokenFetcher(this,
237                                g_browser_process->system_request_context()));
238     oauth2_token_fetcher_->StartExchangeFromAuthCode(auth_code_);
239   } else {
240     NOTREACHED();
241     SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
242   }
243 }
244 
OnOAuth2TokensAvailable(const GaiaAuthConsumer::ClientOAuthResult & oauth2_tokens)245 void OAuth2LoginManager::OnOAuth2TokensAvailable(
246     const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
247   VLOG(1) << "OAuth2 tokens fetched";
248   DCHECK(refresh_token_.empty());
249   refresh_token_.assign(oauth2_tokens.refresh_token);
250   oauthlogin_access_token_ = oauth2_tokens.access_token;
251   StoreOAuth2Token();
252 }
253 
OnOAuth2TokensFetchFailed()254 void OAuth2LoginManager::OnOAuth2TokensFetchFailed() {
255   LOG(ERROR) << "OAuth2 tokens fetch failed!";
256   RecordSessionRestoreOutcome(SESSION_RESTORE_TOKEN_FETCH_FAILED,
257                               SESSION_RESTORE_FAILED);
258 }
259 
VerifySessionCookies()260 void OAuth2LoginManager::VerifySessionCookies() {
261   DCHECK(!login_verifier_.get());
262   login_verifier_.reset(
263       new OAuth2LoginVerifier(this,
264                               g_browser_process->system_request_context(),
265                               user_profile_->GetRequestContext(),
266                               oauthlogin_access_token_));
267 
268   if (restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN) {
269     login_verifier_->VerifyUserCookies(user_profile_);
270     return;
271   }
272 
273   RestoreSessionCookies();
274 }
275 
RestoreSessionCookies()276 void OAuth2LoginManager::RestoreSessionCookies() {
277   SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS);
278   login_verifier_->VerifyProfileTokens(user_profile_);
279 }
280 
Shutdown()281 void OAuth2LoginManager::Shutdown() {
282   GetTokenService()->RemoveObserver(this);
283   login_verifier_.reset();
284   oauth2_token_fetcher_.reset();
285 }
286 
OnSessionMergeSuccess()287 void OAuth2LoginManager::OnSessionMergeSuccess() {
288   VLOG(1) << "OAuth2 refresh and/or GAIA token verification succeeded.";
289   RecordSessionRestoreOutcome(SESSION_RESTORE_SUCCESS,
290                               SESSION_RESTORE_DONE);
291 }
292 
OnSessionMergeFailure(bool connection_error)293 void OAuth2LoginManager::OnSessionMergeFailure(bool connection_error) {
294   LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!"
295              << " connection_error: " << connection_error;
296   RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
297                               connection_error ?
298                                   SESSION_RESTORE_CONNECTION_FAILED :
299                                   SESSION_RESTORE_FAILED);
300 }
301 
OnListAccountsSuccess(const std::string & data)302 void OAuth2LoginManager::OnListAccountsSuccess(const std::string& data) {
303   MergeVerificationOutcome outcome = POST_MERGE_SUCCESS;
304   // Let's analyze which accounts we see logged in here:
305   std::vector<std::pair<std::string, bool> > accounts;
306   gaia::ParseListAccountsData(data, &accounts);
307   std::string user_email = gaia::CanonicalizeEmail(GetPrimaryAccountId());
308   if (!accounts.empty()) {
309     bool found = false;
310     bool first = true;
311     for (std::vector<std::pair<std::string, bool> >::const_iterator iter =
312              accounts.begin();
313          iter != accounts.end(); ++iter) {
314       if (gaia::CanonicalizeEmail(iter->first) == user_email) {
315         found = iter->second;
316         break;
317       }
318 
319       first = false;
320     }
321 
322     if (!found)
323       outcome = POST_MERGE_MISSING_PRIMARY_ACCOUNT;
324     else if (!first)
325       outcome = POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT;
326 
327   } else {
328     outcome = POST_MERGE_NO_ACCOUNTS;
329   }
330 
331   bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
332   RecordCookiesCheckOutcome(is_pre_merge, outcome);
333   // If the primary account is missing during the initial cookie freshness
334   // check, try to restore GAIA session cookies form the OAuth2 tokens.
335   if (is_pre_merge) {
336     if (outcome != POST_MERGE_SUCCESS &&
337         outcome != POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT) {
338       RestoreSessionCookies();
339     } else {
340       // We are done with this account, it's GAIA cookies are legit.
341       RecordSessionRestoreOutcome(SESSION_RESTORE_NOT_NEEDED,
342                                   SESSION_RESTORE_DONE);
343     }
344   }
345 }
346 
OnListAccountsFailure(bool connection_error)347 void OAuth2LoginManager::OnListAccountsFailure(bool connection_error) {
348   bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
349   RecordCookiesCheckOutcome(
350       is_pre_merge,
351       connection_error ? POST_MERGE_CONNECTION_FAILED :
352                          POST_MERGE_VERIFICATION_FAILED);
353   if (is_pre_merge) {
354     if (!connection_error) {
355       // If we failed to get account list, our cookies might be stale so we
356       // need to attempt to restore them.
357       RestoreSessionCookies();
358     } else {
359       RecordSessionRestoreOutcome(SESSION_RESTORE_LISTACCOUNTS_FAILED,
360                                   SESSION_RESTORE_CONNECTION_FAILED);
361     }
362   }
363 }
364 
RecordSessionRestoreOutcome(SessionRestoreOutcome outcome,OAuth2LoginManager::SessionRestoreState state)365 void OAuth2LoginManager::RecordSessionRestoreOutcome(
366     SessionRestoreOutcome outcome,
367     OAuth2LoginManager::SessionRestoreState state) {
368   UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
369                             outcome,
370                             SESSION_RESTORE_COUNT);
371   SetSessionRestoreState(state);
372 }
373 
374 // static
RecordCookiesCheckOutcome(bool is_pre_merge,MergeVerificationOutcome outcome)375 void OAuth2LoginManager::RecordCookiesCheckOutcome(
376     bool is_pre_merge,
377     MergeVerificationOutcome outcome) {
378   if (is_pre_merge) {
379     UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PreMergeVerification",
380                               outcome,
381                               POST_MERGE_COUNT);
382   } else {
383     UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PostMergeVerification",
384                               outcome,
385                               POST_MERGE_COUNT);
386   }
387 }
388 
SetSessionRestoreState(OAuth2LoginManager::SessionRestoreState state)389 void OAuth2LoginManager::SetSessionRestoreState(
390     OAuth2LoginManager::SessionRestoreState state) {
391   if (state_ == state)
392     return;
393 
394   state_ = state;
395   if (state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
396     UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToFailure",
397                         base::Time::Now() - session_restore_start_);
398   } else if (state == OAuth2LoginManager::SESSION_RESTORE_DONE) {
399     UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToSuccess",
400                         base::Time::Now() - session_restore_start_);
401   }
402 
403   FOR_EACH_OBSERVER(Observer, observer_list_,
404                     OnSessionRestoreStateChanged(user_profile_, state_));
405 }
406 
SetSessionRestoreStartForTesting(const base::Time & time)407 void OAuth2LoginManager::SetSessionRestoreStartForTesting(
408     const base::Time& time) {
409   session_restore_start_ = time;
410 }
411 
412 }  // namespace chromeos
413