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