ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / chromeos / login / signin / oauth2_login_manager.cc
blob5e046a4b5de19f0f0119465f99fc3f2b1b9e13e1
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.
5 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
7 #include <utility>
8 #include <vector>
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/signin/account_tracker_service_factory.h"
14 #include "chrome/browser/signin/chrome_signin_client_factory.h"
15 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17 #include "chrome/browser/signin/signin_manager_factory.h"
18 #include "chromeos/chromeos_switches.h"
19 #include "components/signin/core/browser/account_tracker_service.h"
20 #include "components/signin/core/browser/profile_oauth2_token_service.h"
21 #include "components/signin/core/browser/signin_client.h"
22 #include "components/signin/core/browser/signin_manager.h"
23 #include "components/user_manager/user.h"
24 #include "components/user_manager/user_manager.h"
25 #include "google_apis/gaia/gaia_auth_util.h"
26 #include "google_apis/gaia/gaia_urls.h"
28 namespace chromeos {
30 namespace {
32 static const char kServiceScopeGetUserInfo[] =
33 "https://www.googleapis.com/auth/userinfo.email";
34 static const int kMaxRetries = 5;
36 } // namespace
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);
44 // For telemetry, we mark session restore completed to avoid warnings from
45 // MergeSessionThrottle.
46 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
47 chromeos::switches::kDisableGaiaServices)) {
48 SetSessionRestoreState(SESSION_RESTORE_DONE);
52 OAuth2LoginManager::~OAuth2LoginManager() {
55 void OAuth2LoginManager::AddObserver(OAuth2LoginManager::Observer* observer) {
56 observer_list_.AddObserver(observer);
59 void OAuth2LoginManager::RemoveObserver(
60 OAuth2LoginManager::Observer* observer) {
61 observer_list_.RemoveObserver(observer);
64 void OAuth2LoginManager::RestoreSession(
65 net::URLRequestContextGetter* auth_request_context,
66 SessionRestoreStrategy restore_strategy,
67 const std::string& oauth2_refresh_token,
68 const std::string& oauth2_access_token) {
69 DCHECK(user_profile_);
70 auth_request_context_ = auth_request_context;
71 restore_strategy_ = restore_strategy;
72 refresh_token_ = oauth2_refresh_token;
73 oauthlogin_access_token_ = oauth2_access_token;
74 session_restore_start_ = base::Time::Now();
75 ContinueSessionRestore();
78 void OAuth2LoginManager::ContinueSessionRestore() {
79 SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_PREPARING);
80 if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR) {
81 FetchOAuth2Tokens();
82 return;
85 // Save passed OAuth2 refresh token.
86 if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
87 DCHECK(!refresh_token_.empty());
88 restore_strategy_ = RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
89 StoreOAuth2Token();
90 return;
93 DCHECK(restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN);
94 RestoreSessionFromSavedTokens();
97 void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
98 ProfileOAuth2TokenService* token_service = GetTokenService();
99 const std::string& primary_account_id = GetPrimaryAccountId();
100 if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
101 VLOG(1) << "OAuth2 refresh token is already loaded.";
102 VerifySessionCookies();
103 } else {
104 VLOG(1) << "Loading OAuth2 refresh token from database.";
106 // Flag user with unknown token status in case there are no saved tokens
107 // and OnRefreshTokenAvailable is not called. Flagging it here would
108 // cause user to go through Gaia in next login to obtain a new refresh
109 // token.
110 user_manager::UserManager::Get()->SaveUserOAuthStatus(
111 primary_account_id, user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
113 token_service->LoadCredentials(primary_account_id);
117 void OAuth2LoginManager::Stop() {
118 oauth2_token_fetcher_.reset();
119 login_verifier_.reset();
122 bool OAuth2LoginManager::SessionRestoreIsRunning() const {
123 return state_ == SESSION_RESTORE_PREPARING ||
124 state_ == SESSION_RESTORE_IN_PROGRESS;
127 bool OAuth2LoginManager::ShouldBlockTabLoading() const {
128 return SessionRestoreIsRunning();
131 void OAuth2LoginManager::OnRefreshTokenAvailable(
132 const std::string& account_id) {
133 VLOG(1) << "OnRefreshTokenAvailable";
135 if (state_ == SESSION_RESTORE_NOT_STARTED)
136 return;
138 // TODO(fgorski): Once ProfileOAuth2TokenService supports multi-login, make
139 // sure to restore session cookies in the context of the correct account_id.
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;
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();
156 ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() {
157 return ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_);
160 const std::string& OAuth2LoginManager::GetPrimaryAccountId() {
161 SigninManagerBase* signin_manager =
162 SigninManagerFactory::GetForProfile(user_profile_);
163 return signin_manager->GetAuthenticatedAccountId();
166 void OAuth2LoginManager::StoreOAuth2Token() {
167 const std::string& primary_account_id = GetPrimaryAccountId();
168 if (primary_account_id.empty()) {
169 GetAccountInfoOfRefreshToken(refresh_token_);
170 return;
173 UpdateCredentials(primary_account_id);
176 void OAuth2LoginManager::GetAccountInfoOfRefreshToken(
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();
183 account_info_fetcher_.reset(new gaia::GaiaOAuthClient(
184 auth_request_context_.get()));
185 account_info_fetcher_->RefreshToken(client_info, refresh_token,
186 std::vector<std::string>(1, kServiceScopeGetUserInfo), kMaxRetries,
187 this);
190 void OAuth2LoginManager::UpdateCredentials(const std::string& account_id) {
191 DCHECK(!account_id.empty());
192 DCHECK(!refresh_token_.empty());
193 // |account_id| is assumed to be already canonicalized if it's an email.
194 GetTokenService()->UpdateCredentials(account_id, refresh_token_);
196 FOR_EACH_OBSERVER(Observer, observer_list_,
197 OnNewRefreshTokenAvaiable(user_profile_));
200 void OAuth2LoginManager::OnRefreshTokenResponse(
201 const std::string& access_token,
202 int expires_in_seconds) {
203 account_info_fetcher_->GetUserInfo(access_token, kMaxRetries, this);
206 void OAuth2LoginManager::OnGetUserInfoResponse(
207 scoped_ptr<base::DictionaryValue> user_info) {
208 account_info_fetcher_.reset();
210 std::string gaia_id;
211 std::string email;
212 user_info->GetString("id", &gaia_id);
213 user_info->GetString("email", &email);
215 AccountTrackerService* account_tracker =
216 AccountTrackerServiceFactory::GetForProfile(user_profile_);
217 account_tracker->SeedAccountInfo(gaia_id, email);
218 UpdateCredentials(account_tracker->PickAccountIdForAccount(gaia_id, email));
221 void OAuth2LoginManager::OnOAuthError() {
222 account_info_fetcher_.reset();
223 LOG(ERROR) << "Account info fetch failed!";
224 SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
227 void OAuth2LoginManager::OnNetworkError(int response_code) {
228 account_info_fetcher_.reset();
229 LOG(ERROR) << "Account info fetch failed! response_code=" << response_code;
230 SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
233 void OAuth2LoginManager::FetchOAuth2Tokens() {
234 DCHECK(auth_request_context_.get());
235 if (restore_strategy_ != RESTORE_FROM_COOKIE_JAR) {
236 NOTREACHED();
237 SetSessionRestoreState(SESSION_RESTORE_FAILED);
238 return;
241 // If we have authenticated cookie jar, get OAuth1 token first, then fetch
242 // SID/LSID cookies through OAuthLogin call.
243 SigninClient* signin_client =
244 ChromeSigninClientFactory::GetForProfile(user_profile_);
245 std::string signin_scoped_device_id =
246 signin_client->GetSigninScopedDeviceId();
248 oauth2_token_fetcher_.reset(
249 new OAuth2TokenFetcher(this, auth_request_context_.get()));
250 oauth2_token_fetcher_->StartExchangeFromCookies(std::string(),
251 signin_scoped_device_id);
254 void OAuth2LoginManager::OnOAuth2TokensAvailable(
255 const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
256 VLOG(1) << "OAuth2 tokens fetched";
257 DCHECK(refresh_token_.empty());
258 refresh_token_.assign(oauth2_tokens.refresh_token);
259 oauthlogin_access_token_ = oauth2_tokens.access_token;
260 StoreOAuth2Token();
263 void OAuth2LoginManager::OnOAuth2TokensFetchFailed() {
264 LOG(ERROR) << "OAuth2 tokens fetch failed!";
265 RecordSessionRestoreOutcome(SESSION_RESTORE_TOKEN_FETCH_FAILED,
266 SESSION_RESTORE_FAILED);
269 void OAuth2LoginManager::VerifySessionCookies() {
270 DCHECK(!login_verifier_.get());
271 login_verifier_.reset(new OAuth2LoginVerifier(
272 this, GaiaCookieManagerServiceFactory::GetForProfile(user_profile_),
273 GetPrimaryAccountId(), oauthlogin_access_token_));
275 if (restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN) {
276 login_verifier_->VerifyUserCookies();
277 return;
280 RestoreSessionCookies();
283 void OAuth2LoginManager::RestoreSessionCookies() {
284 SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS);
285 login_verifier_->VerifyProfileTokens();
288 void OAuth2LoginManager::Shutdown() {
289 GetTokenService()->RemoveObserver(this);
290 login_verifier_.reset();
291 oauth2_token_fetcher_.reset();
294 void OAuth2LoginManager::OnSessionMergeSuccess() {
295 VLOG(1) << "OAuth2 refresh and/or GAIA token verification succeeded.";
296 RecordSessionRestoreOutcome(SESSION_RESTORE_SUCCESS,
297 SESSION_RESTORE_DONE);
300 void OAuth2LoginManager::OnSessionMergeFailure(bool connection_error) {
301 LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!"
302 << " connection_error: " << connection_error;
303 RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
304 connection_error ?
305 SESSION_RESTORE_CONNECTION_FAILED :
306 SESSION_RESTORE_FAILED);
309 void OAuth2LoginManager::OnListAccountsSuccess(
310 const std::vector<gaia::ListedAccount>& accounts) {
311 MergeVerificationOutcome outcome = POST_MERGE_SUCCESS;
312 // Let's analyze which accounts we see logged in here:
313 std::string user_email = gaia::CanonicalizeEmail(GetPrimaryAccountId());
314 if (!accounts.empty()) {
315 bool found = false;
316 bool first = true;
317 for (std::vector<gaia::ListedAccount>::const_iterator iter =
318 accounts.begin();
319 iter != accounts.end(); ++iter) {
320 if (iter->email == user_email) {
321 found = iter->valid;
322 break;
325 first = false;
328 if (!found)
329 outcome = POST_MERGE_MISSING_PRIMARY_ACCOUNT;
330 else if (!first)
331 outcome = POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT;
333 } else {
334 outcome = POST_MERGE_NO_ACCOUNTS;
337 bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
338 RecordCookiesCheckOutcome(is_pre_merge, outcome);
339 // If the primary account is missing during the initial cookie freshness
340 // check, try to restore GAIA session cookies form the OAuth2 tokens.
341 if (is_pre_merge) {
342 if (outcome != POST_MERGE_SUCCESS &&
343 outcome != POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT) {
344 RestoreSessionCookies();
345 } else {
346 // We are done with this account, it's GAIA cookies are legit.
347 RecordSessionRestoreOutcome(SESSION_RESTORE_NOT_NEEDED,
348 SESSION_RESTORE_DONE);
353 void OAuth2LoginManager::OnListAccountsFailure(bool connection_error) {
354 bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
355 RecordCookiesCheckOutcome(
356 is_pre_merge,
357 connection_error ? POST_MERGE_CONNECTION_FAILED :
358 POST_MERGE_VERIFICATION_FAILED);
359 if (is_pre_merge) {
360 if (!connection_error) {
361 // If we failed to get account list, our cookies might be stale so we
362 // need to attempt to restore them.
363 RestoreSessionCookies();
364 } else {
365 RecordSessionRestoreOutcome(SESSION_RESTORE_LISTACCOUNTS_FAILED,
366 SESSION_RESTORE_CONNECTION_FAILED);
371 void OAuth2LoginManager::RecordSessionRestoreOutcome(
372 SessionRestoreOutcome outcome,
373 OAuth2LoginManager::SessionRestoreState state) {
374 UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
375 outcome,
376 SESSION_RESTORE_COUNT);
377 SetSessionRestoreState(state);
380 // static
381 void OAuth2LoginManager::RecordCookiesCheckOutcome(
382 bool is_pre_merge,
383 MergeVerificationOutcome outcome) {
384 if (is_pre_merge) {
385 UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PreMergeVerification",
386 outcome,
387 POST_MERGE_COUNT);
388 } else {
389 UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PostMergeVerification",
390 outcome,
391 POST_MERGE_COUNT);
395 void OAuth2LoginManager::SetSessionRestoreState(
396 OAuth2LoginManager::SessionRestoreState state) {
397 if (state_ == state)
398 return;
400 state_ = state;
401 if (state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
402 UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToFailure",
403 base::Time::Now() - session_restore_start_);
404 } else if (state == OAuth2LoginManager::SESSION_RESTORE_DONE) {
405 UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToSuccess",
406 base::Time::Now() - session_restore_start_);
409 FOR_EACH_OBSERVER(Observer, observer_list_,
410 OnSessionRestoreStateChanged(user_profile_, state_));
413 void OAuth2LoginManager::SetSessionRestoreStartForTesting(
414 const base::Time& time) {
415 session_restore_start_ = time;
418 } // namespace chromeos