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.
5 #include "chrome/browser/extensions/api/identity/identity_api.h"
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/chrome_notification_types.h"
20 #include "chrome/browser/extensions/event_router.h"
21 #include "chrome/browser/extensions/extension_function_dispatcher.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/policy/browser_policy_connector.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/signin/profile_oauth2_token_service.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28 #include "chrome/browser/signin/signin_global_error.h"
29 #include "chrome/common/extensions/api/identity.h"
30 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
31 #include "chrome/common/extensions/extension.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/common/url_constants.h"
34 #include "google_apis/gaia/gaia_urls.h"
37 #if defined(OS_CHROMEOS)
38 #include "chrome/browser/chromeos/login/user_manager.h"
39 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
40 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
41 #include "google_apis/gaia/gaia_constants.h"
44 namespace extensions {
46 namespace identity_constants {
47 const char kInvalidClientId[] = "Invalid OAuth2 Client ID.";
48 const char kInvalidScopes[] = "Invalid OAuth2 scopes.";
49 const char kAuthFailure[] = "OAuth2 request failed: ";
50 const char kNoGrant[] = "OAuth2 not granted or revoked.";
51 const char kUserRejected[] = "The user did not approve access.";
52 const char kUserNotSignedIn[] = "The user is not signed in.";
53 const char kInteractionRequired[] = "User interaction required.";
54 const char kInvalidRedirect[] = "Did not redirect to the right URL.";
55 const char kOffTheRecord[] = "Identity API is disabled in incognito windows.";
56 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
58 const int kCachedIssueAdviceTTLSeconds = 1;
59 } // namespace identity_constants
63 static const char kChromiumDomainRedirectUrlPattern[] =
64 "https://%s.chromiumapp.org/";
68 namespace identity = api::identity;
70 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
71 : should_prompt_for_scopes_(false),
72 should_prompt_for_signin_(false) {}
74 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {}
76 bool IdentityGetAuthTokenFunction::RunImpl() {
77 if (GetProfile()->IsOffTheRecord()) {
78 error_ = identity_constants::kOffTheRecord;
82 scoped_ptr<identity::GetAuthToken::Params> params(
83 identity::GetAuthToken::Params::Create(*args_));
84 EXTENSION_FUNCTION_VALIDATE(params.get());
85 bool interactive = params->details.get() &&
86 params->details->interactive.get() &&
87 *params->details->interactive;
89 should_prompt_for_scopes_ = interactive;
90 should_prompt_for_signin_ = interactive;
92 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
94 // Check that the necessary information is present in the manifest.
95 oauth2_client_id_ = GetOAuth2ClientId();
96 if (oauth2_client_id_.empty()) {
97 error_ = identity_constants::kInvalidClientId;
101 if (oauth2_info.scopes.size() == 0) {
102 error_ = identity_constants::kInvalidScopes;
106 // Balanced in CompleteFunctionWithResult|CompleteFunctionWithError
109 #if defined(OS_CHROMEOS)
110 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp() &&
111 g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
112 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
117 if (!HasLoginToken()) {
118 if (!should_prompt_for_signin_) {
119 error_ = identity_constants::kUserNotSignedIn;
123 // Display a login prompt.
126 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
132 void IdentityGetAuthTokenFunction::CompleteFunctionWithResult(
133 const std::string& access_token) {
134 SetResult(new base::StringValue(access_token));
136 Release(); // Balanced in RunImpl.
139 void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
140 const std::string& error) {
143 Release(); // Balanced in RunImpl.
146 void IdentityGetAuthTokenFunction::StartSigninFlow() {
147 // All cached tokens are invalid because the user is not signed in.
148 IdentityAPI* id_api =
149 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
151 id_api->EraseAllCachedTokens();
152 // Display a login prompt. If the subsequent mint fails, don't display the
153 // login prompt again.
154 should_prompt_for_signin_ = false;
158 void IdentityGetAuthTokenFunction::StartMintTokenFlow(
159 IdentityMintRequestQueue::MintType type) {
160 mint_token_flow_type_ = type;
162 // Flows are serialized to prevent excessive traffic to GAIA, and
163 // to consolidate UI pop-ups.
164 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
165 std::set<std::string> scopes(oauth2_info.scopes.begin(),
166 oauth2_info.scopes.end());
167 IdentityAPI* id_api =
168 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
171 if (!should_prompt_for_scopes_) {
172 // Caller requested no interaction.
174 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) {
175 // GAIA told us to do a consent UI.
176 CompleteFunctionWithError(identity_constants::kNoGrant);
179 if (!id_api->mint_queue()->empty(
180 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE,
181 GetExtension()->id(), scopes)) {
182 // Another call is going through a consent UI.
183 CompleteFunctionWithError(identity_constants::kNoGrant);
187 id_api->mint_queue()->RequestStart(type,
188 GetExtension()->id(),
193 void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
194 IdentityMintRequestQueue::MintType type = mint_token_flow_type_;
196 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
197 std::set<std::string> scopes(oauth2_info.scopes.begin(),
198 oauth2_info.scopes.end());
200 extensions::IdentityAPI::GetFactoryInstance()
201 ->GetForProfile(GetProfile())
203 ->RequestComplete(type, GetExtension()->id(), scopes, this);
206 void IdentityGetAuthTokenFunction::StartMintToken(
207 IdentityMintRequestQueue::MintType type) {
208 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
209 IdentityAPI* id_api =
210 IdentityAPI::GetFactoryInstance()->GetForProfile(GetProfile());
211 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(
212 GetExtension()->id(), oauth2_info.scopes);
213 IdentityTokenCacheValue::CacheValueStatus cache_status =
214 cache_entry.status();
216 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
217 switch (cache_status) {
218 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
219 #if defined(OS_CHROMEOS)
220 // Always force minting token for ChromeOS kiosk app.
221 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) {
222 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
223 if (g_browser_process->browser_policy_connector()->
224 IsEnterpriseManaged()) {
225 StartDeviceLoginAccessTokenRequest();
227 StartLoginAccessTokenRequest();
233 if (oauth2_info.auto_approve)
234 // oauth2_info.auto_approve is protected by a whitelist in
235 // _manifest_features.json hence only selected extensions take
236 // advantage of forcefully minting the token.
237 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
239 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE;
240 StartLoginAccessTokenRequest();
243 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
244 CompleteMintTokenFlow();
245 CompleteFunctionWithResult(cache_entry.token());
248 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
249 CompleteMintTokenFlow();
250 should_prompt_for_signin_ = false;
251 issue_advice_ = cache_entry.issue_advice();
252 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
256 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
258 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) {
259 CompleteMintTokenFlow();
260 CompleteFunctionWithResult(cache_entry.token());
262 ShowOAuthApprovalDialog(issue_advice_);
267 void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
268 const std::string& access_token, int time_to_live) {
269 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
270 IdentityTokenCacheValue token(access_token,
271 base::TimeDelta::FromSeconds(time_to_live));
272 IdentityAPI::GetFactoryInstance()
273 ->GetForProfile(GetProfile())
274 ->SetCachedToken(GetExtension()->id(), oauth2_info.scopes, token);
276 CompleteMintTokenFlow();
277 CompleteFunctionWithResult(access_token);
280 void IdentityGetAuthTokenFunction::OnMintTokenFailure(
281 const GoogleServiceAuthError& error) {
282 CompleteMintTokenFlow();
284 switch (error.state()) {
285 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
286 case GoogleServiceAuthError::ACCOUNT_DELETED:
287 case GoogleServiceAuthError::ACCOUNT_DISABLED:
288 extensions::IdentityAPI::GetFactoryInstance()
289 ->GetForProfile(GetProfile())
290 ->ReportAuthError(error);
291 if (should_prompt_for_signin_) {
292 // Display a login prompt and try again (once).
298 // Return error to caller.
302 CompleteFunctionWithError(
303 std::string(identity_constants::kAuthFailure) + error.ToString());
306 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
307 const IssueAdviceInfo& issue_advice) {
308 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
309 IdentityAPI::GetFactoryInstance()
310 ->GetForProfile(GetProfile())
311 ->SetCachedToken(GetExtension()->id(),
313 IdentityTokenCacheValue(issue_advice));
314 CompleteMintTokenFlow();
316 should_prompt_for_signin_ = false;
317 // Existing grant was revoked and we used NO_FORCE, so we got info back
318 // instead. Start a consent UI if we can.
319 issue_advice_ = issue_advice;
320 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
323 void IdentityGetAuthTokenFunction::SigninSuccess() {
324 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
327 void IdentityGetAuthTokenFunction::SigninFailed() {
328 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
331 void IdentityGetAuthTokenFunction::OnGaiaFlowFailure(
332 GaiaWebAuthFlow::Failure failure,
333 GoogleServiceAuthError service_error,
334 const std::string& oauth_error) {
335 CompleteMintTokenFlow();
339 case GaiaWebAuthFlow::WINDOW_CLOSED:
340 error = identity_constants::kUserRejected;
343 case GaiaWebAuthFlow::INVALID_REDIRECT:
344 error = identity_constants::kInvalidRedirect;
347 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR:
348 error = std::string(identity_constants::kAuthFailure) +
349 service_error.ToString();
352 case GaiaWebAuthFlow::OAUTH_ERROR:
353 error = MapOAuth2ErrorToDescription(oauth_error);
356 case GaiaWebAuthFlow::LOAD_FAILED:
357 error = identity_constants::kPageLoadFailure;
361 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure;
362 error = identity_constants::kInvalidRedirect;
366 CompleteFunctionWithError(error);
369 void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted(
370 const std::string& access_token,
371 const std::string& expiration) {
374 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) {
375 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
376 IdentityTokenCacheValue token_value(
377 access_token, base::TimeDelta::FromSeconds(time_to_live));
378 IdentityAPI::GetFactoryInstance()
379 ->GetForProfile(GetProfile())
380 ->SetCachedToken(GetExtension()->id(), oauth2_info.scopes, token_value);
383 CompleteMintTokenFlow();
384 CompleteFunctionWithResult(access_token);
387 void IdentityGetAuthTokenFunction::OnGetTokenSuccess(
388 const OAuth2TokenService::Request* request,
389 const std::string& access_token,
390 const base::Time& expiration_time) {
391 login_token_request_.reset();
392 StartGaiaRequest(access_token);
395 void IdentityGetAuthTokenFunction::OnGetTokenFailure(
396 const OAuth2TokenService::Request* request,
397 const GoogleServiceAuthError& error) {
398 login_token_request_.reset();
399 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
402 #if defined(OS_CHROMEOS)
403 void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() {
404 chromeos::DeviceOAuth2TokenServiceFactory::Get(
405 base::Bind(&IdentityGetAuthTokenFunction::DidGetTokenService,
409 void IdentityGetAuthTokenFunction::DidGetTokenService(
410 chromeos::DeviceOAuth2TokenService* service) {
412 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
415 // Since robot account refresh tokens are scoped down to [any-api] only,
416 // request access token for [any-api] instead of login.
417 OAuth2TokenService::ScopeSet scopes;
418 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
419 login_token_request_ =
420 service->StartRequest(service->GetRobotAccountId(),
426 void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
427 ProfileOAuth2TokenService* service =
428 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
429 #if defined(OS_CHROMEOS)
430 if (chrome::IsRunningInForcedAppMode()) {
431 std::string app_client_id;
432 std::string app_client_secret;
433 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
434 &app_client_id, &app_client_secret)) {
435 login_token_request_ =
436 service->StartRequestForClient(service->GetPrimaryAccountId(),
439 OAuth2TokenService::ScopeSet(),
445 login_token_request_ = service->StartRequest(
446 service->GetPrimaryAccountId(), OAuth2TokenService::ScopeSet(), this);
449 void IdentityGetAuthTokenFunction::StartGaiaRequest(
450 const std::string& login_access_token) {
451 DCHECK(!login_access_token.empty());
452 mint_token_flow_.reset(CreateMintTokenFlow(login_access_token));
453 mint_token_flow_->Start();
456 void IdentityGetAuthTokenFunction::ShowLoginPopup() {
457 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile()));
458 signin_flow_->Start();
461 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog(
462 const IssueAdviceInfo& issue_advice) {
463 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
464 const std::string locale = g_browser_process->local_state()->GetString(
465 prefs::kApplicationLocale);
467 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(
468 this, GetProfile(), GetExtension()->id(), oauth2_info, locale));
469 gaia_web_auth_flow_->Start();
472 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow(
473 const std::string& login_access_token) {
474 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
476 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow(
477 GetProfile()->GetRequestContext(),
479 OAuth2MintTokenFlow::Parameters(login_access_token,
480 GetExtension()->id(),
483 gaia_mint_token_mode_));
484 return mint_token_flow;
487 bool IdentityGetAuthTokenFunction::HasLoginToken() const {
488 ProfileOAuth2TokenService* token_service =
489 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
490 return token_service->RefreshTokenIsAvailable(
491 token_service->GetPrimaryAccountId());
494 std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription(
495 const std::string& error) {
496 const char kOAuth2ErrorAccessDenied[] = "access_denied";
497 const char kOAuth2ErrorInvalidScope[] = "invalid_scope";
499 if (error == kOAuth2ErrorAccessDenied)
500 return std::string(identity_constants::kUserRejected);
501 else if (error == kOAuth2ErrorInvalidScope)
502 return std::string(identity_constants::kInvalidScopes);
504 return std::string(identity_constants::kAuthFailure) + error;
507 std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const {
508 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
509 std::string client_id = oauth2_info.client_id;
511 // Component apps using auto_approve may use Chrome's client ID by
512 // omitting the field.
513 if (client_id.empty() && GetExtension()->location() == Manifest::COMPONENT &&
514 oauth2_info.auto_approve) {
515 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
520 IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() {
523 IdentityRemoveCachedAuthTokenFunction::
524 ~IdentityRemoveCachedAuthTokenFunction() {
527 bool IdentityRemoveCachedAuthTokenFunction::RunImpl() {
528 if (GetProfile()->IsOffTheRecord()) {
529 error_ = identity_constants::kOffTheRecord;
533 scoped_ptr<identity::RemoveCachedAuthToken::Params> params(
534 identity::RemoveCachedAuthToken::Params::Create(*args_));
535 EXTENSION_FUNCTION_VALIDATE(params.get());
536 IdentityAPI::GetFactoryInstance()
537 ->GetForProfile(GetProfile())
538 ->EraseCachedToken(GetExtension()->id(), params->details.token);
542 IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {}
544 IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() {
546 auth_flow_.release()->DetachDelegateAndDelete();
549 bool IdentityLaunchWebAuthFlowFunction::RunImpl() {
550 if (GetProfile()->IsOffTheRecord()) {
551 error_ = identity_constants::kOffTheRecord;
555 scoped_ptr<identity::LaunchWebAuthFlow::Params> params(
556 identity::LaunchWebAuthFlow::Params::Create(*args_));
557 EXTENSION_FUNCTION_VALIDATE(params.get());
559 GURL auth_url(params->details.url);
560 WebAuthFlow::Mode mode =
561 params->details.interactive && *params->details.interactive ?
562 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT;
564 // Set up acceptable target URLs. (Does not include chrome-extension
565 // scheme for this version of the API.)
566 InitFinalRedirectURLPrefix(GetExtension()->id());
568 AddRef(); // Balanced in OnAuthFlowSuccess/Failure.
570 auth_flow_.reset(new WebAuthFlow(this, GetProfile(), auth_url, mode));
575 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest(
576 const std::string& extension_id) {
577 InitFinalRedirectURLPrefix(extension_id);
580 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix(
581 const std::string& extension_id) {
582 if (final_url_prefix_.is_empty()) {
583 final_url_prefix_ = GURL(base::StringPrintf(
584 kChromiumDomainRedirectUrlPattern, extension_id.c_str()));
588 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure(
589 WebAuthFlow::Failure failure) {
591 case WebAuthFlow::WINDOW_CLOSED:
592 error_ = identity_constants::kUserRejected;
594 case WebAuthFlow::INTERACTION_REQUIRED:
595 error_ = identity_constants::kInteractionRequired;
597 case WebAuthFlow::LOAD_FAILED:
598 error_ = identity_constants::kPageLoadFailure;
601 NOTREACHED() << "Unexpected error from web auth flow: " << failure;
602 error_ = identity_constants::kInvalidRedirect;
606 Release(); // Balanced in RunImpl.
609 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
610 const GURL& redirect_url) {
611 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) {
612 SetResult(new base::StringValue(redirect_url.spec()));
614 Release(); // Balanced in RunImpl.
618 IdentityTokenCacheValue::IdentityTokenCacheValue()
619 : status_(CACHE_STATUS_NOTFOUND) {
622 IdentityTokenCacheValue::IdentityTokenCacheValue(
623 const IssueAdviceInfo& issue_advice) : status_(CACHE_STATUS_ADVICE),
624 issue_advice_(issue_advice) {
625 expiration_time_ = base::Time::Now() + base::TimeDelta::FromSeconds(
626 identity_constants::kCachedIssueAdviceTTLSeconds);
629 IdentityTokenCacheValue::IdentityTokenCacheValue(
630 const std::string& token, base::TimeDelta time_to_live)
631 : status_(CACHE_STATUS_TOKEN),
633 // Remove 20 minutes from the ttl so cached tokens will have some time
634 // to live any time they are returned.
635 time_to_live -= base::TimeDelta::FromMinutes(20);
637 base::TimeDelta zero_delta;
638 if (time_to_live < zero_delta)
639 time_to_live = zero_delta;
641 expiration_time_ = base::Time::Now() + time_to_live;
644 IdentityTokenCacheValue::~IdentityTokenCacheValue() {
647 IdentityTokenCacheValue::CacheValueStatus
648 IdentityTokenCacheValue::status() const {
650 return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND;
655 const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const {
656 return issue_advice_;
659 const std::string& IdentityTokenCacheValue::token() const {
663 bool IdentityTokenCacheValue::is_expired() const {
664 return status_ == CACHE_STATUS_NOTFOUND ||
665 expiration_time_ < base::Time::Now();
668 const base::Time& IdentityTokenCacheValue::expiration_time() const {
669 return expiration_time_;
672 IdentityAPI::IdentityAPI(Profile* profile)
674 account_tracker_(profile) {
675 account_tracker_.AddObserver(this);
678 IdentityAPI::~IdentityAPI() {}
680 IdentityMintRequestQueue* IdentityAPI::mint_queue() {
684 void IdentityAPI::SetCachedToken(const std::string& extension_id,
685 const std::vector<std::string> scopes,
686 const IdentityTokenCacheValue& token_data) {
687 std::set<std::string> scopeset(scopes.begin(), scopes.end());
688 TokenCacheKey key(extension_id, scopeset);
690 CachedTokens::iterator it = token_cache_.find(key);
691 if (it != token_cache_.end() && it->second.status() <= token_data.status())
692 token_cache_.erase(it);
694 token_cache_.insert(std::make_pair(key, token_data));
697 void IdentityAPI::EraseCachedToken(const std::string& extension_id,
698 const std::string& token) {
699 CachedTokens::iterator it;
700 for (it = token_cache_.begin(); it != token_cache_.end(); ++it) {
701 if (it->first.extension_id == extension_id &&
702 it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN &&
703 it->second.token() == token) {
704 token_cache_.erase(it);
710 void IdentityAPI::EraseAllCachedTokens() {
711 token_cache_.clear();
714 const IdentityTokenCacheValue& IdentityAPI::GetCachedToken(
715 const std::string& extension_id, const std::vector<std::string> scopes) {
716 std::set<std::string> scopeset(scopes.begin(), scopes.end());
717 TokenCacheKey key(extension_id, scopeset);
718 return token_cache_[key];
721 const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() {
725 void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
726 ProfileOAuth2TokenService* token_service =
727 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
728 account_tracker_.ReportAuthError(token_service->GetPrimaryAccountId(), error);
731 void IdentityAPI::Shutdown() {
732 account_tracker_.RemoveObserver(this);
733 account_tracker_.Shutdown();
736 static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> >
737 g_factory = LAZY_INSTANCE_INITIALIZER;
740 ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() {
741 return &g_factory.Get();
744 void IdentityAPI::OnAccountAdded(const AccountIds& ids) {}
746 void IdentityAPI::OnAccountRemoved(const AccountIds& ids) {}
748 void IdentityAPI::OnAccountSignInChanged(const AccountIds& ids,
750 api::identity::AccountInfo account_info;
751 account_info.id = ids.gaia;
753 scoped_ptr<base::ListValue> args =
754 api::identity::OnSignInChanged::Create(account_info, is_signed_in);
755 scoped_ptr<Event> event(new Event(
756 api::identity::OnSignInChanged::kEventName, args.Pass(), profile_));
758 ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(event.Pass());
762 void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() {
763 DependsOn(ExtensionSystemFactory::GetInstance());
764 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
767 IdentityAPI::TokenCacheKey::TokenCacheKey(const std::string& extension_id,
768 const std::set<std::string> scopes)
769 : extension_id(extension_id),
773 IdentityAPI::TokenCacheKey::~TokenCacheKey() {
776 bool IdentityAPI::TokenCacheKey::operator<(
777 const IdentityAPI::TokenCacheKey& rhs) const {
778 if (extension_id < rhs.extension_id)
780 else if (rhs.extension_id < extension_id)
783 return scopes < rhs.scopes;
786 } // namespace extensions