identity_api.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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/extensions/api/identity/identity_api.h" 6 7#include <set> 8#include <string> 9#include <utility> 10#include <vector> 11 12#include "base/lazy_instance.h" 13#include "base/prefs/pref_service.h" 14#include "base/stringprintf.h" 15#include "base/strings/string_number_conversions.h" 16#include "base/values.h" 17#include "chrome/browser/app_mode/app_mode_utils.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/extensions/extension_function_dispatcher.h" 20#include "chrome/browser/extensions/extension_service.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/signin/signin_manager.h" 23#include "chrome/browser/signin/signin_manager_factory.h" 24#include "chrome/browser/signin/token_service.h" 25#include "chrome/browser/signin/token_service_factory.h" 26#include "chrome/common/extensions/api/identity.h" 27#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 28#include "chrome/common/extensions/extension.h" 29#include "chrome/common/extensions/extension_manifest_constants.h" 30#include "chrome/common/pref_names.h" 31#include "chrome/common/url_constants.h" 32#include "google_apis/gaia/gaia_constants.h" 33#include "googleurl/src/gurl.h" 34 35#if defined(OS_CHROMEOS) 36#include "chrome/browser/chromeos/login/user_manager.h" 37#endif 38 39namespace extensions { 40 41namespace identity_constants { 42const char kInvalidClientId[] = "Invalid OAuth2 Client ID."; 43const char kInvalidScopes[] = "Invalid OAuth2 scopes."; 44const char kAuthFailure[] = "OAuth2 request failed: "; 45const char kNoGrant[] = "OAuth2 not granted or revoked."; 46const char kUserRejected[] = "The user did not approve access."; 47const char kUserNotSignedIn[] = "The user is not signed in."; 48const char kInteractionRequired[] = "User interaction required."; 49const char kInvalidRedirect[] = "Did not redirect to the right URL."; 50const char kOffTheRecord[] = "Identity API is disabled in incognito windows."; 51const char kPageLoadFailure[] = "Authorization page could not be loaded."; 52 53const int kCachedIssueAdviceTTLSeconds = 1; 54} // namespace identity_constants 55 56namespace { 57 58static const char kChromiumDomainRedirectUrlPattern[] = 59 "https://%s.chromiumapp.org/"; 60 61} // namespace 62 63namespace identity = api::identity; 64 65IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() 66 : should_prompt_for_scopes_(false), 67 should_prompt_for_signin_(false) {} 68 69IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} 70 71bool IdentityGetAuthTokenFunction::RunImpl() { 72 if (profile()->IsOffTheRecord()) { 73 error_ = identity_constants::kOffTheRecord; 74 return false; 75 } 76 77 scoped_ptr<identity::GetAuthToken::Params> params( 78 identity::GetAuthToken::Params::Create(*args_)); 79 EXTENSION_FUNCTION_VALIDATE(params.get()); 80 bool interactive = params->details.get() && 81 params->details->interactive.get() && 82 *params->details->interactive; 83 84 should_prompt_for_scopes_ = interactive; 85 should_prompt_for_signin_ = interactive; 86 87 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 88 89 // Check that the necessary information is present in the manifest. 90 if (oauth2_info.client_id.empty()) { 91 error_ = identity_constants::kInvalidClientId; 92 return false; 93 } 94 95 if (oauth2_info.scopes.size() == 0) { 96 error_ = identity_constants::kInvalidScopes; 97 return false; 98 } 99 100 // Balanced in CompleteFunctionWithResult|CompleteFunctionWithError 101 AddRef(); 102 103 if (!HasLoginToken()) { 104 if (!should_prompt_for_signin_) { 105 error_ = identity_constants::kUserNotSignedIn; 106 Release(); 107 return false; 108 } 109 // Display a login prompt. 110 StartSigninFlow(); 111 } else { 112 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); 113 refresh_token_ = token_service->GetOAuth2LoginRefreshToken(); 114 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 115 } 116 117 return true; 118} 119 120void IdentityGetAuthTokenFunction::CompleteFunctionWithResult( 121 const std::string& access_token) { 122 SetResult(Value::CreateStringValue(access_token)); 123 SendResponse(true); 124 Release(); // Balanced in RunImpl. 125} 126 127void IdentityGetAuthTokenFunction::CompleteFunctionWithError( 128 const std::string& error) { 129 error_ = error; 130 SendResponse(false); 131 Release(); // Balanced in RunImpl. 132} 133 134void IdentityGetAuthTokenFunction::StartSigninFlow() { 135 // All cached tokens are invalid because the user is not signed in. 136 IdentityAPI* id_api = 137 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(profile_); 138 id_api->EraseAllCachedTokens(); 139 // Display a login prompt. If the subsequent mint fails, don't display the 140 // login prompt again. 141 should_prompt_for_signin_ = false; 142 ShowLoginPopup(); 143} 144 145void IdentityGetAuthTokenFunction::StartMintTokenFlow( 146 IdentityMintRequestQueue::MintType type) { 147 mint_token_flow_type_ = type; 148 149 // Flows are serialized to prevent excessive traffic to GAIA, and 150 // to consolidate UI pop-ups. 151 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 152 std::set<std::string> scopes(oauth2_info.scopes.begin(), 153 oauth2_info.scopes.end()); 154 IdentityAPI* id_api = 155 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(profile_); 156 157 if (!should_prompt_for_scopes_) { 158 // Caller requested no interaction. 159 160 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) { 161 // GAIA told us to do a consent UI. 162 CompleteFunctionWithError(identity_constants::kNoGrant); 163 return; 164 } 165 if (!id_api->mint_queue()->empty( 166 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, 167 GetExtension()->id(), scopes)) { 168 // Another call is going through a consent UI. 169 CompleteFunctionWithError(identity_constants::kNoGrant); 170 return; 171 } 172 } 173 id_api->mint_queue()->RequestStart(type, 174 GetExtension()->id(), 175 scopes, 176 this); 177} 178 179void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() { 180 IdentityMintRequestQueue::MintType type = mint_token_flow_type_; 181 182 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 183 std::set<std::string> scopes(oauth2_info.scopes.begin(), 184 oauth2_info.scopes.end()); 185 186 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 187 profile_)->mint_queue()->RequestComplete(type, 188 GetExtension()->id(), 189 scopes, 190 this); 191} 192 193void IdentityGetAuthTokenFunction::StartMintToken( 194 IdentityMintRequestQueue::MintType type) { 195 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 196 IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->GetForProfile( 197 profile()); 198 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken( 199 GetExtension()->id(), oauth2_info.scopes); 200 IdentityTokenCacheValue::CacheValueStatus cache_status = 201 cache_entry.status(); 202 203 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) { 204 switch (cache_status) { 205 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND: 206#if defined(OS_CHROMEOS) 207 // Always force minting token for ChromeOS kiosk app. 208 if (chrome::IsRunningInForcedAppMode()) { 209 StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE); 210 return; 211 } 212#endif 213 StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE); 214 break; 215 216 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN: 217 CompleteMintTokenFlow(); 218 CompleteFunctionWithResult(cache_entry.token()); 219 break; 220 221 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE: 222 CompleteMintTokenFlow(); 223 should_prompt_for_signin_ = false; 224 issue_advice_ = cache_entry.issue_advice(); 225 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 226 break; 227 } 228 } else { 229 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 230 231 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) { 232 CompleteMintTokenFlow(); 233 CompleteFunctionWithResult(cache_entry.token()); 234 } else { 235 ShowOAuthApprovalDialog(issue_advice_); 236 } 237 } 238} 239 240void IdentityGetAuthTokenFunction::OnMintTokenSuccess( 241 const std::string& access_token, int time_to_live) { 242 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 243 IdentityTokenCacheValue token(access_token, 244 base::TimeDelta::FromSeconds(time_to_live)); 245 IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->SetCachedToken( 246 GetExtension()->id(), oauth2_info.scopes, token); 247 248 CompleteMintTokenFlow(); 249 CompleteFunctionWithResult(access_token); 250} 251 252void IdentityGetAuthTokenFunction::OnMintTokenFailure( 253 const GoogleServiceAuthError& error) { 254 CompleteMintTokenFlow(); 255 256 switch (error.state()) { 257 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 258 case GoogleServiceAuthError::ACCOUNT_DELETED: 259 case GoogleServiceAuthError::ACCOUNT_DISABLED: 260 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 261 profile())->ReportAuthError(error); 262 if (should_prompt_for_signin_) { 263 // Display a login prompt and try again (once). 264 StartSigninFlow(); 265 return; 266 } 267 break; 268 default: 269 // Return error to caller. 270 break; 271 } 272 273 CompleteFunctionWithError( 274 std::string(identity_constants::kAuthFailure) + error.ToString()); 275} 276 277void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( 278 const IssueAdviceInfo& issue_advice) { 279 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 280 IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->SetCachedToken( 281 GetExtension()->id(), oauth2_info.scopes, 282 IdentityTokenCacheValue(issue_advice)); 283 CompleteMintTokenFlow(); 284 285 should_prompt_for_signin_ = false; 286 // Existing grant was revoked and we used NO_FORCE, so we got info back 287 // instead. Start a consent UI if we can. 288 issue_advice_ = issue_advice; 289 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 290} 291 292void IdentityGetAuthTokenFunction::SigninSuccess(const std::string& token) { 293 refresh_token_ = token; 294 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 295} 296 297void IdentityGetAuthTokenFunction::SigninFailed() { 298 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); 299} 300 301void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( 302 GaiaWebAuthFlow::Failure failure, 303 GoogleServiceAuthError service_error, 304 const std::string& oauth_error) { 305 CompleteMintTokenFlow(); 306 std::string error; 307 308 switch (failure) { 309 case GaiaWebAuthFlow::WINDOW_CLOSED: 310 error = identity_constants::kUserRejected; 311 break; 312 313 case GaiaWebAuthFlow::INVALID_REDIRECT: 314 error = identity_constants::kInvalidRedirect; 315 break; 316 317 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR: 318 error = std::string(identity_constants::kAuthFailure) + 319 service_error.ToString(); 320 break; 321 322 case GaiaWebAuthFlow::OAUTH_ERROR: 323 error = MapOAuth2ErrorToDescription(oauth_error); 324 break; 325 326 // TODO(courage): load failure tests 327 328 case GaiaWebAuthFlow::LOAD_FAILED: 329 error = identity_constants::kPageLoadFailure; 330 break; 331 332 default: 333 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; 334 error = identity_constants::kInvalidRedirect; 335 break; 336 } 337 338 CompleteFunctionWithError(error); 339} 340 341void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted( 342 const std::string& access_token, 343 const std::string& expiration) { 344 345 int time_to_live; 346 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) { 347 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 348 IdentityTokenCacheValue token_value( 349 access_token, base::TimeDelta::FromSeconds(time_to_live)); 350 IdentityAPI::GetFactoryInstance()->GetForProfile(profile()) 351 ->SetCachedToken(GetExtension()->id(), oauth2_info.scopes, token_value); 352 } 353 354 CompleteMintTokenFlow(); 355 CompleteFunctionWithResult(access_token); 356} 357 358void IdentityGetAuthTokenFunction::StartGaiaRequest( 359 OAuth2MintTokenFlow::Mode mode) { 360 mint_token_flow_.reset(CreateMintTokenFlow(mode)); 361 mint_token_flow_->Start(); 362} 363 364void IdentityGetAuthTokenFunction::ShowLoginPopup() { 365 signin_flow_.reset(new IdentitySigninFlow(this, profile())); 366 signin_flow_->Start(); 367} 368 369void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( 370 const IssueAdviceInfo& issue_advice) { 371 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 372 const std::string locale = g_browser_process->local_state()->GetString( 373 prefs::kApplicationLocale); 374 375 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow( 376 this, profile(), GetExtension()->id(), oauth2_info, locale)); 377 gaia_web_auth_flow_->Start(); 378} 379 380OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( 381 OAuth2MintTokenFlow::Mode mode) { 382 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 383 OAuth2MintTokenFlow* mint_token_flow = 384 new OAuth2MintTokenFlow( 385 profile()->GetRequestContext(), 386 this, 387 OAuth2MintTokenFlow::Parameters( 388 refresh_token_, 389 GetExtension()->id(), 390 oauth2_info.client_id, 391 oauth2_info.scopes, 392 mode)); 393#if defined(OS_CHROMEOS) 394 if (chrome::IsRunningInForcedAppMode()) { 395 std::string chrome_client_id; 396 std::string chrome_client_secret; 397 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo( 398 &chrome_client_id, &chrome_client_secret)) { 399 mint_token_flow->SetChromeOAuthClientInfo(chrome_client_id, 400 chrome_client_secret); 401 } 402 } 403#endif 404 return mint_token_flow; 405} 406 407bool IdentityGetAuthTokenFunction::HasLoginToken() const { 408 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); 409 return token_service->HasOAuthLoginToken(); 410} 411 412std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription( 413 const std::string& error) { 414 const char kOAuth2ErrorAccessDenied[] = "access_denied"; 415 const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; 416 417 if (error == kOAuth2ErrorAccessDenied) 418 return std::string(identity_constants::kUserRejected); 419 else if (error == kOAuth2ErrorInvalidScope) 420 return std::string(identity_constants::kInvalidScopes); 421 else 422 return std::string(identity_constants::kAuthFailure) + error; 423} 424 425IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() { 426} 427 428IdentityRemoveCachedAuthTokenFunction:: 429 ~IdentityRemoveCachedAuthTokenFunction() { 430} 431 432bool IdentityRemoveCachedAuthTokenFunction::RunImpl() { 433 if (profile()->IsOffTheRecord()) { 434 error_ = identity_constants::kOffTheRecord; 435 return false; 436 } 437 438 scoped_ptr<identity::RemoveCachedAuthToken::Params> params( 439 identity::RemoveCachedAuthToken::Params::Create(*args_)); 440 EXTENSION_FUNCTION_VALIDATE(params.get()); 441 IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->EraseCachedToken( 442 GetExtension()->id(), params->details.token); 443 return true; 444} 445 446IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {} 447 448IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() { 449 if (auth_flow_) 450 auth_flow_.release()->DetachDelegateAndDelete(); 451} 452 453bool IdentityLaunchWebAuthFlowFunction::RunImpl() { 454 if (profile()->IsOffTheRecord()) { 455 error_ = identity_constants::kOffTheRecord; 456 return false; 457 } 458 459 scoped_ptr<identity::LaunchWebAuthFlow::Params> params( 460 identity::LaunchWebAuthFlow::Params::Create(*args_)); 461 EXTENSION_FUNCTION_VALIDATE(params.get()); 462 463 GURL auth_url(params->details.url); 464 WebAuthFlow::Mode mode = 465 params->details.interactive && *params->details.interactive ? 466 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT; 467 468 // Set up acceptable target URLs. (Does not include chrome-extension 469 // scheme for this version of the API.) 470 InitFinalRedirectURLPrefix(GetExtension()->id()); 471 472 AddRef(); // Balanced in OnAuthFlowSuccess/Failure. 473 474 auth_flow_.reset(new WebAuthFlow(this, profile(), auth_url, mode)); 475 auth_flow_->Start(); 476 return true; 477} 478 479void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest( 480 const std::string& extension_id) { 481 InitFinalRedirectURLPrefix(extension_id); 482} 483 484void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix( 485 const std::string& extension_id) { 486 if (final_url_prefix_.is_empty()) { 487 final_url_prefix_ = GURL(base::StringPrintf( 488 kChromiumDomainRedirectUrlPattern, extension_id.c_str())); 489 } 490} 491 492void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure( 493 WebAuthFlow::Failure failure) { 494 switch (failure) { 495 case WebAuthFlow::WINDOW_CLOSED: 496 error_ = identity_constants::kUserRejected; 497 break; 498 case WebAuthFlow::INTERACTION_REQUIRED: 499 error_ = identity_constants::kInteractionRequired; 500 break; 501 case WebAuthFlow::LOAD_FAILED: 502 error_ = identity_constants::kPageLoadFailure; 503 break; 504 default: 505 NOTREACHED() << "Unexpected error from web auth flow: " << failure; 506 error_ = identity_constants::kInvalidRedirect; 507 break; 508 } 509 SendResponse(false); 510 Release(); // Balanced in RunImpl. 511} 512 513void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange( 514 const GURL& redirect_url) { 515 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) { 516 SetResult(Value::CreateStringValue(redirect_url.spec())); 517 SendResponse(true); 518 Release(); // Balanced in RunImpl. 519 } 520} 521 522IdentityTokenCacheValue::IdentityTokenCacheValue() 523 : status_(CACHE_STATUS_NOTFOUND) { 524} 525 526IdentityTokenCacheValue::IdentityTokenCacheValue( 527 const IssueAdviceInfo& issue_advice) : status_(CACHE_STATUS_ADVICE), 528 issue_advice_(issue_advice) { 529 expiration_time_ = base::Time::Now() + base::TimeDelta::FromSeconds( 530 identity_constants::kCachedIssueAdviceTTLSeconds); 531} 532 533IdentityTokenCacheValue::IdentityTokenCacheValue( 534 const std::string& token, base::TimeDelta time_to_live) 535 : status_(CACHE_STATUS_TOKEN), 536 token_(token) { 537 base::TimeDelta zero_delta; 538 if (time_to_live < zero_delta) 539 time_to_live = zero_delta; 540 541 expiration_time_ = base::Time::Now() + time_to_live; 542} 543 544IdentityTokenCacheValue::~IdentityTokenCacheValue() { 545} 546 547IdentityTokenCacheValue::CacheValueStatus 548 IdentityTokenCacheValue::status() const { 549 if (is_expired()) 550 return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND; 551 else 552 return status_; 553} 554 555const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const { 556 return issue_advice_; 557} 558 559const std::string& IdentityTokenCacheValue::token() const { 560 return token_; 561} 562 563bool IdentityTokenCacheValue::is_expired() const { 564 return status_ == CACHE_STATUS_NOTFOUND || 565 expiration_time_ < base::Time::Now(); 566} 567 568const base::Time& IdentityTokenCacheValue::expiration_time() const { 569 return expiration_time_; 570} 571 572IdentityAPI::IdentityAPI(Profile* profile) 573 : profile_(profile), 574 signin_manager_(NULL), 575 error_(GoogleServiceAuthError::NONE) { 576} 577 578IdentityAPI::~IdentityAPI() { 579} 580 581void IdentityAPI::Initialize() { 582 signin_manager_ = SigninManagerFactory::GetForProfile(profile_); 583 signin_manager_->signin_global_error()->AddProvider(this); 584 585 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 586 registrar_.Add(this, 587 chrome::NOTIFICATION_TOKEN_AVAILABLE, 588 content::Source<TokenService>(token_service)); 589} 590 591IdentityMintRequestQueue* IdentityAPI::mint_queue() { 592 return &mint_queue_; 593} 594 595void IdentityAPI::SetCachedToken(const std::string& extension_id, 596 const std::vector<std::string> scopes, 597 const IdentityTokenCacheValue& token_data) { 598 std::set<std::string> scopeset(scopes.begin(), scopes.end()); 599 TokenCacheKey key(extension_id, scopeset); 600 601 CachedTokens::iterator it = token_cache_.find(key); 602 if (it != token_cache_.end() && it->second.status() <= token_data.status()) 603 token_cache_.erase(it); 604 605 token_cache_.insert(std::make_pair(key, token_data)); 606} 607 608void IdentityAPI::EraseCachedToken(const std::string& extension_id, 609 const std::string& token) { 610 CachedTokens::iterator it; 611 for (it = token_cache_.begin(); it != token_cache_.end(); ++it) { 612 if (it->first.extension_id == extension_id && 613 it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN && 614 it->second.token() == token) { 615 token_cache_.erase(it); 616 break; 617 } 618 } 619} 620 621void IdentityAPI::EraseAllCachedTokens() { 622 token_cache_.clear(); 623} 624 625const IdentityTokenCacheValue& IdentityAPI::GetCachedToken( 626 const std::string& extension_id, const std::vector<std::string> scopes) { 627 std::set<std::string> scopeset(scopes.begin(), scopes.end()); 628 TokenCacheKey key(extension_id, scopeset); 629 return token_cache_[key]; 630} 631 632const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { 633 return token_cache_; 634} 635 636void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { 637 if (!signin_manager_) 638 Initialize(); 639 640 error_ = error; 641 signin_manager_->signin_global_error()->AuthStatusChanged(); 642} 643 644void IdentityAPI::Shutdown() { 645 if (signin_manager_) 646 signin_manager_->signin_global_error()->RemoveProvider(this); 647} 648 649static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> > 650 g_factory = LAZY_INSTANCE_INITIALIZER; 651 652// static 653ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { 654 return &g_factory.Get(); 655} 656 657GoogleServiceAuthError IdentityAPI::GetAuthStatus() const { 658 return error_; 659} 660 661void IdentityAPI::Observe(int type, 662 const content::NotificationSource& source, 663 const content::NotificationDetails& details) { 664 CHECK(type == chrome::NOTIFICATION_TOKEN_AVAILABLE); 665 TokenService::TokenAvailableDetails* token_details = 666 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); 667 if (token_details->service() == 668 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { 669 error_ = GoogleServiceAuthError::AuthErrorNone(); 670 signin_manager_->signin_global_error()->AuthStatusChanged(); 671 } 672} 673 674template <> 675void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { 676 DependsOn(ExtensionSystemFactory::GetInstance()); 677 DependsOn(TokenServiceFactory::GetInstance()); 678 DependsOn(SigninManagerFactory::GetInstance()); 679} 680 681IdentityAPI::TokenCacheKey::TokenCacheKey(const std::string& extension_id, 682 const std::set<std::string> scopes) 683 : extension_id(extension_id), 684 scopes(scopes) { 685} 686 687IdentityAPI::TokenCacheKey::~TokenCacheKey() { 688} 689 690bool IdentityAPI::TokenCacheKey::operator<( 691 const IdentityAPI::TokenCacheKey& rhs) const { 692 if (extension_id < rhs.extension_id) 693 return true; 694 else if (rhs.extension_id < extension_id) 695 return false; 696 697 return scopes < rhs.scopes; 698} 699 700} // namespace extensions 701