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/extension_function_dispatcher.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service.h"
24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
25 #include "chrome/browser/signin/signin_global_error.h"
26 #include "chrome/browser/signin/signin_manager.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/common/extensions/api/identity.h"
29 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/url_constants.h"
32 #include "extensions/browser/event_router.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/common/extension.h"
35 #include "google_apis/gaia/gaia_urls.h"
38 #if defined(OS_CHROMEOS)
39 #include "chrome/browser/chromeos/login/user_manager.h"
40 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
41 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
42 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
43 #include "google_apis/gaia/gaia_constants.h"
46 namespace extensions {
48 namespace identity_constants {
49 const char kInvalidClientId[] = "Invalid OAuth2 Client ID.";
50 const char kInvalidScopes[] = "Invalid OAuth2 scopes.";
51 const char kAuthFailure[] = "OAuth2 request failed: ";
52 const char kNoGrant[] = "OAuth2 not granted or revoked.";
53 const char kUserRejected[] = "The user did not approve access.";
54 const char kUserNotSignedIn[] = "The user is not signed in.";
55 const char kInteractionRequired[] = "User interaction required.";
56 const char kInvalidRedirect[] = "Did not redirect to the right URL.";
57 const char kOffTheRecord[] = "Identity API is disabled in incognito windows.";
58 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
60 const int kCachedIssueAdviceTTLSeconds = 1;
61 } // namespace identity_constants
65 static const char kChromiumDomainRedirectUrlPattern[] =
66 "https://%s.chromiumapp.org/";
68 std::string GetPrimaryAccountId(Profile* profile) {
69 SigninManagerBase* signin_manager =
70 SigninManagerFactory::GetForProfile(profile);
71 return signin_manager->GetAuthenticatedAccountId();
76 namespace identity = api::identity;
78 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
79 : OAuth2TokenService::Consumer("extensions_identity_api"),
80 should_prompt_for_scopes_(false),
81 should_prompt_for_signin_(false) {}
83 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {}
85 bool IdentityGetAuthTokenFunction::RunImpl() {
86 if (GetProfile()->IsOffTheRecord()) {
87 error_ = identity_constants::kOffTheRecord;
91 scoped_ptr<identity::GetAuthToken::Params> params(
92 identity::GetAuthToken::Params::Create(*args_));
93 EXTENSION_FUNCTION_VALIDATE(params.get());
94 bool interactive = params->details.get() &&
95 params->details->interactive.get() &&
96 *params->details->interactive;
98 should_prompt_for_scopes_ = interactive;
99 should_prompt_for_signin_ = interactive;
101 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
103 // Check that the necessary information is present in the manifest.
104 oauth2_client_id_ = GetOAuth2ClientId();
105 if (oauth2_client_id_.empty()) {
106 error_ = identity_constants::kInvalidClientId;
110 if (oauth2_info.scopes.size() == 0) {
111 error_ = identity_constants::kInvalidScopes;
115 std::set<std::string> scopes(oauth2_info.scopes.begin(),
116 oauth2_info.scopes.end());
117 token_key_.reset(new ExtensionTokenKey(
118 GetExtension()->id(), GetPrimaryAccountId(GetProfile()), scopes));
120 // Balanced in CompleteFunctionWithResult|CompleteFunctionWithError
123 #if defined(OS_CHROMEOS)
124 policy::BrowserPolicyConnectorChromeOS* connector =
125 g_browser_process->platform_part()->browser_policy_connector_chromeos();
126 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp() &&
127 connector->IsEnterpriseManaged()) {
128 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
133 if (!HasLoginToken()) {
134 if (!should_prompt_for_signin_) {
135 error_ = identity_constants::kUserNotSignedIn;
139 // Display a login prompt.
142 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
148 void IdentityGetAuthTokenFunction::CompleteFunctionWithResult(
149 const std::string& access_token) {
150 SetResult(new base::StringValue(access_token));
152 Release(); // Balanced in RunImpl.
155 void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
156 const std::string& error) {
159 Release(); // Balanced in RunImpl.
162 void IdentityGetAuthTokenFunction::StartSigninFlow() {
163 // All cached tokens are invalid because the user is not signed in.
164 IdentityAPI* id_api =
165 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
167 id_api->EraseAllCachedTokens();
168 // Display a login prompt. If the subsequent mint fails, don't display the
169 // login prompt again.
170 should_prompt_for_signin_ = false;
174 void IdentityGetAuthTokenFunction::StartMintTokenFlow(
175 IdentityMintRequestQueue::MintType type) {
176 mint_token_flow_type_ = type;
178 // Flows are serialized to prevent excessive traffic to GAIA, and
179 // to consolidate UI pop-ups.
180 IdentityAPI* id_api =
181 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
184 if (!should_prompt_for_scopes_) {
185 // Caller requested no interaction.
187 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) {
188 // GAIA told us to do a consent UI.
189 CompleteFunctionWithError(identity_constants::kNoGrant);
192 if (!id_api->mint_queue()->empty(
193 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) {
194 // Another call is going through a consent UI.
195 CompleteFunctionWithError(identity_constants::kNoGrant);
199 id_api->mint_queue()->RequestStart(type, *token_key_, this);
202 void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
203 IdentityMintRequestQueue::MintType type = mint_token_flow_type_;
205 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
206 std::set<std::string> scopes(oauth2_info.scopes.begin(),
207 oauth2_info.scopes.end());
209 extensions::IdentityAPI::GetFactoryInstance()
210 ->GetForProfile(GetProfile())
212 ->RequestComplete(type, *token_key_, this);
215 void IdentityGetAuthTokenFunction::StartMintToken(
216 IdentityMintRequestQueue::MintType type) {
217 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
218 IdentityAPI* id_api =
219 IdentityAPI::GetFactoryInstance()->GetForProfile(GetProfile());
220 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_);
221 IdentityTokenCacheValue::CacheValueStatus cache_status =
222 cache_entry.status();
224 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
225 switch (cache_status) {
226 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
227 #if defined(OS_CHROMEOS)
228 // Always force minting token for ChromeOS kiosk app.
229 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) {
230 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
231 policy::BrowserPolicyConnectorChromeOS* connector =
232 g_browser_process->platform_part()
233 ->browser_policy_connector_chromeos();
234 if (connector->IsEnterpriseManaged()) {
235 StartDeviceLoginAccessTokenRequest();
237 StartLoginAccessTokenRequest();
243 if (oauth2_info.auto_approve)
244 // oauth2_info.auto_approve is protected by a whitelist in
245 // _manifest_features.json hence only selected extensions take
246 // advantage of forcefully minting the token.
247 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
249 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE;
250 StartLoginAccessTokenRequest();
253 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
254 CompleteMintTokenFlow();
255 CompleteFunctionWithResult(cache_entry.token());
258 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
259 CompleteMintTokenFlow();
260 should_prompt_for_signin_ = false;
261 issue_advice_ = cache_entry.issue_advice();
262 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
266 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
268 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) {
269 CompleteMintTokenFlow();
270 CompleteFunctionWithResult(cache_entry.token());
272 ShowOAuthApprovalDialog(issue_advice_);
277 void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
278 const std::string& access_token, int time_to_live) {
279 IdentityTokenCacheValue token(access_token,
280 base::TimeDelta::FromSeconds(time_to_live));
281 IdentityAPI::GetFactoryInstance()
282 ->GetForProfile(GetProfile())
283 ->SetCachedToken(*token_key_, token);
285 CompleteMintTokenFlow();
286 CompleteFunctionWithResult(access_token);
289 void IdentityGetAuthTokenFunction::OnMintTokenFailure(
290 const GoogleServiceAuthError& error) {
291 CompleteMintTokenFlow();
293 switch (error.state()) {
294 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
295 case GoogleServiceAuthError::SERVICE_ERROR:
296 case GoogleServiceAuthError::ACCOUNT_DELETED:
297 case GoogleServiceAuthError::ACCOUNT_DISABLED:
298 extensions::IdentityAPI::GetFactoryInstance()
299 ->GetForProfile(GetProfile())
300 ->ReportAuthError(error);
301 if (should_prompt_for_signin_) {
302 // Display a login prompt and try again (once).
308 // Return error to caller.
312 CompleteFunctionWithError(
313 std::string(identity_constants::kAuthFailure) + error.ToString());
316 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
317 const IssueAdviceInfo& issue_advice) {
318 IdentityAPI::GetFactoryInstance()
319 ->GetForProfile(GetProfile())
320 ->SetCachedToken(*token_key_,
321 IdentityTokenCacheValue(issue_advice));
322 CompleteMintTokenFlow();
324 should_prompt_for_signin_ = false;
325 // Existing grant was revoked and we used NO_FORCE, so we got info back
326 // instead. Start a consent UI if we can.
327 issue_advice_ = issue_advice;
328 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
331 void IdentityGetAuthTokenFunction::SigninSuccess() {
332 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
335 void IdentityGetAuthTokenFunction::SigninFailed() {
336 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
339 void IdentityGetAuthTokenFunction::OnGaiaFlowFailure(
340 GaiaWebAuthFlow::Failure failure,
341 GoogleServiceAuthError service_error,
342 const std::string& oauth_error) {
343 CompleteMintTokenFlow();
347 case GaiaWebAuthFlow::WINDOW_CLOSED:
348 error = identity_constants::kUserRejected;
351 case GaiaWebAuthFlow::INVALID_REDIRECT:
352 error = identity_constants::kInvalidRedirect;
355 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR:
356 error = std::string(identity_constants::kAuthFailure) +
357 service_error.ToString();
360 case GaiaWebAuthFlow::OAUTH_ERROR:
361 error = MapOAuth2ErrorToDescription(oauth_error);
364 case GaiaWebAuthFlow::LOAD_FAILED:
365 error = identity_constants::kPageLoadFailure;
369 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure;
370 error = identity_constants::kInvalidRedirect;
374 CompleteFunctionWithError(error);
377 void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted(
378 const std::string& access_token,
379 const std::string& expiration) {
382 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) {
383 IdentityTokenCacheValue token_value(
384 access_token, base::TimeDelta::FromSeconds(time_to_live));
385 IdentityAPI::GetFactoryInstance()
386 ->GetForProfile(GetProfile())
387 ->SetCachedToken(*token_key_, token_value);
390 CompleteMintTokenFlow();
391 CompleteFunctionWithResult(access_token);
394 void IdentityGetAuthTokenFunction::OnGetTokenSuccess(
395 const OAuth2TokenService::Request* request,
396 const std::string& access_token,
397 const base::Time& expiration_time) {
398 login_token_request_.reset();
399 StartGaiaRequest(access_token);
402 void IdentityGetAuthTokenFunction::OnGetTokenFailure(
403 const OAuth2TokenService::Request* request,
404 const GoogleServiceAuthError& error) {
405 login_token_request_.reset();
406 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
409 #if defined(OS_CHROMEOS)
410 void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() {
411 chromeos::DeviceOAuth2TokenServiceFactory::Get(
412 base::Bind(&IdentityGetAuthTokenFunction::DidGetTokenService,
416 void IdentityGetAuthTokenFunction::DidGetTokenService(
417 chromeos::DeviceOAuth2TokenService* service) {
419 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
422 // Since robot account refresh tokens are scoped down to [any-api] only,
423 // request access token for [any-api] instead of login.
424 OAuth2TokenService::ScopeSet scopes;
425 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
426 login_token_request_ =
427 service->StartRequest(service->GetRobotAccountId(),
433 void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
434 ProfileOAuth2TokenService* service =
435 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
436 const std::string primary_account_id = GetPrimaryAccountId(GetProfile());
437 #if defined(OS_CHROMEOS)
438 if (chrome::IsRunningInForcedAppMode()) {
439 std::string app_client_id;
440 std::string app_client_secret;
441 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
442 &app_client_id, &app_client_secret)) {
443 login_token_request_ =
444 service->StartRequestForClient(primary_account_id,
447 OAuth2TokenService::ScopeSet(),
453 login_token_request_ = service->StartRequest(
454 primary_account_id, OAuth2TokenService::ScopeSet(), this);
457 void IdentityGetAuthTokenFunction::StartGaiaRequest(
458 const std::string& login_access_token) {
459 DCHECK(!login_access_token.empty());
460 mint_token_flow_.reset(CreateMintTokenFlow(login_access_token));
461 mint_token_flow_->Start();
464 void IdentityGetAuthTokenFunction::ShowLoginPopup() {
465 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile()));
466 signin_flow_->Start();
469 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog(
470 const IssueAdviceInfo& issue_advice) {
471 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
472 const std::string locale = g_browser_process->local_state()->GetString(
473 prefs::kApplicationLocale);
475 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(
476 this, GetProfile(), GetExtension()->id(), oauth2_info, locale));
477 gaia_web_auth_flow_->Start();
480 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow(
481 const std::string& login_access_token) {
482 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
484 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow(
485 GetProfile()->GetRequestContext(),
487 OAuth2MintTokenFlow::Parameters(login_access_token,
488 GetExtension()->id(),
491 gaia_mint_token_mode_));
492 return mint_token_flow;
495 bool IdentityGetAuthTokenFunction::HasLoginToken() const {
496 ProfileOAuth2TokenService* token_service =
497 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
498 return token_service->RefreshTokenIsAvailable(
499 GetPrimaryAccountId(GetProfile()));
502 std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription(
503 const std::string& error) {
504 const char kOAuth2ErrorAccessDenied[] = "access_denied";
505 const char kOAuth2ErrorInvalidScope[] = "invalid_scope";
507 if (error == kOAuth2ErrorAccessDenied)
508 return std::string(identity_constants::kUserRejected);
509 else if (error == kOAuth2ErrorInvalidScope)
510 return std::string(identity_constants::kInvalidScopes);
512 return std::string(identity_constants::kAuthFailure) + error;
515 std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const {
516 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
517 std::string client_id = oauth2_info.client_id;
519 // Component apps using auto_approve may use Chrome's client ID by
520 // omitting the field.
521 if (client_id.empty() && GetExtension()->location() == Manifest::COMPONENT &&
522 oauth2_info.auto_approve) {
523 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
528 IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() {
531 IdentityRemoveCachedAuthTokenFunction::
532 ~IdentityRemoveCachedAuthTokenFunction() {
535 bool IdentityRemoveCachedAuthTokenFunction::RunImpl() {
536 if (GetProfile()->IsOffTheRecord()) {
537 error_ = identity_constants::kOffTheRecord;
541 scoped_ptr<identity::RemoveCachedAuthToken::Params> params(
542 identity::RemoveCachedAuthToken::Params::Create(*args_));
543 EXTENSION_FUNCTION_VALIDATE(params.get());
544 IdentityAPI::GetFactoryInstance()
545 ->GetForProfile(GetProfile())
546 ->EraseCachedToken(GetExtension()->id(), params->details.token);
550 IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {}
552 IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() {
554 auth_flow_.release()->DetachDelegateAndDelete();
557 bool IdentityLaunchWebAuthFlowFunction::RunImpl() {
558 if (GetProfile()->IsOffTheRecord()) {
559 error_ = identity_constants::kOffTheRecord;
563 scoped_ptr<identity::LaunchWebAuthFlow::Params> params(
564 identity::LaunchWebAuthFlow::Params::Create(*args_));
565 EXTENSION_FUNCTION_VALIDATE(params.get());
567 GURL auth_url(params->details.url);
568 WebAuthFlow::Mode mode =
569 params->details.interactive && *params->details.interactive ?
570 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT;
572 // Set up acceptable target URLs. (Does not include chrome-extension
573 // scheme for this version of the API.)
574 InitFinalRedirectURLPrefix(GetExtension()->id());
576 AddRef(); // Balanced in OnAuthFlowSuccess/Failure.
578 auth_flow_.reset(new WebAuthFlow(this, GetProfile(), auth_url, mode));
583 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest(
584 const std::string& extension_id) {
585 InitFinalRedirectURLPrefix(extension_id);
588 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix(
589 const std::string& extension_id) {
590 if (final_url_prefix_.is_empty()) {
591 final_url_prefix_ = GURL(base::StringPrintf(
592 kChromiumDomainRedirectUrlPattern, extension_id.c_str()));
596 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure(
597 WebAuthFlow::Failure failure) {
599 case WebAuthFlow::WINDOW_CLOSED:
600 error_ = identity_constants::kUserRejected;
602 case WebAuthFlow::INTERACTION_REQUIRED:
603 error_ = identity_constants::kInteractionRequired;
605 case WebAuthFlow::LOAD_FAILED:
606 error_ = identity_constants::kPageLoadFailure;
609 NOTREACHED() << "Unexpected error from web auth flow: " << failure;
610 error_ = identity_constants::kInvalidRedirect;
614 Release(); // Balanced in RunImpl.
617 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
618 const GURL& redirect_url) {
619 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) {
620 SetResult(new base::StringValue(redirect_url.spec()));
622 Release(); // Balanced in RunImpl.
626 IdentityTokenCacheValue::IdentityTokenCacheValue()
627 : status_(CACHE_STATUS_NOTFOUND) {
630 IdentityTokenCacheValue::IdentityTokenCacheValue(
631 const IssueAdviceInfo& issue_advice) : status_(CACHE_STATUS_ADVICE),
632 issue_advice_(issue_advice) {
633 expiration_time_ = base::Time::Now() + base::TimeDelta::FromSeconds(
634 identity_constants::kCachedIssueAdviceTTLSeconds);
637 IdentityTokenCacheValue::IdentityTokenCacheValue(
638 const std::string& token, base::TimeDelta time_to_live)
639 : status_(CACHE_STATUS_TOKEN),
641 // Remove 20 minutes from the ttl so cached tokens will have some time
642 // to live any time they are returned.
643 time_to_live -= base::TimeDelta::FromMinutes(20);
645 base::TimeDelta zero_delta;
646 if (time_to_live < zero_delta)
647 time_to_live = zero_delta;
649 expiration_time_ = base::Time::Now() + time_to_live;
652 IdentityTokenCacheValue::~IdentityTokenCacheValue() {
655 IdentityTokenCacheValue::CacheValueStatus
656 IdentityTokenCacheValue::status() const {
658 return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND;
663 const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const {
664 return issue_advice_;
667 const std::string& IdentityTokenCacheValue::token() const {
671 bool IdentityTokenCacheValue::is_expired() const {
672 return status_ == CACHE_STATUS_NOTFOUND ||
673 expiration_time_ < base::Time::Now();
676 const base::Time& IdentityTokenCacheValue::expiration_time() const {
677 return expiration_time_;
680 IdentityAPI::IdentityAPI(Profile* profile)
682 account_tracker_(profile) {
683 account_tracker_.AddObserver(this);
686 IdentityAPI::~IdentityAPI() {}
688 IdentityMintRequestQueue* IdentityAPI::mint_queue() {
692 void IdentityAPI::SetCachedToken(const ExtensionTokenKey& key,
693 const IdentityTokenCacheValue& token_data) {
694 CachedTokens::iterator it = token_cache_.find(key);
695 if (it != token_cache_.end() && it->second.status() <= token_data.status())
696 token_cache_.erase(it);
698 token_cache_.insert(std::make_pair(key, token_data));
701 void IdentityAPI::EraseCachedToken(const std::string& extension_id,
702 const std::string& token) {
703 CachedTokens::iterator it;
704 for (it = token_cache_.begin(); it != token_cache_.end(); ++it) {
705 if (it->first.extension_id == extension_id &&
706 it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN &&
707 it->second.token() == token) {
708 token_cache_.erase(it);
714 void IdentityAPI::EraseAllCachedTokens() {
715 token_cache_.clear();
718 const IdentityTokenCacheValue& IdentityAPI::GetCachedToken(
719 const ExtensionTokenKey& key) {
720 return token_cache_[key];
723 const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() {
727 void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
728 account_tracker_.ReportAuthError(GetPrimaryAccountId(profile_), 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.Pointer();
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(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
764 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
767 } // namespace extensions