1 // Copyright 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/signin/token_service.h" 6 7 #include "base/basictypes.h" 8 #include "base/command_line.h" 9 #include "base/prefs/pref_service.h" 10 #include "base/strings/string_util.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/signin/signin_manager.h" 14 #include "chrome/browser/signin/signin_manager_factory.h" 15 #include "chrome/browser/webdata/token_web_data.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "chrome/common/pref_names.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_source.h" 21 #include "google_apis/gaia/gaia_auth_fetcher.h" 22 #include "google_apis/gaia/gaia_constants.h" 23 #include "net/url_request/url_request_context_getter.h" 24 25 #if defined(OS_CHROMEOS) 26 #include "base/metrics/histogram.h" 27 #endif 28 29 using content::BrowserThread; 30 using namespace signin_internals_util; 31 32 namespace { 33 34 // List of services that are capable of ClientLogin-based authentication. 35 const char* kServices[] = { 36 GaiaConstants::kSyncService, 37 GaiaConstants::kLSOService 38 }; 39 40 #define FOR_DIAGNOSTICS_OBSERVERS(func) \ 41 do { \ 42 FOR_EACH_OBSERVER(SigninDiagnosticsObserver, \ 43 signin_diagnostics_observers_, \ 44 func); \ 45 } while (0) \ 46 47 } // namespace 48 49 50 TokenService::TokenService() 51 : profile_(NULL), 52 token_loading_query_(0), 53 tokens_loaded_(false) { 54 // Allow constructor to be called outside the UI thread, so it can be mocked 55 // out for unit tests. 56 57 COMPILE_ASSERT(arraysize(kServices) == arraysize(fetchers_), 58 kServices_and_fetchers_dont_have_same_size); 59 } 60 61 TokenService::~TokenService() { 62 } 63 64 void TokenService::Shutdown() { 65 if (!source_.empty()) { 66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 67 ResetCredentialsInMemory(); 68 } 69 token_web_data_ = NULL; 70 } 71 72 void TokenService::Initialize(const char* const source, 73 Profile* profile) { 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 75 if (!source_.empty()) { 76 // Already initialized. 77 return; 78 } 79 DCHECK(!profile_); 80 profile_ = profile; 81 getter_ = profile->GetRequestContext(); 82 // Since the user can create a bookmark in incognito, sync may be running. 83 // Thus we have to go for explicit access. 84 token_web_data_ = TokenWebData::FromBrowserContext(profile); 85 source_ = std::string(source); 86 87 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 88 // Allow the token service to be cleared from the command line. We rely on 89 // SigninManager::Initialize() being called to clear out the 90 // kGoogleServicesUsername pref before we call EraseTokensFromDB() as 91 // otherwise the system would be in an invalid state. 92 if (cmd_line->HasSwitch(switches::kClearTokenService)) 93 EraseTokensFromDB(); 94 95 // Allow a token to be injected from the command line. 96 if (cmd_line->HasSwitch(switches::kSetToken)) { 97 std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken); 98 int separator = value.find(':'); 99 std::string service = value.substr(0, separator); 100 std::string token = value.substr(separator + 1); 101 token_map_[service] = token; 102 SaveAuthTokenToDB(service, token); 103 } 104 } 105 106 void TokenService::AddAuthTokenManually(const std::string& service, 107 const std::string& auth_token) { 108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 109 VLOG(1) << "Got an authorization token for " << service; 110 token_map_[service] = auth_token; 111 FireTokenAvailableNotification(service, auth_token); 112 SaveAuthTokenToDB(service, auth_token); 113 114 // We don't ever want to fetch OAuth2 tokens from LSO service token in case 115 // when ChromeOS is in forced OAuth2 use mode. OAuth2 token should only 116 // arrive into token service exclusively through UpdateCredentialsWithOAuth2. 117 #if !defined(OS_CHROMEOS) 118 // If we got ClientLogin token for "lso" service, and we don't already have 119 // OAuth2 tokens, start fetching OAuth2 login scoped token pair. 120 if (service == GaiaConstants::kLSOService && !HasOAuthLoginToken()) { 121 int index = GetServiceIndex(service); 122 CHECK_GE(index, 0); 123 // iOS fetches the service tokens outside of the TokenService. 124 if (!fetchers_[index].get()) { 125 fetchers_[index].reset(new GaiaAuthFetcher(this, source_, getter_.get())); 126 } 127 fetchers_[index]->StartLsoForOAuthLoginTokenExchange(auth_token); 128 } 129 #endif 130 } 131 132 133 void TokenService::ResetCredentialsInMemory() { 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 135 136 // Terminate any running fetchers. Callbacks will not return. 137 for (size_t i = 0; i < arraysize(kServices); ++i) { 138 fetchers_[i].reset(); 139 } 140 141 // Cancel pending loads. Callbacks will not return. 142 if (token_loading_query_) { 143 token_web_data_->CancelRequest(token_loading_query_); 144 token_loading_query_ = 0; 145 } 146 147 tokens_loaded_ = false; 148 token_map_.clear(); 149 credentials_ = GaiaAuthConsumer::ClientLoginResult(); 150 } 151 152 void TokenService::UpdateCredentials( 153 const GaiaAuthConsumer::ClientLoginResult& credentials) { 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 155 credentials_ = credentials; 156 157 SaveAuthTokenToDB(GaiaConstants::kGaiaLsid, credentials.lsid); 158 SaveAuthTokenToDB(GaiaConstants::kGaiaSid, credentials.sid); 159 160 // Cancel any currently running requests. 161 for (size_t i = 0; i < arraysize(kServices); i++) { 162 fetchers_[i].reset(); 163 } 164 165 // Notify AboutSigninInternals that a new lsid and sid are available. 166 FOR_DIAGNOSTICS_OBSERVERS(NotifySigninValueChanged( 167 signin_internals_util::SID, credentials.sid)); 168 FOR_DIAGNOSTICS_OBSERVERS(NotifySigninValueChanged(LSID, credentials.lsid)); 169 } 170 171 void TokenService::UpdateCredentialsWithOAuth2( 172 const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) { 173 SaveOAuth2Credentials(oauth2_tokens); 174 } 175 176 void TokenService::LoadTokensFromDB() { 177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 178 if (token_web_data_.get()) 179 token_loading_query_ = token_web_data_->GetAllTokens(this); 180 } 181 182 void TokenService::SaveAuthTokenToDB(const std::string& service, 183 const std::string& auth_token) { 184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 185 if (token_web_data_.get()) 186 token_web_data_->SetTokenForService(service, auth_token); 187 } 188 189 void TokenService::EraseTokensFromDB() { 190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 191 if (token_web_data_.get()) 192 token_web_data_->RemoveAllTokens(); 193 194 content::NotificationService::current()->Notify( 195 chrome::NOTIFICATION_TOKENS_CLEARED, 196 content::Source<TokenService>(this), 197 content::NotificationService::NoDetails()); 198 199 // Clear in-memory token values stored by AboutSigninInternals 200 // Note that although this is clearing in-memory values, it belongs here and 201 // not in ResetCredentialsInMemory() (which is invoked both on sign out and 202 // shutdown). 203 for (size_t i = 0; i < kNumTokenPrefs; ++i) { 204 FOR_DIAGNOSTICS_OBSERVERS(NotifyClearStoredToken(kTokenPrefsArray[i])); 205 } 206 207 } 208 209 bool TokenService::TokensLoadedFromDB() const { 210 return tokens_loaded_; 211 } 212 213 // static 214 int TokenService::GetServiceIndex(const std::string& service) { 215 for (size_t i = 0; i < arraysize(kServices); ++i) { 216 if (kServices[i] == service) 217 return i; 218 } 219 return -1; 220 } 221 222 bool TokenService::AreCredentialsValid() const { 223 return !credentials_.lsid.empty() && !credentials_.sid.empty(); 224 } 225 226 void TokenService::StartFetchingTokens() { 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 228 DCHECK(AreCredentialsValid()); 229 for (size_t i = 0; i < arraysize(kServices); i++) { 230 fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_.get())); 231 fetchers_[i]->StartIssueAuthToken( 232 credentials_.sid, credentials_.lsid, kServices[i]); 233 } 234 } 235 236 // Services dependent on a token will check if a token is available. 237 // If it isn't, they'll go to sleep until they get a token event. 238 bool TokenService::HasTokenForService(const char* service) const { 239 return token_map_.count(service) > 0; 240 } 241 242 const std::string& TokenService::GetTokenForService( 243 const char* const service) const { 244 245 if (token_map_.count(service) > 0) { 246 // Note map[key] is not const. 247 return (*token_map_.find(service)).second; 248 } 249 return EmptyString(); 250 } 251 252 bool TokenService::HasOAuthLoginToken() const { 253 return HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken); 254 } 255 256 const std::string& TokenService::GetOAuth2LoginRefreshToken() const { 257 return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken); 258 } 259 260 // static 261 void TokenService::GetServiceNames(std::vector<std::string>* names) { 262 names->resize(arraysize(kServices)); 263 std::copy(kServices, kServices + arraysize(kServices), names->begin()); 264 } 265 266 // Note that this can fire twice or more for any given service. 267 // It can fire once from the DB read, and then once from the initial 268 // fetcher. Future fetches can cause more notification firings. 269 // The DB read will not however fire a notification if the fetcher 270 // returned first. So it's always safe to use the latest notification. 271 void TokenService::FireTokenAvailableNotification( 272 const std::string& service, 273 const std::string& auth_token) { 274 275 TokenAvailableDetails details(service, auth_token); 276 content::NotificationService::current()->Notify( 277 chrome::NOTIFICATION_TOKEN_AVAILABLE, 278 content::Source<TokenService>(this), 279 content::Details<const TokenAvailableDetails>(&details)); 280 } 281 282 void TokenService::FireTokenRequestFailedNotification( 283 const std::string& service, 284 const GoogleServiceAuthError& error) { 285 286 #if defined(OS_CHROMEOS) 287 std::string metric = "TokenService.TokenRequestFailed." + service; 288 // We can't use the UMA_HISTOGRAM_ENUMERATION here since the macro creates 289 // a static histogram in the function - locking us to one metric name. Since 290 // the metric name can be "TokenService.TokenRequestFailed." + one of four 291 // different values, we need to create the histogram ourselves and add the 292 // sample. 293 base::HistogramBase* histogram = 294 base::LinearHistogram::FactoryGet( 295 metric, 296 1, 297 GoogleServiceAuthError::NUM_STATES, 298 GoogleServiceAuthError::NUM_STATES + 1, 299 base::HistogramBase::kUmaTargetedHistogramFlag); 300 histogram->Add(error.state()); 301 #endif 302 303 FOR_DIAGNOSTICS_OBSERVERS( 304 NotifyTokenReceivedFailure(service, error.ToString())); 305 306 TokenRequestFailedDetails details(service, error); 307 content::NotificationService::current()->Notify( 308 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED, 309 content::Source<TokenService>(this), 310 content::Details<const TokenRequestFailedDetails>(&details)); 311 } 312 313 void TokenService::IssueAuthTokenForTest(const std::string& service, 314 const std::string& auth_token) { 315 token_map_[service] = auth_token; 316 FireTokenAvailableNotification(service, auth_token); 317 } 318 319 void TokenService::OnIssueAuthTokenSuccess(const std::string& service, 320 const std::string& auth_token) { 321 FOR_DIAGNOSTICS_OBSERVERS( 322 NotifyTokenReceivedSuccess(service, auth_token, true)); 323 AddAuthTokenManually(service, auth_token); 324 } 325 326 void TokenService::OnIssueAuthTokenFailure(const std::string& service, 327 const GoogleServiceAuthError& error) { 328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 329 LOG(WARNING) << "Auth token issuing failed for service:" << service 330 << ", error: " << error.ToString(); 331 FOR_DIAGNOSTICS_OBSERVERS( 332 NotifyTokenReceivedFailure(service, error.ToString())); 333 FireTokenRequestFailedNotification(service, error); 334 } 335 336 void TokenService::OnClientOAuthSuccess(const ClientOAuthResult& result) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 VLOG(1) << "Got OAuth2 login token pair"; 339 SaveOAuth2Credentials(result); 340 } 341 342 void TokenService::SaveOAuth2Credentials(const ClientOAuthResult& result) { 343 token_map_[GaiaConstants::kGaiaOAuth2LoginRefreshToken] = 344 result.refresh_token; 345 SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginRefreshToken, 346 result.refresh_token); 347 // We don't save expiration information for now. 348 349 FOR_DIAGNOSTICS_OBSERVERS( 350 NotifyTokenReceivedSuccess(GaiaConstants::kGaiaOAuth2LoginRefreshToken, 351 result.refresh_token, true)); 352 353 FireTokenAvailableNotification(GaiaConstants::kGaiaOAuth2LoginRefreshToken, 354 result.refresh_token); 355 } 356 357 void TokenService::OnClientOAuthFailure( 358 const GoogleServiceAuthError& error) { 359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 360 LOG(WARNING) << "OAuth2 login token pair fetch failed: " << error.ToString(); 361 FireTokenRequestFailedNotification( 362 GaiaConstants::kGaiaOAuth2LoginRefreshToken, error); 363 } 364 365 void TokenService::OnWebDataServiceRequestDone(WebDataServiceBase::Handle h, 366 const WDTypedResult* result) { 367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 368 DCHECK(token_loading_query_); 369 token_loading_query_ = 0; 370 371 // If the fetch failed, there will be no result. In that case, we just don't 372 // load any tokens at all from the DB. 373 if (result) { 374 DCHECK(result->GetType() == TOKEN_RESULT); 375 const WDResult<std::map<std::string, std::string> > * token_result = 376 static_cast<const WDResult<std::map<std::string, std::string> > * > ( 377 result); 378 LoadTokensIntoMemory(token_result->GetValue(), &token_map_); 379 } 380 381 tokens_loaded_ = true; 382 content::NotificationService::current()->Notify( 383 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, 384 content::Source<TokenService>(this), 385 content::NotificationService::NoDetails()); 386 } 387 388 // Load tokens from the db_token map into the in memory token map. 389 void TokenService::LoadTokensIntoMemory( 390 const std::map<std::string, std::string>& db_tokens, 391 std::map<std::string, std::string>* in_memory_tokens) { 392 // Ensure that there are no active fetchers at the time we first load 393 // tokens from the DB into memory. 394 for (size_t i = 0; i < arraysize(kServices); ++i) { 395 DCHECK(NULL == fetchers_[i].get()); 396 } 397 398 for (size_t i = 0; i < arraysize(kServices); i++) { 399 LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens, kServices[i]); 400 } 401 LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens, 402 GaiaConstants::kGaiaOAuth2LoginRefreshToken); 403 404 if (credentials_.lsid.empty() && credentials_.sid.empty()) { 405 // Look for GAIA SID and LSID tokens. If we have both, and the current 406 // crendentials are empty, update the credentials. 407 std::string lsid; 408 std::string sid; 409 410 if (db_tokens.count(GaiaConstants::kGaiaLsid) > 0) 411 lsid = db_tokens.find(GaiaConstants::kGaiaLsid)->second; 412 413 if (db_tokens.count(GaiaConstants::kGaiaSid) > 0) 414 sid = db_tokens.find(GaiaConstants::kGaiaSid)->second; 415 416 if (!lsid.empty() && !sid.empty()) { 417 FOR_DIAGNOSTICS_OBSERVERS(NotifySigninValueChanged( 418 signin_internals_util::SID, sid)); 419 FOR_DIAGNOSTICS_OBSERVERS(NotifySigninValueChanged(LSID, lsid)); 420 421 credentials_ = GaiaAuthConsumer::ClientLoginResult(sid, 422 lsid, 423 std::string(), 424 std::string()); 425 } 426 } 427 } 428 429 void TokenService::LoadSingleTokenIntoMemory( 430 const std::map<std::string, std::string>& db_tokens, 431 std::map<std::string, std::string>* in_memory_tokens, 432 const std::string& service) { 433 // OnIssueAuthTokenSuccess should come from the same thread. 434 // If a token is already present in the map, it could only have 435 // come from a DB read or from IssueAuthToken. Since we should never 436 // fetch from the DB twice in a browser session, it must be from 437 // OnIssueAuthTokenSuccess, which is a live fetcher. 438 // 439 // Network fetched tokens take priority over DB tokens, so exclude tokens 440 // which have already been loaded by the fetcher. 441 if (!in_memory_tokens->count(service) && db_tokens.count(service)) { 442 std::string db_token = db_tokens.find(service)->second; 443 if (!db_token.empty()) { 444 VLOG(1) << "Loading " << service << " token from DB: " << db_token; 445 (*in_memory_tokens)[service] = db_token; 446 FireTokenAvailableNotification(service, db_token); 447 // Failures are only for network errors. 448 449 // Update the token info for about:sigin-internals, but don't update the 450 // time-stamps since we only care about the time it was downloaded. 451 FOR_DIAGNOSTICS_OBSERVERS( 452 NotifyTokenReceivedSuccess(service, db_token, false)); 453 } 454 } 455 } 456 457 void TokenService::AddSigninDiagnosticsObserver( 458 SigninDiagnosticsObserver* observer) { 459 signin_diagnostics_observers_.AddObserver(observer); 460 } 461 462 void TokenService::RemoveSigninDiagnosticsObserver( 463 SigninDiagnosticsObserver* observer) { 464 signin_diagnostics_observers_.RemoveObserver(observer); 465 } 466