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