identity_api.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Use of this source code is governed by a BSD-style license that can be 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// found in the LICENSE file. 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "chrome/browser/extensions/api/identity/identity_api.h" 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <set> 8a5572e5bb2a2bbeeb59de0741c2527869d365a0ccommit-bot@chromium.org#include <string> 9a5572e5bb2a2bbeeb59de0741c2527869d365a0ccommit-bot@chromium.org#include <utility> 10a5572e5bb2a2bbeeb59de0741c2527869d365a0ccommit-bot@chromium.org#include <vector> 11a5572e5bb2a2bbeeb59de0741c2527869d365a0ccommit-bot@chromium.org 12c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.org#include "base/lazy_instance.h" 13c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.org#include "base/prefs/pref_service.h" 14d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org#include "base/strings/string_number_conversions.h" 158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "base/strings/stringprintf.h" 168b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "base/values.h" 178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/app_mode/app_mode_utils.h" 189c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/browser_process.h" 199c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/chrome_notification_types.h" 209c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/extensions/extension_service.h" 219c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/profiles/profile.h" 229c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 239c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org#include "chrome/browser/signin/signin_global_error.h" 248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/signin/signin_manager_factory.h" 258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/extensions/api/identity.h" 268b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 27f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "chrome/common/pref_names.h" 288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/url_constants.h" 29f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "components/signin/core/browser/profile_oauth2_token_service.h" 30f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "components/signin/core/browser/signin_manager.h" 31f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "extensions/browser/event_router.h" 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "extensions/browser/extension_function_dispatcher.h" 338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "extensions/browser/extension_system.h" 348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "extensions/common/extension.h" 358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "google_apis/gaia/gaia_urls.h" 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "url/gurl.h" 378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 388b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#if defined(OS_CHROMEOS) 398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/chromeos/login/user_manager.h" 40f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" 41f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 42f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 43f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com#include "google_apis/gaia/gaia_constants.h" 448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comnamespace extensions { 4787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comnamespace identity_constants { 498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst char kInvalidClientId[] = "Invalid OAuth2 Client ID."; 508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst char kInvalidScopes[] = "Invalid OAuth2 scopes."; 51f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.comconst char kAuthFailure[] = "OAuth2 request failed: "; 52f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.comconst char kNoGrant[] = "OAuth2 not granted or revoked."; 538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst char kUserRejected[] = "The user did not approve access."; 548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst char kUserNotSignedIn[] = "The user is not signed in."; 5587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst char kInteractionRequired[] = "User interaction required."; 5687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst char kInvalidRedirect[] = "Did not redirect to the right URL."; 5787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst char kOffTheRecord[] = "Identity API is disabled in incognito windows."; 5887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst char kPageLoadFailure[] = "Authorization page could not be loaded."; 5987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst char kCanceled[] = "canceled"; 6087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 6187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgconst int kCachedIssueAdviceTTLSeconds = 1; 6287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org} // namespace identity_constants 6387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comnamespace { 658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 6687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstatic const char kChromiumDomainRedirectUrlPattern[] = 6787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org "https://%s.chromiumapp.org/"; 6887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 6987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgstd::string GetPrimaryAccountId(content::BrowserContext* context) { 7087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org SigninManagerBase* signin_manager = 7187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org SigninManagerFactory::GetForProfile(Profile::FromBrowserContext(context)); 7287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org return signin_manager->GetAuthenticatedAccountId(); 7387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org} 7487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 7587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org} // namespace 7687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 7787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgnamespace identity = api::identity; 78a641f3f18e5319773989812a888f3fad49e4f2adreed@google.com 79a641f3f18e5319773989812a888f3fad49e4f2adreed@google.comIdentityTokenCacheValue::IdentityTokenCacheValue() 8087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org : status_(CACHE_STATUS_NOTFOUND) {} 8187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 8287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgIdentityTokenCacheValue::IdentityTokenCacheValue( 833bafe74a29c37761082980ed4ee9b831256bd27ereed@google.com const IssueAdviceInfo& issue_advice) 843bafe74a29c37761082980ed4ee9b831256bd27ereed@google.com : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) { 853bafe74a29c37761082980ed4ee9b831256bd27ereed@google.com expiration_time_ = 868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com base::Time::Now() + base::TimeDelta::FromSeconds( 878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com identity_constants::kCachedIssueAdviceTTLSeconds); 8887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org} 898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comIdentityTokenCacheValue::IdentityTokenCacheValue(const std::string& token, 918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com base::TimeDelta time_to_live) 928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com : status_(CACHE_STATUS_TOKEN), token_(token) { 938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // Remove 20 minutes from the ttl so cached tokens will have some time 940c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com // to live any time they are returned. 958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com time_to_live -= base::TimeDelta::FromMinutes(20); 968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com base::TimeDelta zero_delta; 987c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com if (time_to_live < zero_delta) 998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com time_to_live = zero_delta; 1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com expiration_time_ = base::Time::Now() + time_to_live; 1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comIdentityTokenCacheValue::~IdentityTokenCacheValue() {} 1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 10687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.orgIdentityTokenCacheValue::CacheValueStatus IdentityTokenCacheValue::status() 1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com const { 1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if (is_expired()) 1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND; 1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com else 1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return status_; 1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const { 1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return issue_advice_; 1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst std::string& IdentityTokenCacheValue::token() const { return token_; } 1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.combool IdentityTokenCacheValue::is_expired() const { 1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return status_ == CACHE_STATUS_NOTFOUND || 1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com expiration_time_ < base::Time::Now(); 1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst base::Time& IdentityTokenCacheValue::expiration_time() const { 1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return expiration_time_; 1278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comIdentityAPI::IdentityAPI(content::BrowserContext* context) 1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com : browser_context_(context), 1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com account_tracker_(Profile::FromBrowserContext(context)) { 1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com account_tracker_.AddObserver(this); 1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comIdentityAPI::~IdentityAPI() {} 1368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comIdentityMintRequestQueue* IdentityAPI::mint_queue() { return &mint_queue_; } 1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid IdentityAPI::SetCachedToken(const ExtensionTokenKey& key, 1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com const IdentityTokenCacheValue& token_data) { 1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com CachedTokens::iterator it = token_cache_.find(key); 1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if (it != token_cache_.end() && it->second.status() <= token_data.status()) 1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com token_cache_.erase(it); 1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com token_cache_.insert(std::make_pair(key, token_data)); 1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid IdentityAPI::EraseCachedToken(const std::string& extension_id, 1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com const std::string& token) { 1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com CachedTokens::iterator it; 1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com for (it = token_cache_.begin(); it != token_cache_.end(); ++it) { 1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if (it->first.extension_id == extension_id && 1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN && 1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com it->second.token() == token) { 1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com token_cache_.erase(it); 1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com break; 1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 16087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid IdentityAPI::EraseAllCachedTokens() { token_cache_.clear(); } 1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1638d430185e08d2067584837a76b7193b803fee7a0tomhudson@google.comconst IdentityTokenCacheValue& IdentityAPI::GetCachedToken( 1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com const ExtensionTokenKey& key) { 1658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return token_cache_[key]; 1668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comconst IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { 1698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return token_cache_; 1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { 1738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com account_tracker_.ReportAuthError(GetPrimaryAccountId(browser_context_), 1748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com error); 17587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org} 176f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com 1778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comGoogleServiceAuthError IdentityAPI::GetAuthStatusForTest() const { 1788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return account_tracker_.GetAuthStatus(); 179d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org} 180d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org 181d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.orgvoid IdentityAPI::Shutdown() { 182d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org FOR_EACH_OBSERVER(ShutdownObserver, shutdown_observer_list_, OnShutdown()); 1830ac6af49975c54c2debf41e9200af416ecd2d973bsalomon@google.com account_tracker_.RemoveObserver(this); 184a3bdc1a6cd1ce9630df43a28fffef17a12c41f32humper@google.com account_tracker_.Shutdown(); 18503c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com} 18603c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com 1879c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.orgstatic base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI> > 1889c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org g_factory = LAZY_INSTANCE_INITIALIZER; 1899c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org 1908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// static 1918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comBrowserContextKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { 192c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.org return g_factory.Pointer(); 193c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.org} 194c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.org 195c5d9bb0f677069f62ec76373b9730e70e7352455commit-bot@chromium.orgvoid IdentityAPI::OnAccountAdded(const AccountIds& ids) {} 1960f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.org 19776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid IdentityAPI::OnAccountRemoved(const AccountIds& ids) {} 19876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 19976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid IdentityAPI::OnAccountSignInChanged(const AccountIds& ids, 20076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com bool is_signed_in) { 20176f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com api::identity::AccountInfo account_info; 20276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com account_info.id = ids.gaia; 20376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 20476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com scoped_ptr<base::ListValue> args = 2058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com api::identity::OnSignInChanged::Create(account_info, is_signed_in); 2068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com scoped_ptr<Event> event(new Event(api::identity::OnSignInChanged::kEventName, 2078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com args.Pass(), 2088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com browser_context_)); 2098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 21087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org ExtensionSystem::Get(browser_context_)->event_router()->BroadcastEvent( 21176a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org event.Pass()); 212f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com} 213f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com 214b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.orgvoid IdentityAPI::AddShutdownObserver(ShutdownObserver* observer) { 215b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org shutdown_observer_list_.AddObserver(observer); 216b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org} 217b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org 2188b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgvoid IdentityAPI::RemoveShutdownObserver(ShutdownObserver* observer) { 21976a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org shutdown_observer_list_.RemoveObserver(observer); 22076a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org} 22176a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org 22276a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.orgtemplate <> 22376a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.orgvoid BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { 22476a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); 22576a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); 22676a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org} 2278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 228c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.comIdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() 2298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com : OAuth2TokenService::Consumer("extensions_identity_api"), 2308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com should_prompt_for_scopes_(false), 2318b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org should_prompt_for_signin_(false) {} 2328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 233c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.comIdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} 2348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 2358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.combool IdentityGetAuthTokenFunction::RunImpl() { 23687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org if (GetProfile()->IsOffTheRecord()) { 23759ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com error_ = identity_constants::kOffTheRecord; 23859ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com return false; 23959ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com } 24087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 2418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com scoped_ptr<identity::GetAuthToken::Params> params( 2428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com identity::GetAuthToken::Params::Create(*args_)); 2438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com EXTENSION_FUNCTION_VALIDATE(params.get()); 24487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org bool interactive = params->details.get() && 24587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org params->details->interactive.get() && 24687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org *params->details->interactive; 24787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 2488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com should_prompt_for_scopes_ = interactive; 2498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com should_prompt_for_signin_ = interactive; 25087fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 25187fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 25287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 25387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org // Check that the necessary information is present in the manifest. 25487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org oauth2_client_id_ = GetOAuth2ClientId(); 25587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org if (oauth2_client_id_.empty()) { 25687fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org error_ = identity_constants::kInvalidClientId; 25787fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org return false; 25887fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org } 2599c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org 2609c9005a347e9996f357bd79591bd34f74f8bbc66commit-bot@chromium.org if (oauth2_info.scopes.size() == 0) { 2618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com error_ = identity_constants::kInvalidScopes; 26287fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org return false; 26387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org } 26487fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 2658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com std::set<std::string> scopes(oauth2_info.scopes.begin(), 2668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com oauth2_info.scopes.end()); 2678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com token_key_.reset(new ExtensionTokenKey( 2688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com GetExtension()->id(), GetPrimaryAccountId(GetProfile()), scopes)); 2698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 2708f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com // From here on out, results must be returned asynchronously. 2718f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com StartAsyncRun(); 2728f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com 2738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if defined(OS_CHROMEOS) 2748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com policy::BrowserPolicyConnectorChromeOS* connector = 2758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com g_browser_process->platform_part()->browser_policy_connector_chromeos(); 2768f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp() && 2775b81535014f545f6498f5c8721723b81576989b1reed@android.com connector->IsEnterpriseManaged()) { 2785119bdb952025a30f115b9c6a187173956e55097reed@android.com StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 2795b81535014f545f6498f5c8721723b81576989b1reed@android.com return true; 2805b81535014f545f6498f5c8721723b81576989b1reed@android.com } 2815b81535014f545f6498f5c8721723b81576989b1reed@android.com#endif 2825119bdb952025a30f115b9c6a187173956e55097reed@android.com 2838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if (!HasLoginToken()) { 2848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if (!should_prompt_for_signin_) { 28587fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org CompleteFunctionWithError(identity_constants::kUserNotSignedIn); 2868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return true; 2878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 2888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // Display a login prompt. 28987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org StartSigninFlow(); 2908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } else { 2918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 2928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 29387fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org 2948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return true; 2958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 2968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 297f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.comvoid IdentityGetAuthTokenFunction::StartAsyncRun() { 298f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com // Balanced in CompleteAsyncRun 29991f319c5dc4493384f0a52aaeef3dcc311ef6ed0rileya@google.com AddRef(); 3002be9e8b407624fa696854b78b407b97a01dbb703reed@google.com extensions::IdentityAPI::GetFactoryInstance() 301f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com ->Get(GetProfile()) 302f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com ->AddShutdownObserver(this); 303d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org} 304d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org 305d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.orgvoid IdentityGetAuthTokenFunction::CompleteAsyncRun(bool success) { 306d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org extensions::IdentityAPI::GetFactoryInstance() 307d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org ->Get(GetProfile()) 308d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org ->RemoveShutdownObserver(this); 309d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org 310d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org SendResponse(success); 311d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org Release(); // Balanced in StartAsyncRun 312d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org} 31337a201231b8f6381938282675eb9abb50ab3b389reed@google.com 3140f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.orgvoid IdentityGetAuthTokenFunction::CompleteFunctionWithResult( 31576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com const std::string& access_token) { 31676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 31776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com SetResult(new base::StringValue(access_token)); 31876a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org CompleteAsyncRun(true); 31976a3b2abd02841c4ae786ac4cf59c3a51c545f73commit-bot@chromium.org} 32076f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 32176f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid IdentityGetAuthTokenFunction::CompleteFunctionWithError( 32276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com const std::string& error) { 32376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com error_ = error; 32476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com CompleteAsyncRun(false); 32576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com} 32676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 32737a201231b8f6381938282675eb9abb50ab3b389reed@google.comvoid IdentityGetAuthTokenFunction::StartSigninFlow() { 32837a201231b8f6381938282675eb9abb50ab3b389reed@google.com // All cached tokens are invalid because the user is not signed in. 32987fcd950198a16211b3988610beebb5ca5bcf323commit-bot@chromium.org IdentityAPI* id_api = 33037a201231b8f6381938282675eb9abb50ab3b389reed@google.com extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 33137a201231b8f6381938282675eb9abb50ab3b389reed@google.com id_api->EraseAllCachedTokens(); 33276f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com // Display a login prompt. If the subsequent mint fails, don't display the 33376f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com // login prompt again. 33476f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com should_prompt_for_signin_ = false; 33576f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com ShowLoginPopup(); 33676f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com} 33776f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com 33876f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.comvoid IdentityGetAuthTokenFunction::StartMintTokenFlow( 33976f9e938df0b5826fd4c80b854ceafaf385cfbe1robertphillips@google.com IdentityMintRequestQueue::MintType type) { 340 mint_token_flow_type_ = type; 341 342 // Flows are serialized to prevent excessive traffic to GAIA, and 343 // to consolidate UI pop-ups. 344 IdentityAPI* id_api = 345 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 346 347 if (!should_prompt_for_scopes_) { 348 // Caller requested no interaction. 349 350 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) { 351 // GAIA told us to do a consent UI. 352 CompleteFunctionWithError(identity_constants::kNoGrant); 353 return; 354 } 355 if (!id_api->mint_queue()->empty( 356 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) { 357 // Another call is going through a consent UI. 358 CompleteFunctionWithError(identity_constants::kNoGrant); 359 return; 360 } 361 } 362 id_api->mint_queue()->RequestStart(type, *token_key_, this); 363} 364 365void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() { 366 IdentityMintRequestQueue::MintType type = mint_token_flow_type_; 367 368 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 369 std::set<std::string> scopes(oauth2_info.scopes.begin(), 370 oauth2_info.scopes.end()); 371 372 extensions::IdentityAPI::GetFactoryInstance() 373 ->Get(GetProfile()) 374 ->mint_queue() 375 ->RequestComplete(type, *token_key_, this); 376} 377 378void IdentityGetAuthTokenFunction::StartMintToken( 379 IdentityMintRequestQueue::MintType type) { 380 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 381 IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 382 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_); 383 IdentityTokenCacheValue::CacheValueStatus cache_status = 384 cache_entry.status(); 385 386 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) { 387 switch (cache_status) { 388 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND: 389#if defined(OS_CHROMEOS) 390 // Always force minting token for ChromeOS kiosk app. 391 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) { 392 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; 393 policy::BrowserPolicyConnectorChromeOS* connector = 394 g_browser_process->platform_part() 395 ->browser_policy_connector_chromeos(); 396 if (connector->IsEnterpriseManaged()) { 397 StartDeviceLoginAccessTokenRequest(); 398 } else { 399 StartLoginAccessTokenRequest(); 400 } 401 return; 402 } 403#endif 404 405 if (oauth2_info.auto_approve) 406 // oauth2_info.auto_approve is protected by a whitelist in 407 // _manifest_features.json hence only selected extensions take 408 // advantage of forcefully minting the token. 409 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; 410 else 411 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE; 412 StartLoginAccessTokenRequest(); 413 break; 414 415 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN: 416 CompleteMintTokenFlow(); 417 CompleteFunctionWithResult(cache_entry.token()); 418 break; 419 420 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE: 421 CompleteMintTokenFlow(); 422 should_prompt_for_signin_ = false; 423 issue_advice_ = cache_entry.issue_advice(); 424 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 425 break; 426 } 427 } else { 428 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 429 430 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) { 431 CompleteMintTokenFlow(); 432 CompleteFunctionWithResult(cache_entry.token()); 433 } else { 434 ShowOAuthApprovalDialog(issue_advice_); 435 } 436 } 437} 438 439void IdentityGetAuthTokenFunction::OnMintTokenSuccess( 440 const std::string& access_token, int time_to_live) { 441 IdentityTokenCacheValue token(access_token, 442 base::TimeDelta::FromSeconds(time_to_live)); 443 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 444 *token_key_, token); 445 446 CompleteMintTokenFlow(); 447 CompleteFunctionWithResult(access_token); 448} 449 450void IdentityGetAuthTokenFunction::OnMintTokenFailure( 451 const GoogleServiceAuthError& error) { 452 CompleteMintTokenFlow(); 453 454 switch (error.state()) { 455 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 456 case GoogleServiceAuthError::ACCOUNT_DELETED: 457 case GoogleServiceAuthError::ACCOUNT_DISABLED: 458 extensions::IdentityAPI::GetFactoryInstance() 459 ->Get(GetProfile()) 460 ->ReportAuthError(error); 461 if (should_prompt_for_signin_) { 462 // Display a login prompt and try again (once). 463 StartSigninFlow(); 464 return; 465 } 466 break; 467 default: 468 // Return error to caller. 469 break; 470 } 471 472 CompleteFunctionWithError( 473 std::string(identity_constants::kAuthFailure) + error.ToString()); 474} 475 476void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( 477 const IssueAdviceInfo& issue_advice) { 478 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 479 *token_key_, IdentityTokenCacheValue(issue_advice)); 480 CompleteMintTokenFlow(); 481 482 should_prompt_for_signin_ = false; 483 // Existing grant was revoked and we used NO_FORCE, so we got info back 484 // instead. Start a consent UI if we can. 485 issue_advice_ = issue_advice; 486 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 487} 488 489void IdentityGetAuthTokenFunction::SigninSuccess() { 490 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 491} 492 493void IdentityGetAuthTokenFunction::SigninFailed() { 494 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); 495} 496 497void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( 498 GaiaWebAuthFlow::Failure failure, 499 GoogleServiceAuthError service_error, 500 const std::string& oauth_error) { 501 CompleteMintTokenFlow(); 502 std::string error; 503 504 switch (failure) { 505 case GaiaWebAuthFlow::WINDOW_CLOSED: 506 error = identity_constants::kUserRejected; 507 break; 508 509 case GaiaWebAuthFlow::INVALID_REDIRECT: 510 error = identity_constants::kInvalidRedirect; 511 break; 512 513 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR: 514 error = std::string(identity_constants::kAuthFailure) + 515 service_error.ToString(); 516 break; 517 518 case GaiaWebAuthFlow::OAUTH_ERROR: 519 error = MapOAuth2ErrorToDescription(oauth_error); 520 break; 521 522 case GaiaWebAuthFlow::LOAD_FAILED: 523 error = identity_constants::kPageLoadFailure; 524 break; 525 526 default: 527 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; 528 error = identity_constants::kInvalidRedirect; 529 break; 530 } 531 532 CompleteFunctionWithError(error); 533} 534 535void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted( 536 const std::string& access_token, 537 const std::string& expiration) { 538 539 int time_to_live; 540 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) { 541 IdentityTokenCacheValue token_value( 542 access_token, base::TimeDelta::FromSeconds(time_to_live)); 543 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 544 *token_key_, token_value); 545 } 546 547 CompleteMintTokenFlow(); 548 CompleteFunctionWithResult(access_token); 549} 550 551void IdentityGetAuthTokenFunction::OnGetTokenSuccess( 552 const OAuth2TokenService::Request* request, 553 const std::string& access_token, 554 const base::Time& expiration_time) { 555 login_token_request_.reset(); 556 StartGaiaRequest(access_token); 557} 558 559void IdentityGetAuthTokenFunction::OnGetTokenFailure( 560 const OAuth2TokenService::Request* request, 561 const GoogleServiceAuthError& error) { 562 login_token_request_.reset(); 563 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string()); 564} 565 566void IdentityGetAuthTokenFunction::OnShutdown() { 567 gaia_web_auth_flow_.reset(); 568 signin_flow_.reset(); 569 login_token_request_.reset(); 570 extensions::IdentityAPI::GetFactoryInstance() 571 ->Get(GetProfile()) 572 ->mint_queue() 573 ->RequestCancel(*token_key_, this); 574 CompleteFunctionWithError(identity_constants::kCanceled); 575} 576 577#if defined(OS_CHROMEOS) 578void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() { 579 chromeos::DeviceOAuth2TokenService* service = 580 chromeos::DeviceOAuth2TokenServiceFactory::Get(); 581 // Since robot account refresh tokens are scoped down to [any-api] only, 582 // request access token for [any-api] instead of login. 583 OAuth2TokenService::ScopeSet scopes; 584 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); 585 login_token_request_ = 586 service->StartRequest(service->GetRobotAccountId(), 587 scopes, 588 this); 589} 590#endif 591 592void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() { 593 ProfileOAuth2TokenService* service = 594 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); 595 const std::string primary_account_id = GetPrimaryAccountId(GetProfile()); 596#if defined(OS_CHROMEOS) 597 if (chrome::IsRunningInForcedAppMode()) { 598 std::string app_client_id; 599 std::string app_client_secret; 600 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo( 601 &app_client_id, &app_client_secret)) { 602 login_token_request_ = 603 service->StartRequestForClient(primary_account_id, 604 app_client_id, 605 app_client_secret, 606 OAuth2TokenService::ScopeSet(), 607 this); 608 return; 609 } 610 } 611#endif 612 login_token_request_ = service->StartRequest( 613 primary_account_id, OAuth2TokenService::ScopeSet(), this); 614} 615 616void IdentityGetAuthTokenFunction::StartGaiaRequest( 617 const std::string& login_access_token) { 618 DCHECK(!login_access_token.empty()); 619 mint_token_flow_.reset(CreateMintTokenFlow(login_access_token)); 620 mint_token_flow_->Start(); 621} 622 623void IdentityGetAuthTokenFunction::ShowLoginPopup() { 624 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile())); 625 signin_flow_->Start(); 626} 627 628void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( 629 const IssueAdviceInfo& issue_advice) { 630 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 631 const std::string locale = g_browser_process->local_state()->GetString( 632 prefs::kApplicationLocale); 633 634 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow( 635 this, GetProfile(), GetExtension()->id(), oauth2_info, locale)); 636 gaia_web_auth_flow_->Start(); 637} 638 639OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( 640 const std::string& login_access_token) { 641 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 642 643 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow( 644 GetProfile()->GetRequestContext(), 645 this, 646 OAuth2MintTokenFlow::Parameters(login_access_token, 647 GetExtension()->id(), 648 oauth2_client_id_, 649 oauth2_info.scopes, 650 gaia_mint_token_mode_)); 651 return mint_token_flow; 652} 653 654bool IdentityGetAuthTokenFunction::HasLoginToken() const { 655 ProfileOAuth2TokenService* token_service = 656 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); 657 return token_service->RefreshTokenIsAvailable( 658 GetPrimaryAccountId(GetProfile())); 659} 660 661std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription( 662 const std::string& error) { 663 const char kOAuth2ErrorAccessDenied[] = "access_denied"; 664 const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; 665 666 if (error == kOAuth2ErrorAccessDenied) 667 return std::string(identity_constants::kUserRejected); 668 else if (error == kOAuth2ErrorInvalidScope) 669 return std::string(identity_constants::kInvalidScopes); 670 else 671 return std::string(identity_constants::kAuthFailure) + error; 672} 673 674std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const { 675 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 676 std::string client_id = oauth2_info.client_id; 677 678 // Component apps using auto_approve may use Chrome's client ID by 679 // omitting the field. 680 if (client_id.empty() && GetExtension()->location() == Manifest::COMPONENT && 681 oauth2_info.auto_approve) { 682 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); 683 } 684 return client_id; 685} 686 687IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() { 688} 689 690IdentityRemoveCachedAuthTokenFunction:: 691 ~IdentityRemoveCachedAuthTokenFunction() { 692} 693 694bool IdentityRemoveCachedAuthTokenFunction::RunImpl() { 695 if (GetProfile()->IsOffTheRecord()) { 696 error_ = identity_constants::kOffTheRecord; 697 return false; 698 } 699 700 scoped_ptr<identity::RemoveCachedAuthToken::Params> params( 701 identity::RemoveCachedAuthToken::Params::Create(*args_)); 702 EXTENSION_FUNCTION_VALIDATE(params.get()); 703 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->EraseCachedToken( 704 GetExtension()->id(), params->details.token); 705 return true; 706} 707 708IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {} 709 710IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() { 711 if (auth_flow_) 712 auth_flow_.release()->DetachDelegateAndDelete(); 713} 714 715bool IdentityLaunchWebAuthFlowFunction::RunImpl() { 716 if (GetProfile()->IsOffTheRecord()) { 717 error_ = identity_constants::kOffTheRecord; 718 return false; 719 } 720 721 scoped_ptr<identity::LaunchWebAuthFlow::Params> params( 722 identity::LaunchWebAuthFlow::Params::Create(*args_)); 723 EXTENSION_FUNCTION_VALIDATE(params.get()); 724 725 GURL auth_url(params->details.url); 726 WebAuthFlow::Mode mode = 727 params->details.interactive && *params->details.interactive ? 728 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT; 729 730 // Set up acceptable target URLs. (Does not include chrome-extension 731 // scheme for this version of the API.) 732 InitFinalRedirectURLPrefix(GetExtension()->id()); 733 734 AddRef(); // Balanced in OnAuthFlowSuccess/Failure. 735 736 auth_flow_.reset(new WebAuthFlow(this, GetProfile(), auth_url, mode)); 737 auth_flow_->Start(); 738 return true; 739} 740 741void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest( 742 const std::string& extension_id) { 743 InitFinalRedirectURLPrefix(extension_id); 744} 745 746void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix( 747 const std::string& extension_id) { 748 if (final_url_prefix_.is_empty()) { 749 final_url_prefix_ = GURL(base::StringPrintf( 750 kChromiumDomainRedirectUrlPattern, extension_id.c_str())); 751 } 752} 753 754void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure( 755 WebAuthFlow::Failure failure) { 756 switch (failure) { 757 case WebAuthFlow::WINDOW_CLOSED: 758 error_ = identity_constants::kUserRejected; 759 break; 760 case WebAuthFlow::INTERACTION_REQUIRED: 761 error_ = identity_constants::kInteractionRequired; 762 break; 763 case WebAuthFlow::LOAD_FAILED: 764 error_ = identity_constants::kPageLoadFailure; 765 break; 766 default: 767 NOTREACHED() << "Unexpected error from web auth flow: " << failure; 768 error_ = identity_constants::kInvalidRedirect; 769 break; 770 } 771 SendResponse(false); 772 Release(); // Balanced in RunImpl. 773} 774 775void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange( 776 const GURL& redirect_url) { 777 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) { 778 SetResult(new base::StringValue(redirect_url.spec())); 779 SendResponse(true); 780 Release(); // Balanced in RunImpl. 781 } 782} 783 784} // namespace extensions 785