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