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