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_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23 #include "chrome/browser/signin/signin_global_error.h"
24 #include "chrome/browser/signin/signin_manager_factory.h"
25 #include "chrome/common/extensions/api/identity.h"
26 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/signin/core/browser/profile_oauth2_token_service.h"
30 #include "components/signin/core/browser/signin_manager.h"
31 #include "extensions/browser/event_router.h"
32 #include "extensions/browser/extension_function_dispatcher.h"
33 #include "extensions/common/extension.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/policy/browser_policy_connector_chromeos.h"
40 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
41 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
42 #include "google_apis/gaia/gaia_constants.h"
45 namespace extensions {
47 namespace identity_constants {
48 const char kInvalidClientId[] = "Invalid OAuth2 Client ID.";
49 const char kInvalidScopes[] = "Invalid OAuth2 scopes.";
50 const char kAuthFailure[] = "OAuth2 request failed: ";
51 const char kNoGrant[] = "OAuth2 not granted or revoked.";
52 const char kUserRejected[] = "The user did not approve access.";
53 const char kUserNotSignedIn[] = "The user is not signed in.";
54 const char kInteractionRequired[] = "User interaction required.";
55 const char kInvalidRedirect[] = "Did not redirect to the right URL.";
56 const char kOffTheRecord[] = "Identity API is disabled in incognito windows.";
57 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
58 const char kCanceled[] = "canceled";
60 const int kCachedIssueAdviceTTLSeconds = 1;
61 } // namespace identity_constants
65 static const char kChromiumDomainRedirectUrlPattern[] =
66 "https://%s.chromiumapp.org/";
68 std::string GetPrimaryAccountId(content::BrowserContext* context) {
69 SigninManagerBase* signin_manager =
70 SigninManagerFactory::GetForProfile(Profile::FromBrowserContext(context));
71 return signin_manager->GetAuthenticatedAccountId();
76 namespace identity = api::identity;
78 IdentityTokenCacheValue::IdentityTokenCacheValue()
79 : status_(CACHE_STATUS_NOTFOUND) {}
81 IdentityTokenCacheValue::IdentityTokenCacheValue(
82 const IssueAdviceInfo& issue_advice)
83 : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) {
85 base::Time::Now() + base::TimeDelta::FromSeconds(
86 identity_constants::kCachedIssueAdviceTTLSeconds);
89 IdentityTokenCacheValue::IdentityTokenCacheValue(const std::string& token,
90 base::TimeDelta time_to_live)
91 : status_(CACHE_STATUS_TOKEN), token_(token) {
92 // Remove 20 minutes from the ttl so cached tokens will have some time
93 // to live any time they are returned.
94 time_to_live -= base::TimeDelta::FromMinutes(20);
96 base::TimeDelta zero_delta;
97 if (time_to_live < zero_delta)
98 time_to_live = zero_delta;
100 expiration_time_ = base::Time::Now() + time_to_live;
103 IdentityTokenCacheValue::~IdentityTokenCacheValue() {}
105 IdentityTokenCacheValue::CacheValueStatus IdentityTokenCacheValue::status()
108 return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND;
113 const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const {
114 return issue_advice_;
117 const std::string& IdentityTokenCacheValue::token() const { return token_; }
119 bool IdentityTokenCacheValue::is_expired() const {
120 return status_ == CACHE_STATUS_NOTFOUND ||
121 expiration_time_ < base::Time::Now();
124 const base::Time& IdentityTokenCacheValue::expiration_time() const {
125 return expiration_time_;
128 IdentityAPI::IdentityAPI(content::BrowserContext* context)
129 : browser_context_(context),
130 account_tracker_(Profile::FromBrowserContext(context)) {
131 account_tracker_.AddObserver(this);
134 IdentityAPI::~IdentityAPI() {}
136 IdentityMintRequestQueue* IdentityAPI::mint_queue() { return &mint_queue_; }
138 void IdentityAPI::SetCachedToken(const ExtensionTokenKey& key,
139 const IdentityTokenCacheValue& token_data) {
140 CachedTokens::iterator it = token_cache_.find(key);
141 if (it != token_cache_.end() && it->second.status() <= token_data.status())
142 token_cache_.erase(it);
144 token_cache_.insert(std::make_pair(key, token_data));
147 void IdentityAPI::EraseCachedToken(const std::string& extension_id,
148 const std::string& token) {
149 CachedTokens::iterator it;
150 for (it = token_cache_.begin(); it != token_cache_.end(); ++it) {
151 if (it->first.extension_id == extension_id &&
152 it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN &&
153 it->second.token() == token) {
154 token_cache_.erase(it);
160 void IdentityAPI::EraseAllCachedTokens() { token_cache_.clear(); }
162 const IdentityTokenCacheValue& IdentityAPI::GetCachedToken(
163 const ExtensionTokenKey& key) {
164 return token_cache_[key];
167 const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() {
171 void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
172 account_tracker_.ReportAuthError(GetPrimaryAccountId(browser_context_),
176 GoogleServiceAuthError IdentityAPI::GetAuthStatusForTest() const {
177 return account_tracker_.GetAuthStatus();
180 void IdentityAPI::Shutdown() {
181 FOR_EACH_OBSERVER(ShutdownObserver, shutdown_observer_list_, OnShutdown());
182 account_tracker_.RemoveObserver(this);
183 account_tracker_.Shutdown();
186 static base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI> >
187 g_factory = LAZY_INSTANCE_INITIALIZER;
190 BrowserContextKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() {
191 return g_factory.Pointer();
194 void IdentityAPI::OnAccountAdded(const AccountIds& ids) {}
196 void IdentityAPI::OnAccountRemoved(const AccountIds& ids) {}
198 void IdentityAPI::OnAccountSignInChanged(const AccountIds& ids,
200 api::identity::AccountInfo account_info;
201 account_info.id = ids.gaia;
203 scoped_ptr<base::ListValue> args =
204 api::identity::OnSignInChanged::Create(account_info, is_signed_in);
205 scoped_ptr<Event> event(new Event(api::identity::OnSignInChanged::kEventName,
209 EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass());
212 void IdentityAPI::AddShutdownObserver(ShutdownObserver* observer) {
213 shutdown_observer_list_.AddObserver(observer);
216 void IdentityAPI::RemoveShutdownObserver(ShutdownObserver* observer) {
217 shutdown_observer_list_.RemoveObserver(observer);
221 void BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() {
222 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
223 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
226 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
227 : OAuth2TokenService::Consumer("extensions_identity_api"),
228 should_prompt_for_scopes_(false),
229 should_prompt_for_signin_(false) {}
231 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {}
233 bool IdentityGetAuthTokenFunction::RunAsync() {
234 if (GetProfile()->IsOffTheRecord()) {
235 error_ = identity_constants::kOffTheRecord;
239 scoped_ptr<identity::GetAuthToken::Params> params(
240 identity::GetAuthToken::Params::Create(*args_));
241 EXTENSION_FUNCTION_VALIDATE(params.get());
242 bool interactive = params->details.get() &&
243 params->details->interactive.get() &&
244 *params->details->interactive;
246 should_prompt_for_scopes_ = interactive;
247 should_prompt_for_signin_ = interactive;
249 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
251 // Check that the necessary information is present in the manifest.
252 oauth2_client_id_ = GetOAuth2ClientId();
253 if (oauth2_client_id_.empty()) {
254 error_ = identity_constants::kInvalidClientId;
258 if (oauth2_info.scopes.size() == 0) {
259 error_ = identity_constants::kInvalidScopes;
263 std::set<std::string> scopes(oauth2_info.scopes.begin(),
264 oauth2_info.scopes.end());
265 token_key_.reset(new ExtensionTokenKey(
266 GetExtension()->id(), GetPrimaryAccountId(GetProfile()), scopes));
268 // From here on out, results must be returned asynchronously.
271 #if defined(OS_CHROMEOS)
272 policy::BrowserPolicyConnectorChromeOS* connector =
273 g_browser_process->platform_part()->browser_policy_connector_chromeos();
274 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp() &&
275 connector->IsEnterpriseManaged()) {
276 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
281 if (!HasLoginToken()) {
282 if (!should_prompt_for_signin_) {
283 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
286 // Display a login prompt.
289 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
295 void IdentityGetAuthTokenFunction::StartAsyncRun() {
296 // Balanced in CompleteAsyncRun
298 extensions::IdentityAPI::GetFactoryInstance()
300 ->AddShutdownObserver(this);
303 void IdentityGetAuthTokenFunction::CompleteAsyncRun(bool success) {
304 extensions::IdentityAPI::GetFactoryInstance()
306 ->RemoveShutdownObserver(this);
308 SendResponse(success);
309 Release(); // Balanced in StartAsyncRun
312 void IdentityGetAuthTokenFunction::CompleteFunctionWithResult(
313 const std::string& access_token) {
315 SetResult(new base::StringValue(access_token));
316 CompleteAsyncRun(true);
319 void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
320 const std::string& error) {
322 CompleteAsyncRun(false);
325 void IdentityGetAuthTokenFunction::StartSigninFlow() {
326 // All cached tokens are invalid because the user is not signed in.
327 IdentityAPI* id_api =
328 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile());
329 id_api->EraseAllCachedTokens();
330 // Display a login prompt. If the subsequent mint fails, don't display the
331 // login prompt again.
332 should_prompt_for_signin_ = false;
336 void IdentityGetAuthTokenFunction::StartMintTokenFlow(
337 IdentityMintRequestQueue::MintType type) {
338 mint_token_flow_type_ = type;
340 // Flows are serialized to prevent excessive traffic to GAIA, and
341 // to consolidate UI pop-ups.
342 IdentityAPI* id_api =
343 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile());
345 if (!should_prompt_for_scopes_) {
346 // Caller requested no interaction.
348 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) {
349 // GAIA told us to do a consent UI.
350 CompleteFunctionWithError(identity_constants::kNoGrant);
353 if (!id_api->mint_queue()->empty(
354 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) {
355 // Another call is going through a consent UI.
356 CompleteFunctionWithError(identity_constants::kNoGrant);
360 id_api->mint_queue()->RequestStart(type, *token_key_, this);
363 void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
364 IdentityMintRequestQueue::MintType type = mint_token_flow_type_;
366 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
367 std::set<std::string> scopes(oauth2_info.scopes.begin(),
368 oauth2_info.scopes.end());
370 extensions::IdentityAPI::GetFactoryInstance()
373 ->RequestComplete(type, *token_key_, this);
376 void IdentityGetAuthTokenFunction::StartMintToken(
377 IdentityMintRequestQueue::MintType type) {
378 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
379 IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile());
380 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_);
381 IdentityTokenCacheValue::CacheValueStatus cache_status =
382 cache_entry.status();
384 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
385 switch (cache_status) {
386 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
387 #if defined(OS_CHROMEOS)
388 // Always force minting token for ChromeOS kiosk app.
389 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) {
390 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
391 policy::BrowserPolicyConnectorChromeOS* connector =
392 g_browser_process->platform_part()
393 ->browser_policy_connector_chromeos();
394 if (connector->IsEnterpriseManaged()) {
395 StartDeviceLoginAccessTokenRequest();
397 StartLoginAccessTokenRequest();
403 if (oauth2_info.auto_approve)
404 // oauth2_info.auto_approve is protected by a whitelist in
405 // _manifest_features.json hence only selected extensions take
406 // advantage of forcefully minting the token.
407 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
409 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE;
410 StartLoginAccessTokenRequest();
413 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
414 CompleteMintTokenFlow();
415 CompleteFunctionWithResult(cache_entry.token());
418 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
419 CompleteMintTokenFlow();
420 should_prompt_for_signin_ = false;
421 issue_advice_ = cache_entry.issue_advice();
422 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
426 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
428 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) {
429 CompleteMintTokenFlow();
430 CompleteFunctionWithResult(cache_entry.token());
432 ShowOAuthApprovalDialog(issue_advice_);
437 void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
438 const std::string& access_token, int time_to_live) {
439 IdentityTokenCacheValue token(access_token,
440 base::TimeDelta::FromSeconds(time_to_live));
441 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
444 CompleteMintTokenFlow();
445 CompleteFunctionWithResult(access_token);
448 void IdentityGetAuthTokenFunction::OnMintTokenFailure(
449 const GoogleServiceAuthError& error) {
450 CompleteMintTokenFlow();
452 switch (error.state()) {
453 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
454 case GoogleServiceAuthError::ACCOUNT_DELETED:
455 case GoogleServiceAuthError::ACCOUNT_DISABLED:
456 extensions::IdentityAPI::GetFactoryInstance()
458 ->ReportAuthError(error);
459 if (should_prompt_for_signin_) {
460 // Display a login prompt and try again (once).
466 // Return error to caller.
470 CompleteFunctionWithError(
471 std::string(identity_constants::kAuthFailure) + error.ToString());
474 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
475 const IssueAdviceInfo& issue_advice) {
476 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
477 *token_key_, IdentityTokenCacheValue(issue_advice));
478 CompleteMintTokenFlow();
480 should_prompt_for_signin_ = false;
481 // Existing grant was revoked and we used NO_FORCE, so we got info back
482 // instead. Start a consent UI if we can.
483 issue_advice_ = issue_advice;
484 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
487 void IdentityGetAuthTokenFunction::SigninSuccess() {
488 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
491 void IdentityGetAuthTokenFunction::SigninFailed() {
492 CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
495 void IdentityGetAuthTokenFunction::OnGaiaFlowFailure(
496 GaiaWebAuthFlow::Failure failure,
497 GoogleServiceAuthError service_error,
498 const std::string& oauth_error) {
499 CompleteMintTokenFlow();
503 case GaiaWebAuthFlow::WINDOW_CLOSED:
504 error = identity_constants::kUserRejected;
507 case GaiaWebAuthFlow::INVALID_REDIRECT:
508 error = identity_constants::kInvalidRedirect;
511 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR:
512 error = std::string(identity_constants::kAuthFailure) +
513 service_error.ToString();
516 case GaiaWebAuthFlow::OAUTH_ERROR:
517 error = MapOAuth2ErrorToDescription(oauth_error);
520 case GaiaWebAuthFlow::LOAD_FAILED:
521 error = identity_constants::kPageLoadFailure;
525 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure;
526 error = identity_constants::kInvalidRedirect;
530 CompleteFunctionWithError(error);
533 void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted(
534 const std::string& access_token,
535 const std::string& expiration) {
538 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) {
539 IdentityTokenCacheValue token_value(
540 access_token, base::TimeDelta::FromSeconds(time_to_live));
541 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
542 *token_key_, token_value);
545 CompleteMintTokenFlow();
546 CompleteFunctionWithResult(access_token);
549 void IdentityGetAuthTokenFunction::OnGetTokenSuccess(
550 const OAuth2TokenService::Request* request,
551 const std::string& access_token,
552 const base::Time& expiration_time) {
553 login_token_request_.reset();
554 StartGaiaRequest(access_token);
557 void IdentityGetAuthTokenFunction::OnGetTokenFailure(
558 const OAuth2TokenService::Request* request,
559 const GoogleServiceAuthError& error) {
560 login_token_request_.reset();
561 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
564 void IdentityGetAuthTokenFunction::OnShutdown() {
565 gaia_web_auth_flow_.reset();
566 signin_flow_.reset();
567 login_token_request_.reset();
568 extensions::IdentityAPI::GetFactoryInstance()
571 ->RequestCancel(*token_key_, this);
572 CompleteFunctionWithError(identity_constants::kCanceled);
575 #if defined(OS_CHROMEOS)
576 void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() {
577 chromeos::DeviceOAuth2TokenService* service =
578 chromeos::DeviceOAuth2TokenServiceFactory::Get();
579 // Since robot account refresh tokens are scoped down to [any-api] only,
580 // request access token for [any-api] instead of login.
581 OAuth2TokenService::ScopeSet scopes;
582 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
583 login_token_request_ =
584 service->StartRequest(service->GetRobotAccountId(),
590 void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
591 ProfileOAuth2TokenService* service =
592 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
593 const std::string primary_account_id = GetPrimaryAccountId(GetProfile());
594 #if defined(OS_CHROMEOS)
595 if (chrome::IsRunningInForcedAppMode()) {
596 std::string app_client_id;
597 std::string app_client_secret;
598 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
599 &app_client_id, &app_client_secret)) {
600 login_token_request_ =
601 service->StartRequestForClient(primary_account_id,
604 OAuth2TokenService::ScopeSet(),
610 login_token_request_ = service->StartRequest(
611 primary_account_id, OAuth2TokenService::ScopeSet(), this);
614 void IdentityGetAuthTokenFunction::StartGaiaRequest(
615 const std::string& login_access_token) {
616 DCHECK(!login_access_token.empty());
617 mint_token_flow_.reset(CreateMintTokenFlow(login_access_token));
618 mint_token_flow_->Start();
621 void IdentityGetAuthTokenFunction::ShowLoginPopup() {
622 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile()));
623 signin_flow_->Start();
626 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog(
627 const IssueAdviceInfo& issue_advice) {
628 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
629 const std::string locale = g_browser_process->local_state()->GetString(
630 prefs::kApplicationLocale);
632 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(
633 this, GetProfile(), GetExtension()->id(), oauth2_info, locale));
634 gaia_web_auth_flow_->Start();
637 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow(
638 const std::string& login_access_token) {
639 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
641 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow(
642 GetProfile()->GetRequestContext(),
644 OAuth2MintTokenFlow::Parameters(login_access_token,
645 GetExtension()->id(),
648 gaia_mint_token_mode_));
649 return mint_token_flow;
652 bool IdentityGetAuthTokenFunction::HasLoginToken() const {
653 ProfileOAuth2TokenService* token_service =
654 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
655 return token_service->RefreshTokenIsAvailable(
656 GetPrimaryAccountId(GetProfile()));
659 std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription(
660 const std::string& error) {
661 const char kOAuth2ErrorAccessDenied[] = "access_denied";
662 const char kOAuth2ErrorInvalidScope[] = "invalid_scope";
664 if (error == kOAuth2ErrorAccessDenied)
665 return std::string(identity_constants::kUserRejected);
666 else if (error == kOAuth2ErrorInvalidScope)
667 return std::string(identity_constants::kInvalidScopes);
669 return std::string(identity_constants::kAuthFailure) + error;
672 std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const {
673 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
674 std::string client_id = oauth2_info.client_id;
676 // Component apps using auto_approve may use Chrome's client ID by
677 // omitting the field.
678 if (client_id.empty() && GetExtension()->location() == Manifest::COMPONENT &&
679 oauth2_info.auto_approve) {
680 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
685 IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() {
688 IdentityRemoveCachedAuthTokenFunction::
689 ~IdentityRemoveCachedAuthTokenFunction() {
692 bool IdentityRemoveCachedAuthTokenFunction::RunSync() {
693 if (GetProfile()->IsOffTheRecord()) {
694 error_ = identity_constants::kOffTheRecord;
698 scoped_ptr<identity::RemoveCachedAuthToken::Params> params(
699 identity::RemoveCachedAuthToken::Params::Create(*args_));
700 EXTENSION_FUNCTION_VALIDATE(params.get());
701 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->EraseCachedToken(
702 GetExtension()->id(), params->details.token);
706 IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {}
708 IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() {
710 auth_flow_.release()->DetachDelegateAndDelete();
713 bool IdentityLaunchWebAuthFlowFunction::RunAsync() {
714 if (GetProfile()->IsOffTheRecord()) {
715 error_ = identity_constants::kOffTheRecord;
719 scoped_ptr<identity::LaunchWebAuthFlow::Params> params(
720 identity::LaunchWebAuthFlow::Params::Create(*args_));
721 EXTENSION_FUNCTION_VALIDATE(params.get());
723 GURL auth_url(params->details.url);
724 WebAuthFlow::Mode mode =
725 params->details.interactive && *params->details.interactive ?
726 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT;
728 // Set up acceptable target URLs. (Does not include chrome-extension
729 // scheme for this version of the API.)
730 InitFinalRedirectURLPrefix(GetExtension()->id());
732 AddRef(); // Balanced in OnAuthFlowSuccess/Failure.
734 auth_flow_.reset(new WebAuthFlow(this, GetProfile(), auth_url, mode));
739 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest(
740 const std::string& extension_id) {
741 InitFinalRedirectURLPrefix(extension_id);
744 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix(
745 const std::string& extension_id) {
746 if (final_url_prefix_.is_empty()) {
747 final_url_prefix_ = GURL(base::StringPrintf(
748 kChromiumDomainRedirectUrlPattern, extension_id.c_str()));
752 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure(
753 WebAuthFlow::Failure failure) {
755 case WebAuthFlow::WINDOW_CLOSED:
756 error_ = identity_constants::kUserRejected;
758 case WebAuthFlow::INTERACTION_REQUIRED:
759 error_ = identity_constants::kInteractionRequired;
761 case WebAuthFlow::LOAD_FAILED:
762 error_ = identity_constants::kPageLoadFailure;
765 NOTREACHED() << "Unexpected error from web auth flow: " << failure;
766 error_ = identity_constants::kInvalidRedirect;
770 Release(); // Balanced in RunAsync.
773 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
774 const GURL& redirect_url) {
775 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) {
776 SetResult(new base::StringValue(redirect_url.spec()));
778 Release(); // Balanced in RunAsync.
782 } // namespace extensions