summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcourage@chromium.org <courage@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-05 17:18:01 +0000
committercourage@chromium.org <courage@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-05 17:18:01 +0000
commit47600beff0c5d467201cca76757e039e0a863fdb (patch)
tree334e8d3fc0e6a4f6ad98877a5aed28725994c94f
parenta4470ecccba04d91fadd470ef7f2c453644f7418 (diff)
downloadchromium_src-47600beff0c5d467201cca76757e039e0a863fdb.zip
chromium_src-47600beff0c5d467201cca76757e039e0a863fdb.tar.gz
chromium_src-47600beff0c5d467201cca76757e039e0a863fdb.tar.bz2
Multiple account support in chrome.identity.getAuthToken
This change adds an optional "account" field in the TokenDetails parameter to getAuthToken. When this field is populated with an object identifying a valid account signed in on the current profile, getAuthToken will return a token for the specified account. When the parameter is not present, getAuthToken behaves as before, returning tokens for the primary profile user. BUG=258515 Review URL: https://codereview.chromium.org/293063002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275173 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/identity/account_tracker.cc14
-rw-r--r--chrome/browser/extensions/api/identity/account_tracker.h1
-rw-r--r--chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc11
-rw-r--r--chrome/browser/extensions/api/identity/gaia_web_auth_flow.h6
-rw-r--r--chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc1
-rw-r--r--chrome/browser/extensions/api/identity/identity_api.cc45
-rw-r--r--chrome/browser/extensions/api/identity/identity_api.h40
-rw-r--r--chrome/browser/extensions/api/identity/identity_apitest.cc273
-rw-r--r--chrome/common/extensions/api/identity.idl19
9 files changed, 354 insertions, 56 deletions
diff --git a/chrome/browser/extensions/api/identity/account_tracker.cc b/chrome/browser/extensions/api/identity/account_tracker.cc
index 6afc9f9..a93f505 100644
--- a/chrome/browser/extensions/api/identity/account_tracker.cc
+++ b/chrome/browser/extensions/api/identity/account_tracker.cc
@@ -77,6 +77,20 @@ std::vector<AccountIds> AccountTracker::GetAccounts() const {
return accounts;
}
+std::string AccountTracker::FindAccountKeyByGaiaId(const std::string& gaia_id) {
+ for (std::map<std::string, AccountState>::const_iterator it =
+ accounts_.begin();
+ it != accounts_.end();
+ ++it) {
+ const AccountState& state = it->second;
+ if (state.ids.gaia == gaia_id) {
+ return state.ids.account_key;
+ }
+ }
+
+ return std::string();
+}
+
void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) {
// Ignore refresh tokens if there is no primary account ID at all.
if (signin_manager_account_id().empty())
diff --git a/chrome/browser/extensions/api/identity/account_tracker.h b/chrome/browser/extensions/api/identity/account_tracker.h
index 7c8db2c..3cd3a98 100644
--- a/chrome/browser/extensions/api/identity/account_tracker.h
+++ b/chrome/browser/extensions/api/identity/account_tracker.h
@@ -64,6 +64,7 @@ class AccountTracker : public OAuth2TokenService::Observer,
// have been fetched. The primary account for the profile will be first
// in the vector. Additional accounts will be in order of their gaia IDs.
std::vector<AccountIds> GetAccounts() const;
+ std::string FindAccountKeyByGaiaId(const std::string& gaia_id);
// OAuth2TokenService::Observer implementation.
virtual void OnRefreshTokenAvailable(const std::string& account_key) OVERRIDE;
diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc
index 5da652d..49d4d1e 100644
--- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc
+++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc
@@ -20,11 +20,11 @@ namespace extensions {
GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate,
Profile* profile,
+ const std::string& account_id,
const std::string& extension_id,
const OAuth2Info& oauth2_info,
const std::string& locale)
- : delegate_(delegate),
- profile_(profile) {
+ : delegate_(delegate), profile_(profile), account_id_(account_id) {
const char kOAuth2RedirectPathFormat[] = "/%s#";
const char kOAuth2AuthorizeFormat[] =
"?response_type=token&approval_prompt=force&authuser=0&"
@@ -64,10 +64,7 @@ void GaiaWebAuthFlow::Start() {
ubertoken_fetcher_.reset(new UbertokenFetcher(token_service,
this,
profile_->GetRequestContext()));
- SigninManagerBase* signin_manager =
- SigninManagerFactory::GetForProfile(profile_);
- ubertoken_fetcher_->StartFetchingToken(
- signin_manager->GetAuthenticatedAccountId());
+ ubertoken_fetcher_->StartFetchingToken(account_id_);
}
void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) {
@@ -87,6 +84,7 @@ void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) {
}
void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) {
+ DVLOG(1) << "OnUbertokenFailure: " << error.error_message();
delegate_->OnGaiaFlowFailure(
GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
}
@@ -99,6 +97,7 @@ void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
gaia_failure = GaiaWebAuthFlow::WINDOW_CLOSED;
break;
case WebAuthFlow::LOAD_FAILED:
+ DVLOG(1) << "OnAuthFlowFailure LOAD_FAILED";
gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
break;
default:
diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h
index 684fb0b..1b2abae 100644
--- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h
+++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.h
@@ -14,7 +14,7 @@ namespace extensions {
// Implements a web-based OAuth2 scope approval dialog. This flow has
// four parts:
-// 1. Fetch an ubertoken for the signed-in user.
+// 1. Fetch an ubertoken for a signed-in user.
// 2. Use the ubertoken to get session cookies using MergeSession.
// 3. Start the OAuth flow and wait for final redirect.
// 4. Parse results from the fragment component of the final redirect URI.
@@ -60,6 +60,7 @@ class GaiaWebAuthFlow : public UbertokenConsumer, public WebAuthFlow::Delegate {
GaiaWebAuthFlow(Delegate* delegate,
Profile* profile,
+ const std::string& account_id,
const std::string& extension_id,
const OAuth2Info& oauth2_info,
const std::string& locale);
@@ -84,6 +85,7 @@ class GaiaWebAuthFlow : public UbertokenConsumer, public WebAuthFlow::Delegate {
Delegate* delegate_;
Profile* profile_;
+ std::string account_id_;
chrome::HostDesktopType host_desktop_type_;
std::string redirect_scheme_;
std::string redirect_path_prefix_;
@@ -94,6 +96,6 @@ class GaiaWebAuthFlow : public UbertokenConsumer, public WebAuthFlow::Delegate {
DISALLOW_COPY_AND_ASSIGN(GaiaWebAuthFlow);
};
-} // extensions
+} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_WEB_AUTH_FLOW_H_
diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc b/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc
index a55c533..4b61e04 100644
--- a/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc
+++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc
@@ -33,6 +33,7 @@ class TestGaiaWebAuthFlow : public GaiaWebAuthFlow {
GoogleServiceAuthError::State ubertoken_error_state)
: GaiaWebAuthFlow(delegate,
NULL,
+ "account_id",
"extension_id",
oauth2_info,
"en-us"),
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc
index d3571e9..17c973a 100644
--- a/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -169,6 +169,7 @@ const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() {
}
std::vector<std::string> IdentityAPI::GetAccounts() const {
+ const std::string primary_account_id = GetPrimaryAccountId(browser_context_);
const std::vector<AccountIds> ids = account_tracker_.GetAccounts();
std::vector<std::string> gaia_ids;
@@ -185,6 +186,10 @@ std::vector<std::string> IdentityAPI::GetAccounts() const {
return gaia_ids;
}
+std::string IdentityAPI::FindAccountKeyByGaiaId(const std::string& gaia_id) {
+ return account_tracker_.FindAccountKeyByGaiaId(gaia_id);
+}
+
void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
account_tracker_.ReportAuthError(GetPrimaryAccountId(browser_context_),
error);
@@ -317,8 +322,28 @@ bool IdentityGetAuthTokenFunction::RunAsync() {
std::set<std::string> scopes(oauth2_info.scopes.begin(),
oauth2_info.scopes.end());
- token_key_.reset(new ExtensionTokenKey(
- GetExtension()->id(), GetPrimaryAccountId(GetProfile()), scopes));
+
+ std::string account_key = GetPrimaryAccountId(GetProfile());
+
+ if (params->details->account.get()) {
+ std::string detail_key =
+ extensions::IdentityAPI::GetFactoryInstance()
+ ->Get(GetProfile())
+ ->FindAccountKeyByGaiaId(params->details->account->id);
+
+ if (detail_key != account_key) {
+ if (detail_key.empty() || !switches::IsExtensionsMultiAccount()) {
+ // TODO(courage): should this be a different error?
+ error_ = identity_constants::kUserNotSignedIn;
+ return false;
+ }
+
+ account_key = detail_key;
+ }
+ }
+
+ token_key_.reset(
+ new ExtensionTokenKey(GetExtension()->id(), account_key, scopes));
// From here on out, results must be returned asynchronously.
StartAsyncRun();
@@ -645,7 +670,6 @@ void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() {
void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
ProfileOAuth2TokenService* service =
ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
- const std::string primary_account_id = GetPrimaryAccountId(GetProfile());
#if defined(OS_CHROMEOS)
if (chrome::IsRunningInForcedAppMode()) {
std::string app_client_id;
@@ -653,7 +677,7 @@ void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
&app_client_id, &app_client_secret)) {
login_token_request_ =
- service->StartRequestForClient(primary_account_id,
+ service->StartRequestForClient(token_key_->account_id,
app_client_id,
app_client_secret,
OAuth2TokenService::ScopeSet(),
@@ -663,7 +687,7 @@ void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
}
#endif
login_token_request_ = service->StartRequest(
- primary_account_id, OAuth2TokenService::ScopeSet(), this);
+ token_key_->account_id, OAuth2TokenService::ScopeSet(), this);
}
void IdentityGetAuthTokenFunction::StartGaiaRequest(
@@ -684,8 +708,12 @@ void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog(
const std::string locale = g_browser_process->local_state()->GetString(
prefs::kApplicationLocale);
- gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(
- this, GetProfile(), GetExtension()->id(), oauth2_info, locale));
+ gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(this,
+ GetProfile(),
+ token_key_->account_id,
+ GetExtension()->id(),
+ oauth2_info,
+ locale));
gaia_web_auth_flow_->Start();
}
@@ -707,8 +735,7 @@ OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow(
bool IdentityGetAuthTokenFunction::HasLoginToken() const {
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
- return token_service->RefreshTokenIsAvailable(
- GetPrimaryAccountId(GetProfile()));
+ return token_service->RefreshTokenIsAvailable(token_key_->account_id);
}
std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription(
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h
index 8551c1c..351aad9 100644
--- a/chrome/browser/extensions/api/identity/identity_api.h
+++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -110,6 +110,7 @@ class IdentityAPI : public BrowserContextKeyedAPI,
// Account queries.
std::vector<std::string> GetAccounts() const;
+ std::string FindAccountKeyByGaiaId(const std::string& gaia_id);
// Global error reporting.
void ReportAuthError(const GoogleServiceAuthError& error);
@@ -194,6 +195,26 @@ class IdentityGetAuthTokenFunction : public ChromeAsyncExtensionFunction,
protected:
virtual ~IdentityGetAuthTokenFunction();
+ // IdentitySigninFlow::Delegate implementation:
+ virtual void SigninSuccess() OVERRIDE;
+ virtual void SigninFailed() OVERRIDE;
+
+ // GaiaWebAuthFlow::Delegate implementation:
+ virtual void OnGaiaFlowFailure(GaiaWebAuthFlow::Failure failure,
+ GoogleServiceAuthError service_error,
+ const std::string& oauth_error) OVERRIDE;
+ virtual void OnGaiaFlowCompleted(const std::string& access_token,
+ const std::string& expiration) OVERRIDE;
+
+ // OAuth2TokenService::Consumer implementation:
+ virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) OVERRIDE;
+ virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ scoped_ptr<OAuth2TokenService::Request> login_token_request_;
+
private:
FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest,
ComponentWithChromeClientId);
@@ -228,24 +249,6 @@ class IdentityGetAuthTokenFunction : public ChromeAsyncExtensionFunction,
virtual void OnIssueAdviceSuccess(
const IssueAdviceInfo& issue_advice) OVERRIDE;
- // IdentitySigninFlow::Delegate implementation:
- virtual void SigninSuccess() OVERRIDE;
- virtual void SigninFailed() OVERRIDE;
-
- // GaiaWebAuthFlow::Delegate implementation:
- virtual void OnGaiaFlowFailure(GaiaWebAuthFlow::Failure failure,
- GoogleServiceAuthError service_error,
- const std::string& oauth_error) OVERRIDE;
- virtual void OnGaiaFlowCompleted(const std::string& access_token,
- const std::string& expiration) OVERRIDE;
-
- // OAuth2TokenService::Consumer implementation:
- virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
- const std::string& access_token,
- const base::Time& expiration_time) OVERRIDE;
- virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
- const GoogleServiceAuthError& error) OVERRIDE;
-
// IdentityAPI::ShutdownObserver implementation:
virtual void OnShutdown() OVERRIDE;
@@ -290,7 +293,6 @@ class IdentityGetAuthTokenFunction : public ChromeAsyncExtensionFunction,
IssueAdviceInfo issue_advice_;
scoped_ptr<GaiaWebAuthFlow> gaia_web_auth_flow_;
scoped_ptr<IdentitySigninFlow> signin_flow_;
- scoped_ptr<OAuth2TokenService::Request> login_token_request_;
};
class IdentityRemoveCachedAuthTokenFunction
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index c0eb892..4e1ca7f 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -19,6 +19,10 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/guest_view/guest_view_base.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
+#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
+#include "chrome/browser/signin/fake_signin_manager.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
@@ -347,13 +351,107 @@ class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
bool scope_ui_shown_;
};
+// TODO(courage): Replace MockGetAuthTokenFunction with
+// FakeGetAuthTokenFunction in all tests.
+
+class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
+ public:
+ FakeGetAuthTokenFunction()
+ : login_access_token_result_(true),
+ login_ui_result_(true),
+ scope_ui_result_(true),
+ login_ui_shown_(false),
+ scope_ui_shown_(false) {}
+
+ void set_login_access_token_result(bool result) {
+ login_access_token_result_ = result;
+ }
+
+ void set_login_ui_result(bool result) { login_ui_result_ = result; }
+
+ void set_mint_token_flow(scoped_ptr<OAuth2MintTokenFlow> flow) {
+ flow_ = flow.Pass();
+ }
+
+ void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) {
+ scope_ui_result_ = false;
+ scope_ui_failure_ = failure;
+ }
+
+ void set_scope_ui_oauth_error(const std::string& oauth_error) {
+ scope_ui_result_ = false;
+ scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR;
+ scope_ui_oauth_error_ = oauth_error;
+ }
+
+ bool login_ui_shown() const { return login_ui_shown_; }
+
+ bool scope_ui_shown() const { return scope_ui_shown_; }
+
+ std::string login_access_token() const { return login_access_token_; }
+
+ virtual void ShowLoginPopup() OVERRIDE {
+ EXPECT_FALSE(login_ui_shown_);
+ login_ui_shown_ = true;
+ if (login_ui_result_)
+ SigninSuccess();
+ else
+ SigninFailed();
+ }
+
+ virtual void ShowOAuthApprovalDialog(
+ const IssueAdviceInfo& issue_advice) OVERRIDE {
+ scope_ui_shown_ = true;
+
+ if (scope_ui_result_) {
+ OnGaiaFlowCompleted(kAccessToken, "3600");
+ } else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) {
+ GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
+ OnGaiaFlowFailure(scope_ui_failure_, error, "");
+ } else {
+ GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
+ OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_);
+ }
+ }
+
+ virtual OAuth2MintTokenFlow* CreateMintTokenFlow(
+ const std::string& login_access_token) OVERRIDE {
+ EXPECT_TRUE(login_access_token_.empty());
+ login_access_token_ = login_access_token;
+ return flow_.release();
+ }
+
+ private:
+ virtual ~FakeGetAuthTokenFunction() {}
+ bool login_access_token_result_;
+ bool login_ui_result_;
+ bool scope_ui_result_;
+ GaiaWebAuthFlow::Failure scope_ui_failure_;
+ std::string scope_ui_oauth_error_;
+ bool login_ui_shown_;
+ bool scope_ui_shown_;
+
+ scoped_ptr<OAuth2MintTokenFlow> flow_;
+
+ std::string login_access_token_;
+};
+
class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {
public:
MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
};
+AccountIds CreateIds(std::string email, std::string obfid) {
+ AccountIds ids;
+ ids.account_key = email;
+ ids.email = email;
+ ids.gaia = obfid;
+ return ids;
+}
+
class IdentityGetAccountsFunctionTest : public ExtensionBrowserTest {
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ ExtensionBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kExtensionsMultiAccount);
}
@@ -363,14 +461,6 @@ class IdentityGetAccountsFunctionTest : public ExtensionBrowserTest {
ids, is_signed_in);
}
- AccountIds CreateIds(std::string email, std::string obfid) {
- AccountIds ids;
- ids.account_key = email;
- ids.email = email;
- ids.gaia = obfid;
- return ids;
- }
-
testing::AssertionResult ExpectGetAccounts(
const std::vector<std::string>& accounts) {
scoped_refptr<IdentityGetAccountsFunction> func(
@@ -492,6 +582,60 @@ IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest,
}
class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
+ public:
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ AsyncExtensionBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kExtensionsMultiAccount);
+ }
+
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+ AsyncExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+
+ will_create_browser_context_services_subscription_ =
+ BrowserContextDependencyManager::GetInstance()
+ ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+ base::Bind(&GetAuthTokenFunctionTest::
+ OnWillCreateBrowserContextServices,
+ base::Unretained(this)))
+ .Pass();
+ }
+
+ void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+ // Replace the signin manager and token service with fakes. Do this ahead of
+ // creating the browser so that a bunch of classes don't register as
+ // observers and end up needing to unregister when the fake is substituted.
+ SigninManagerFactory::GetInstance()->SetTestingFactory(
+ context, &FakeSigninManagerBase::Build);
+ ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory(
+ context, &BuildFakeProfileOAuth2TokenService);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ AsyncExtensionBrowserTest::SetUpOnMainThread();
+
+ // Grab references to the fake signin manager and token service.
+ signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
+ SigninManagerFactory::GetInstance()->GetForProfile(profile()));
+ ASSERT_TRUE(signin_manager_);
+ token_service_ = static_cast<FakeProfileOAuth2TokenService*>(
+ ProfileOAuth2TokenServiceFactory::GetInstance()->GetForProfile(
+ profile()));
+ ASSERT_TRUE(token_service_);
+ }
+
+ void SignIn(const std::string account_key) {
+#if defined(OS_CHROMEOS)
+ signin_manager_->SetAuthenticatedUsername(account_key);
+#else
+ signin_manager_->SignIn(account_key, "password");
+#endif
+ }
+
+ void SetAccountState(AccountIds ids, bool is_signed_in) {
+ IdentityAPI::GetFactoryInstance()->Get(profile())->SetAccountStateForTest(
+ ids, is_signed_in);
+ }
+
protected:
enum OAuth2Fields {
NONE = 0,
@@ -500,6 +644,9 @@ class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
AS_COMPONENT = 4
};
+ FakeSigninManagerForTesting* signin_manager_;
+ FakeProfileOAuth2TokenService* token_service_;
+
virtual ~GetAuthTokenFunctionTest() {}
// Helper to create an extension with specific OAuth2Info fields set.
@@ -544,8 +691,10 @@ class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
id_api()->SetCachedToken(key, token_data);
}
- const IdentityTokenCacheValue& GetCachedToken() {
- ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
+ const IdentityTokenCacheValue& GetCachedToken(std::string account_id) {
+ if (account_id.empty())
+ account_id = GetPrimaryAccountId();
+ ExtensionTokenKey key(extension_id_, account_id, oauth_scopes_);
return id_api()->GetCachedToken(key);
}
@@ -564,6 +713,9 @@ class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
private:
std::string extension_id_;
std::set<std::string> oauth_scopes_;
+
+ scoped_ptr<base::CallbackList<void(content::BrowserContext*)>::Subscription>
+ will_create_browser_context_services_subscription_;
};
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
@@ -644,7 +796,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
EXPECT_FALSE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
- GetCachedToken().status());
+ GetCachedToken(std::string()).status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
@@ -707,7 +859,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
- GetCachedToken().status());
+ GetCachedToken(std::string()).status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
@@ -963,7 +1115,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
EXPECT_TRUE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
- GetCachedToken().status());
+ GetCachedToken(std::string()).status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {
@@ -1224,7 +1376,7 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
EXPECT_TRUE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
- GetCachedToken().status());
+ GetCachedToken(std::string()).status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) {
@@ -1246,6 +1398,98 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) {
EXPECT_EQ("client1", func->GetOAuth2ClientId());
}
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiDefaultUser) {
+ SignIn("primary@example.com");
+ token_service_->IssueRefreshTokenForUser("primary@example.com",
+ "refresh_token");
+ SetAccountState(CreateIds("primary@example.com", "1"), true);
+ SetAccountState(CreateIds("secondary@example.com", "2"), true);
+
+ scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+ scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+ func->set_extension(extension.get());
+ func->set_mint_token_flow(
+ scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow(
+ TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get())));
+
+ RunFunctionAsync(func.get(), "[{}]");
+
+ token_service_->IssueAllTokensForAccount(
+ "primary@example.com",
+ "access_token-primary@example.com",
+ base::Time::Now() + base::TimeDelta::FromSeconds(3600));
+
+ scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
+ std::string access_token;
+ EXPECT_TRUE(value->GetAsString(&access_token));
+ EXPECT_EQ(std::string(kAccessToken), access_token);
+ EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
+ GetCachedToken(std::string()).status());
+ EXPECT_EQ("access_token-primary@example.com", func->login_access_token());
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiPrimaryUser) {
+ SignIn("primary@example.com");
+ token_service_->IssueRefreshTokenForUser("primary@example.com",
+ "refresh_token");
+ SetAccountState(CreateIds("primary@example.com", "1"), true);
+ SetAccountState(CreateIds("secondary@example.com", "2"), true);
+
+ scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+ scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+ func->set_extension(extension.get());
+ func->set_mint_token_flow(
+ scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow(
+ TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get())));
+
+ RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"1\" } }]");
+
+ token_service_->IssueAllTokensForAccount(
+ "primary@example.com",
+ "access_token-primary@example.com",
+ base::Time::Now() + base::TimeDelta::FromSeconds(3600));
+
+ scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
+ std::string access_token;
+ EXPECT_TRUE(value->GetAsString(&access_token));
+ EXPECT_EQ(std::string(kAccessToken), access_token);
+ EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
+ GetCachedToken(std::string()).status());
+ EXPECT_EQ("access_token-primary@example.com", func->login_access_token());
+}
+
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryUser) {
+ SignIn("primary@example.com");
+ token_service_->IssueRefreshTokenForUser("secondary@example.com",
+ "refresh_token");
+ SetAccountState(CreateIds("primary@example.com", "1"), true);
+ SetAccountState(CreateIds("secondary@example.com", "2"), true);
+
+ scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+ scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
+ func->set_extension(extension.get());
+ func->set_mint_token_flow(
+ scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow(
+ TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get())));
+
+ RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"2\" } }]");
+
+ token_service_->IssueAllTokensForAccount(
+ "secondary@example.com",
+ "access_token-secondary@example.com",
+ base::Time::Now() + base::TimeDelta::FromSeconds(3600));
+
+ scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
+ std::string access_token;
+ EXPECT_TRUE(value->GetAsString(&access_token));
+ EXPECT_EQ(std::string(kAccessToken), access_token);
+ EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
+ GetCachedToken("secondary@example.com").status());
+ EXPECT_EQ("access_token-secondary@example.com", func->login_access_token());
+}
+
+// TODO(courage): negative cases for secondary accounts
+
class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {
protected:
bool InvalidateDefaultToken() {
@@ -1317,6 +1561,7 @@ IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) {
class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest {
public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ AsyncExtensionBrowserTest::SetUpCommandLine(command_line);
// Reduce performance test variance by disabling background networking.
command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
}
diff --git a/chrome/common/extensions/api/identity.idl b/chrome/common/extensions/api/identity.idl
index 18bd591..b4bbe93 100644
--- a/chrome/common/extensions/api/identity.idl
+++ b/chrome/common/extensions/api/identity.idl
@@ -5,6 +5,12 @@
// Use the <code>chrome.identity</code> API to get OAuth2 access tokens.
namespace identity {
+ dictionary AccountInfo {
+ // A unique identifier for the account. This ID will not change
+ // for the lifetime of the account.
+ DOMString id;
+ };
+
dictionary TokenDetails {
// Fetching a token may require the user to sign-in to Chrome, or
// approve the application's requested scopes. If the interactive
@@ -13,6 +19,13 @@ namespace identity {
// <code>false</code> or omitted, <code>getAuthToken</code> will
// return failure any time a prompt would be required.
boolean? interactive;
+
+ // The account ID whose token should be returned. If not
+ // specified, the primary account for the profile will be used.
+ //
+ // <code>account</code> is only supported when the
+ // "enable-new-profile-management" flag is set.
+ AccountInfo? account;
};
dictionary InvalidTokenDetails {
@@ -39,12 +52,6 @@ namespace identity {
boolean? interactive;
};
- dictionary AccountInfo {
- // A unique identifier for the account. This ID will not change
- // for the lifetime of the account.
- DOMString id;
- };
-
callback GetAuthTokenCallback = void (optional DOMString token);
callback GetAccountsCallback = void (AccountInfo[] accounts);
callback InvalidateAuthTokenCallback = void ();