| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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/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/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 | |
| 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 | |
| 51 OAuth2LoginManager::~OAuth2LoginManager() { | |
| 52 } | |
| 53 | |
| 54 void OAuth2LoginManager::AddObserver(OAuth2LoginManager::Observer* observer) { | |
| 55 observer_list_.AddObserver(observer); | |
| 56 } | |
| 57 | |
| 58 void OAuth2LoginManager::RemoveObserver( | |
| 59 OAuth2LoginManager::Observer* observer) { | |
| 60 observer_list_.RemoveObserver(observer); | |
| 61 } | |
| 62 | |
| 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 | |
| 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 | |
| 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 | |
| 118 void OAuth2LoginManager::Stop() { | |
| 119 oauth2_token_fetcher_.reset(); | |
| 120 login_verifier_.reset(); | |
| 121 } | |
| 122 | |
| 123 bool OAuth2LoginManager::ShouldBlockTabLoading() { | |
| 124 return state_ == SESSION_RESTORE_PREPARING || | |
| 125 state_ == SESSION_RESTORE_IN_PROGRESS; | |
| 126 } | |
| 127 | |
| 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 | |
| 153 ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() { | |
| 154 return ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_); | |
| 155 } | |
| 156 | |
| 157 const std::string& OAuth2LoginManager::GetPrimaryAccountId() { | |
| 158 SigninManagerBase* signin_manager = | |
| 159 SigninManagerFactory::GetForProfile(user_profile_); | |
| 160 return signin_manager->GetAuthenticatedAccountId(); | |
| 161 } | |
| 162 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 204 void OAuth2LoginManager::OnOAuthError() { | |
| 205 account_id_fetcher_.reset(); | |
| 206 LOG(ERROR) << "Account id fetch failed!"; | |
| 207 SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED); | |
| 208 } | |
| 209 | |
| 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 | |
| 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 | |
| 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 | |
| 245 void OAuth2LoginManager::OnOAuth2TokensFetchFailed() { | |
| 246 LOG(ERROR) << "OAuth2 tokens fetch failed!"; | |
| 247 RecordSessionRestoreOutcome(SESSION_RESTORE_TOKEN_FETCH_FAILED, | |
| 248 SESSION_RESTORE_FAILED); | |
| 249 } | |
| 250 | |
| 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 | |
| 267 void OAuth2LoginManager::RestoreSessionCookies() { | |
| 268 SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS); | |
| 269 login_verifier_->VerifyProfileTokens(user_profile_); | |
| 270 } | |
| 271 | |
| 272 void OAuth2LoginManager::Shutdown() { | |
| 273 GetTokenService()->RemoveObserver(this); | |
| 274 login_verifier_.reset(); | |
| 275 oauth2_token_fetcher_.reset(); | |
| 276 } | |
| 277 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 398 void OAuth2LoginManager::SetSessionRestoreStartForTesting( | |
| 399 const base::Time& time) { | |
| 400 session_restore_start_ = time; | |
| 401 } | |
| 402 | |
| 403 } // namespace chromeos | |
| OLD | NEW |