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