identity_apitest.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <set>
6#include <string>
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/values.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/extensions/api/identity/identity_api.h"
16#include "chrome/browser/extensions/component_loader.h"
17#include "chrome/browser/extensions/extension_apitest.h"
18#include "chrome/browser/extensions/extension_browsertest.h"
19#include "chrome/browser/extensions/extension_function_test_utils.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/signin/account_reconcilor_factory.h"
23#include "chrome/browser/signin/fake_account_reconcilor.h"
24#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
25#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
26#include "chrome/browser/signin/fake_signin_manager.h"
27#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28#include "chrome/browser/signin/signin_manager_factory.h"
29#include "chrome/browser/ui/browser.h"
30#include "chrome/browser/ui/browser_window.h"
31#include "chrome/common/chrome_switches.h"
32#include "chrome/common/extensions/api/identity.h"
33#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
34#include "chrome/test/base/in_process_browser_test.h"
35#include "chrome/test/base/test_switches.h"
36#include "components/signin/core/browser/signin_manager.h"
37#include "components/signin/core/common/profile_management_switches.h"
38#include "components/signin/core/common/signin_pref_names.h"
39#include "content/public/browser/notification_service.h"
40#include "content/public/browser/notification_source.h"
41#include "content/public/test/test_utils.h"
42#include "extensions/browser/guest_view/guest_view_base.h"
43#include "extensions/common/id_util.h"
44#include "google_apis/gaia/google_service_auth_error.h"
45#include "google_apis/gaia/oauth2_mint_token_flow.h"
46#include "net/test/spawned_test_server/spawned_test_server.h"
47#include "testing/gmock/include/gmock/gmock.h"
48#include "testing/gtest/include/gtest/gtest.h"
49#include "url/gurl.h"
50
51using testing::_;
52using testing::Return;
53using testing::ReturnRef;
54
55namespace extensions {
56
57namespace {
58
59namespace errors = identity_constants;
60namespace utils = extension_function_test_utils;
61
62static const char kAccessToken[] = "auth_token";
63static const char kExtensionId[] = "ext_id";
64
65// This helps us be able to wait until an UIThreadExtensionFunction calls
66// SendResponse.
67class SendResponseDelegate
68    : public UIThreadExtensionFunction::DelegateForTests {
69 public:
70  SendResponseDelegate() : should_post_quit_(false) {}
71
72  virtual ~SendResponseDelegate() {}
73
74  void set_should_post_quit(bool should_quit) {
75    should_post_quit_ = should_quit;
76  }
77
78  bool HasResponse() {
79    return response_.get() != NULL;
80  }
81
82  bool GetResponse() {
83    EXPECT_TRUE(HasResponse());
84    return *response_.get();
85  }
86
87  virtual void OnSendResponse(UIThreadExtensionFunction* function,
88                              bool success,
89                              bool bad_message) OVERRIDE {
90    ASSERT_FALSE(bad_message);
91    ASSERT_FALSE(HasResponse());
92    response_.reset(new bool);
93    *response_ = success;
94    if (should_post_quit_) {
95      base::MessageLoopForUI::current()->Quit();
96    }
97  }
98
99 private:
100  scoped_ptr<bool> response_;
101  bool should_post_quit_;
102};
103
104class AsyncExtensionBrowserTest : public ExtensionBrowserTest {
105 protected:
106  // Asynchronous function runner allows tests to manipulate the browser window
107  // after the call happens.
108  void RunFunctionAsync(
109      UIThreadExtensionFunction* function,
110      const std::string& args) {
111    response_delegate_.reset(new SendResponseDelegate);
112    function->set_test_delegate(response_delegate_.get());
113    scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args));
114    EXPECT_TRUE(parsed_args.get()) <<
115        "Could not parse extension function arguments: " << args;
116    function->SetArgs(parsed_args.get());
117
118    if (!function->extension()) {
119      scoped_refptr<Extension> empty_extension(
120          utils::CreateEmptyExtension());
121      function->set_extension(empty_extension.get());
122    }
123
124    function->set_browser_context(browser()->profile());
125    function->set_has_callback(true);
126    function->Run()->Execute();
127  }
128
129  std::string WaitForError(UIThreadExtensionFunction* function) {
130    RunMessageLoopUntilResponse();
131    EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
132    return function->GetError();
133  }
134
135  base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) {
136    RunMessageLoopUntilResponse();
137    EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
138                                              << function->GetError();
139    const base::Value* single_result = NULL;
140    if (function->GetResultList() != NULL &&
141        function->GetResultList()->Get(0, &single_result)) {
142      return single_result->DeepCopy();
143    }
144    return NULL;
145  }
146
147 private:
148  void RunMessageLoopUntilResponse() {
149    // If the RunAsync of |function| didn't already call SendResponse, run the
150    // message loop until they do.
151    if (!response_delegate_->HasResponse()) {
152      response_delegate_->set_should_post_quit(true);
153      content::RunMessageLoop();
154    }
155    EXPECT_TRUE(response_delegate_->HasResponse());
156  }
157
158  scoped_ptr<SendResponseDelegate> response_delegate_;
159};
160
161class TestHangOAuth2MintTokenFlow : public OAuth2MintTokenFlow {
162 public:
163  TestHangOAuth2MintTokenFlow()
164      : OAuth2MintTokenFlow(NULL, NULL, OAuth2MintTokenFlow::Parameters()) {}
165
166  virtual void Start() OVERRIDE {
167    // Do nothing, simulating a hanging network call.
168  }
169};
170
171class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow {
172 public:
173  enum ResultType {
174    ISSUE_ADVICE_SUCCESS,
175    MINT_TOKEN_SUCCESS,
176    MINT_TOKEN_FAILURE,
177    MINT_TOKEN_BAD_CREDENTIALS,
178    MINT_TOKEN_SERVICE_ERROR
179  };
180
181  TestOAuth2MintTokenFlow(ResultType result,
182                          OAuth2MintTokenFlow::Delegate* delegate)
183    : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()),
184      result_(result),
185      delegate_(delegate) {
186  }
187
188  virtual void Start() OVERRIDE {
189    switch (result_) {
190      case ISSUE_ADVICE_SUCCESS: {
191        IssueAdviceInfo info;
192        delegate_->OnIssueAdviceSuccess(info);
193        break;
194      }
195      case MINT_TOKEN_SUCCESS: {
196        delegate_->OnMintTokenSuccess(kAccessToken, 3600);
197        break;
198      }
199      case MINT_TOKEN_FAILURE: {
200        GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
201        delegate_->OnMintTokenFailure(error);
202        break;
203      }
204      case MINT_TOKEN_BAD_CREDENTIALS: {
205        GoogleServiceAuthError error(
206            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
207        delegate_->OnMintTokenFailure(error);
208        break;
209      }
210      case MINT_TOKEN_SERVICE_ERROR: {
211        GoogleServiceAuthError error =
212            GoogleServiceAuthError::FromServiceError("invalid_scope");
213        delegate_->OnMintTokenFailure(error);
214        break;
215      }
216    }
217  }
218
219 private:
220  ResultType result_;
221  OAuth2MintTokenFlow::Delegate* delegate_;
222};
223
224// Waits for a specific GURL to generate a NOTIFICATION_LOAD_STOP event and
225// saves a pointer to the window embedding the WebContents, which can be later
226// closed.
227class WaitForGURLAndCloseWindow : public content::WindowedNotificationObserver {
228 public:
229  explicit WaitForGURLAndCloseWindow(GURL url)
230      : WindowedNotificationObserver(
231            content::NOTIFICATION_LOAD_STOP,
232            content::NotificationService::AllSources()),
233        url_(url) {}
234
235  // NotificationObserver:
236  virtual void Observe(int type,
237                       const content::NotificationSource& source,
238                       const content::NotificationDetails& details) OVERRIDE {
239    content::NavigationController* web_auth_flow_controller =
240        content::Source<content::NavigationController>(source).ptr();
241    content::WebContents* web_contents =
242        web_auth_flow_controller->GetWebContents();
243
244    if (web_contents->GetURL() == url_) {
245      // It is safe to keep the pointer here, because we know in a test, that
246      // the WebContents won't go away before CloseEmbedderWebContents is
247      // called. Don't copy this code to production.
248      GuestViewBase* guest = GuestViewBase::FromWebContents(web_contents);
249      embedder_web_contents_ = guest->embedder_web_contents();
250      // Condtionally invoke parent class so that Wait will not exit
251      // until the target URL arrives.
252      content::WindowedNotificationObserver::Observe(type, source, details);
253    }
254  }
255
256  // Closes the window embedding the WebContents. The action is separated from
257  // the Observe method to make sure the list of observers is not deleted,
258  // while some event is already being processed. (That causes ASAN failures.)
259  void CloseEmbedderWebContents() {
260    if (embedder_web_contents_)
261      embedder_web_contents_->Close();
262  }
263
264 private:
265  GURL url_;
266  content::WebContents* embedder_web_contents_;
267};
268
269}  // namespace
270
271class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
272 public:
273  FakeGetAuthTokenFunction()
274      : login_access_token_result_(true),
275        auto_login_access_token_(true),
276        login_ui_result_(true),
277        scope_ui_result_(true),
278        login_ui_shown_(false),
279        scope_ui_shown_(false) {}
280
281  void set_login_access_token_result(bool result) {
282    login_access_token_result_ = result;
283  }
284
285  void set_auto_login_access_token(bool automatic) {
286    auto_login_access_token_ = automatic;
287  }
288
289  void set_login_ui_result(bool result) { login_ui_result_ = result; }
290
291  void set_mint_token_flow(scoped_ptr<OAuth2MintTokenFlow> flow) {
292    flow_ = flow.Pass();
293  }
294
295  void set_mint_token_result(TestOAuth2MintTokenFlow::ResultType result_type) {
296    set_mint_token_flow(scoped_ptr<TestOAuth2MintTokenFlow>(
297                            new TestOAuth2MintTokenFlow(result_type, this))
298                            .PassAs<OAuth2MintTokenFlow>());
299  }
300
301  void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) {
302    scope_ui_result_ = false;
303    scope_ui_failure_ = failure;
304  }
305
306  void set_scope_ui_oauth_error(const std::string& oauth_error) {
307    scope_ui_result_ = false;
308    scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR;
309    scope_ui_oauth_error_ = oauth_error;
310  }
311
312  bool login_ui_shown() const { return login_ui_shown_; }
313
314  bool scope_ui_shown() const { return scope_ui_shown_; }
315
316  std::string login_access_token() const { return login_access_token_; }
317
318  virtual void StartLoginAccessTokenRequest() OVERRIDE {
319    if (auto_login_access_token_) {
320      if (login_access_token_result_) {
321        OnGetTokenSuccess(login_token_request_.get(),
322                          "access_token",
323                          base::Time::Now() + base::TimeDelta::FromHours(1LL));
324      } else {
325        GoogleServiceAuthError error(
326            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
327        OnGetTokenFailure(login_token_request_.get(), error);
328      }
329    } else {
330      // Make a request to the token service. The test now must tell
331      // the token service to issue an access token (or an error).
332      IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest();
333    }
334  }
335
336  virtual void ShowLoginPopup() OVERRIDE {
337    EXPECT_FALSE(login_ui_shown_);
338    login_ui_shown_ = true;
339    if (login_ui_result_)
340      SigninSuccess();
341    else
342      SigninFailed();
343  }
344
345  virtual void ShowOAuthApprovalDialog(
346      const IssueAdviceInfo& issue_advice) OVERRIDE {
347    scope_ui_shown_ = true;
348
349    if (scope_ui_result_) {
350      OnGaiaFlowCompleted(kAccessToken, "3600");
351    } else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) {
352      GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
353      OnGaiaFlowFailure(scope_ui_failure_, error, "");
354    } else {
355      GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
356      OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_);
357    }
358  }
359
360  virtual OAuth2MintTokenFlow* CreateMintTokenFlow(
361      const std::string& login_access_token) OVERRIDE {
362    EXPECT_TRUE(login_access_token_.empty());
363    // Save the login token used to create the flow so tests can see
364    // what account was used.
365    login_access_token_ = login_access_token;
366    return flow_.release();
367  }
368
369 private:
370  virtual ~FakeGetAuthTokenFunction() {}
371  bool login_access_token_result_;
372  bool auto_login_access_token_;
373  bool login_ui_result_;
374  bool scope_ui_result_;
375  GaiaWebAuthFlow::Failure scope_ui_failure_;
376  std::string scope_ui_oauth_error_;
377  bool login_ui_shown_;
378  bool scope_ui_shown_;
379
380  scoped_ptr<OAuth2MintTokenFlow> flow_;
381
382  std::string login_access_token_;
383};
384
385class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {
386 public:
387  MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
388};
389
390gaia::AccountIds CreateIds(std::string email, std::string obfid) {
391  gaia::AccountIds ids;
392  ids.account_key = email;
393  ids.email = email;
394  ids.gaia = obfid;
395  return ids;
396}
397
398class IdentityGetAccountsFunctionTest : public ExtensionBrowserTest {
399  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
400    ExtensionBrowserTest::SetUpCommandLine(command_line);
401    command_line->AppendSwitch(switches::kExtensionsMultiAccount);
402  }
403
404 protected:
405  void SetAccountState(gaia::AccountIds ids, bool is_signed_in) {
406    IdentityAPI::GetFactoryInstance()->Get(profile())->SetAccountStateForTest(
407        ids, is_signed_in);
408  }
409
410  testing::AssertionResult ExpectGetAccounts(
411      const std::vector<std::string>& accounts) {
412    scoped_refptr<IdentityGetAccountsFunction> func(
413        new IdentityGetAccountsFunction);
414    func->set_extension(utils::CreateEmptyExtension(kExtensionId).get());
415    if (!utils::RunFunction(
416            func.get(), std::string("[]"), browser(), utils::NONE)) {
417      return GenerateFailureResult(accounts, NULL)
418             << "getAccounts did not return a result.";
419    }
420    const base::ListValue* callback_arguments = func->GetResultList();
421    if (!callback_arguments)
422      return GenerateFailureResult(accounts, NULL) << "NULL result";
423
424    if (callback_arguments->GetSize() != 1) {
425      return GenerateFailureResult(accounts, NULL)
426             << "Expected 1 argument but got " << callback_arguments->GetSize();
427    }
428
429    const base::ListValue* results;
430    if (!callback_arguments->GetList(0, &results))
431      GenerateFailureResult(accounts, NULL) << "Result was not an array";
432
433    std::set<std::string> result_ids;
434    for (base::ListValue::const_iterator it = results->begin();
435         it != results->end();
436         ++it) {
437      scoped_ptr<api::identity::AccountInfo> info =
438          api::identity::AccountInfo::FromValue(**it);
439      if (info.get())
440        result_ids.insert(info->id);
441      else
442        return GenerateFailureResult(accounts, results);
443    }
444
445    for (std::vector<std::string>::const_iterator it = accounts.begin();
446         it != accounts.end();
447         ++it) {
448      if (result_ids.find(*it) == result_ids.end())
449        return GenerateFailureResult(accounts, results);
450    }
451
452    return testing::AssertionResult(true);
453  }
454
455  testing::AssertionResult GenerateFailureResult(
456      const ::std::vector<std::string>& accounts,
457      const base::ListValue* results) {
458    testing::Message msg("Expected: ");
459    for (std::vector<std::string>::const_iterator it = accounts.begin();
460         it != accounts.end();
461         ++it) {
462      msg << *it << " ";
463    }
464    msg << "Actual: ";
465    if (!results) {
466      msg << "NULL";
467    } else {
468      for (base::ListValue::const_iterator it = results->begin();
469           it != results->end();
470           ++it) {
471        scoped_ptr<api::identity::AccountInfo> info =
472            api::identity::AccountInfo::FromValue(**it);
473        if (info.get())
474          msg << info->id << " ";
475        else
476          msg << *it << "<-" << (*it)->GetType() << " ";
477      }
478    }
479
480    return testing::AssertionFailure(msg);
481  }
482};
483
484IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, MultiAccountOn) {
485  EXPECT_TRUE(switches::IsExtensionsMultiAccount());
486}
487
488IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoneSignedIn) {
489  EXPECT_TRUE(ExpectGetAccounts(std::vector<std::string>()));
490}
491
492IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest,
493                       PrimaryAccountSignedIn) {
494  SetAccountState(CreateIds("primary@example.com", "1"), true);
495  std::vector<std::string> primary;
496  primary.push_back("1");
497  EXPECT_TRUE(ExpectGetAccounts(primary));
498}
499
500IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, TwoAccountsSignedIn) {
501  SetAccountState(CreateIds("primary@example.com", "1"), true);
502  SetAccountState(CreateIds("secondary@example.com", "2"), true);
503  std::vector<std::string> two_accounts;
504  two_accounts.push_back("1");
505  two_accounts.push_back("2");
506  EXPECT_TRUE(ExpectGetAccounts(two_accounts));
507}
508
509class IdentityOldProfilesGetAccountsFunctionTest
510    : public IdentityGetAccountsFunctionTest {
511  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
512    // Don't add the multi-account switch that parent class would have.
513  }
514};
515
516IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest,
517                       MultiAccountOff) {
518  EXPECT_FALSE(switches::IsExtensionsMultiAccount());
519}
520
521IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest,
522                       TwoAccountsSignedIn) {
523  SetAccountState(CreateIds("primary@example.com", "1"), true);
524  SetAccountState(CreateIds("secondary@example.com", "2"), true);
525  std::vector<std::string> only_primary;
526  only_primary.push_back("1");
527  EXPECT_TRUE(ExpectGetAccounts(only_primary));
528}
529
530class IdentityGetProfileUserInfoFunctionTest : public ExtensionBrowserTest {
531 protected:
532  scoped_ptr<api::identity::ProfileUserInfo> RunGetProfileUserInfo() {
533    scoped_refptr<IdentityGetProfileUserInfoFunction> func(
534        new IdentityGetProfileUserInfoFunction);
535    func->set_extension(utils::CreateEmptyExtension(kExtensionId).get());
536    scoped_ptr<base::Value> value(
537        utils::RunFunctionAndReturnSingleResult(func.get(), "[]", browser()));
538    return api::identity::ProfileUserInfo::FromValue(*value.get());
539  }
540
541  scoped_ptr<api::identity::ProfileUserInfo> RunGetProfileUserInfoWithEmail() {
542    scoped_refptr<IdentityGetProfileUserInfoFunction> func(
543        new IdentityGetProfileUserInfoFunction);
544    func->set_extension(CreateExtensionWithEmailPermission());
545    scoped_ptr<base::Value> value(
546        utils::RunFunctionAndReturnSingleResult(func.get(), "[]", browser()));
547    return api::identity::ProfileUserInfo::FromValue(*value.get());
548  }
549
550 private:
551  scoped_refptr<Extension> CreateExtensionWithEmailPermission() {
552    scoped_ptr<base::DictionaryValue> test_extension_value(
553        utils::ParseDictionary(
554            "{\"name\": \"Test\", \"version\": \"1.0\", "
555            "\"permissions\": [\"identity.email\"]}"));
556    return utils::CreateExtension(test_extension_value.get());
557  }
558};
559
560IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest, NotSignedIn) {
561  scoped_ptr<api::identity::ProfileUserInfo> info =
562      RunGetProfileUserInfoWithEmail();
563  EXPECT_TRUE(info->email.empty());
564  EXPECT_TRUE(info->id.empty());
565}
566
567IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest, SignedIn) {
568  profile()->GetPrefs()
569      ->SetString(prefs::kGoogleServicesUsername, "president@example.com");
570  profile()->GetPrefs()
571      ->SetString(prefs::kGoogleServicesUserAccountId, "12345");
572
573  scoped_ptr<api::identity::ProfileUserInfo> info =
574      RunGetProfileUserInfoWithEmail();
575  EXPECT_EQ("president@example.com", info->email);
576  EXPECT_EQ("12345", info->id);
577}
578
579IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest,
580                       NotSignedInNoEmail) {
581  scoped_ptr<api::identity::ProfileUserInfo> info = RunGetProfileUserInfo();
582  EXPECT_TRUE(info->email.empty());
583  EXPECT_TRUE(info->id.empty());
584}
585
586IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest,
587                       SignedInNoEmail) {
588  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
589                                   "president@example.com");
590  profile()->GetPrefs()->SetString(prefs::kGoogleServicesUserAccountId,
591                                   "12345");
592
593  scoped_ptr<api::identity::ProfileUserInfo> info = RunGetProfileUserInfo();
594  EXPECT_TRUE(info->email.empty());
595  EXPECT_EQ("12345", info->id);
596}
597
598class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
599 public:
600  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
601    AsyncExtensionBrowserTest::SetUpCommandLine(command_line);
602    command_line->AppendSwitch(switches::kExtensionsMultiAccount);
603  }
604
605  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
606    AsyncExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
607
608    will_create_browser_context_services_subscription_ =
609        BrowserContextDependencyManager::GetInstance()
610            ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
611                  base::Bind(&GetAuthTokenFunctionTest::
612                                 OnWillCreateBrowserContextServices,
613                             base::Unretained(this)))
614            .Pass();
615  }
616
617  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
618    // Replace the signin manager and token service with fakes. Do this ahead of
619    // creating the browser so that a bunch of classes don't register as
620    // observers and end up needing to unregister when the fake is substituted.
621    SigninManagerFactory::GetInstance()->SetTestingFactory(
622        context, &FakeSigninManagerBase::Build);
623    ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory(
624        context, &BuildFakeProfileOAuth2TokenService);
625    AccountReconcilorFactory::GetInstance()->SetTestingFactory(
626        context, &FakeAccountReconcilor::Build);
627  }
628
629  virtual void SetUpOnMainThread() OVERRIDE {
630    AsyncExtensionBrowserTest::SetUpOnMainThread();
631
632    // Grab references to the fake signin manager and token service.
633    signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
634        SigninManagerFactory::GetInstance()->GetForProfile(profile()));
635    ASSERT_TRUE(signin_manager_);
636    token_service_ = static_cast<FakeProfileOAuth2TokenService*>(
637        ProfileOAuth2TokenServiceFactory::GetInstance()->GetForProfile(
638            profile()));
639    ASSERT_TRUE(token_service_);
640  }
641
642  void SignIn(const std::string account_key) {
643#if defined(OS_CHROMEOS)
644    signin_manager_->SetAuthenticatedUsername(account_key);
645#else
646    signin_manager_->SignIn(account_key, "password");
647#endif
648    token_service_->IssueRefreshTokenForUser(account_key, "refresh_token");
649  }
650
651  void IssueLoginRefreshTokenForAccount(const std::string account_key) {
652    token_service_->IssueRefreshTokenForUser(account_key, "refresh_token");
653  }
654
655  void IssueLoginAccessTokenForAccount(const std::string account_key) {
656    token_service_->IssueAllTokensForAccount(
657        account_key,
658        "access_token-" + account_key,
659        base::Time::Now() + base::TimeDelta::FromSeconds(3600));
660  }
661
662  void SetAccountState(gaia::AccountIds ids, bool is_signed_in) {
663    IdentityAPI::GetFactoryInstance()->Get(profile())->SetAccountStateForTest(
664        ids, is_signed_in);
665  }
666
667 protected:
668  enum OAuth2Fields {
669    NONE = 0,
670    CLIENT_ID = 1,
671    SCOPES = 2,
672    AS_COMPONENT = 4
673  };
674
675  FakeSigninManagerForTesting* signin_manager_;
676  FakeProfileOAuth2TokenService* token_service_;
677
678  virtual ~GetAuthTokenFunctionTest() {}
679
680  // Helper to create an extension with specific OAuth2Info fields set.
681  // |fields_to_set| should be computed by using fields of Oauth2Fields enum.
682  const Extension* CreateExtension(int fields_to_set) {
683    const Extension* ext;
684    base::FilePath manifest_path =
685        test_data_dir_.AppendASCII("platform_apps/oauth2");
686    base::FilePath component_manifest_path =
687        test_data_dir_.AppendASCII("packaged_app/component_oauth2");
688    if ((fields_to_set & AS_COMPONENT) == 0)
689      ext = LoadExtension(manifest_path);
690    else
691      ext = LoadExtensionAsComponent(component_manifest_path);
692    OAuth2Info& oauth2_info =
693        const_cast<OAuth2Info&>(OAuth2Info::GetOAuth2Info(ext));
694    if ((fields_to_set & CLIENT_ID) != 0)
695      oauth2_info.client_id = "client1";
696    if ((fields_to_set & SCOPES) != 0) {
697      oauth2_info.scopes.push_back("scope1");
698      oauth2_info.scopes.push_back("scope2");
699    }
700
701    extension_id_ = ext->id();
702    oauth_scopes_ = std::set<std::string>(oauth2_info.scopes.begin(),
703                                          oauth2_info.scopes.end());
704    return ext;
705  }
706
707  IdentityAPI* id_api() {
708    return IdentityAPI::GetFactoryInstance()->Get(browser()->profile());
709  }
710
711  const std::string GetPrimaryAccountId() {
712    SigninManagerBase* signin_manager =
713        SigninManagerFactory::GetForProfile(browser()->profile());
714    return signin_manager->GetAuthenticatedAccountId();
715  }
716
717  void SetCachedToken(const IdentityTokenCacheValue& token_data) {
718    ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
719    id_api()->SetCachedToken(key, token_data);
720  }
721
722  const IdentityTokenCacheValue& GetCachedToken(std::string account_id) {
723    if (account_id.empty())
724      account_id = GetPrimaryAccountId();
725    ExtensionTokenKey key(extension_id_, account_id, oauth_scopes_);
726    return id_api()->GetCachedToken(key);
727  }
728
729  void QueueRequestStart(IdentityMintRequestQueue::MintType type,
730                         IdentityMintRequestQueue::Request* request) {
731    ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
732    id_api()->mint_queue()->RequestStart(type, key, request);
733  }
734
735  void QueueRequestComplete(IdentityMintRequestQueue::MintType type,
736                            IdentityMintRequestQueue::Request* request) {
737    ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
738    id_api()->mint_queue()->RequestComplete(type, key, request);
739  }
740
741 private:
742  std::string extension_id_;
743  std::set<std::string> oauth_scopes_;
744
745  scoped_ptr<base::CallbackList<void(content::BrowserContext*)>::Subscription>
746      will_create_browser_context_services_subscription_;
747};
748
749IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
750                       NoClientId) {
751  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
752  func->set_extension(CreateExtension(SCOPES));
753  std::string error = utils::RunFunctionAndReturnError(
754      func.get(), "[{}]", browser());
755  EXPECT_EQ(std::string(errors::kInvalidClientId), error);
756  EXPECT_FALSE(func->login_ui_shown());
757  EXPECT_FALSE(func->scope_ui_shown());
758}
759
760IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
761                       NoScopes) {
762  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
763  func->set_extension(CreateExtension(CLIENT_ID));
764  std::string error = utils::RunFunctionAndReturnError(
765      func.get(), "[{}]", browser());
766  EXPECT_EQ(std::string(errors::kInvalidScopes), error);
767  EXPECT_FALSE(func->login_ui_shown());
768  EXPECT_FALSE(func->scope_ui_shown());
769}
770
771IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
772                       NonInteractiveNotSignedIn) {
773  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
774  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
775  std::string error = utils::RunFunctionAndReturnError(
776      func.get(), "[{}]", browser());
777  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
778  EXPECT_FALSE(func->login_ui_shown());
779  EXPECT_FALSE(func->scope_ui_shown());
780}
781
782IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
783                       NonInteractiveMintFailure) {
784  SignIn("primary@example.com");
785  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
786  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
787  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE);
788  std::string error =
789      utils::RunFunctionAndReturnError(func.get(), "[{}]", browser());
790  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
791  EXPECT_FALSE(func->login_ui_shown());
792  EXPECT_FALSE(func->scope_ui_shown());
793}
794
795IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
796                       NonInteractiveLoginAccessTokenFailure) {
797  SignIn("primary@example.com");
798  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
799  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
800  func->set_login_access_token_result(false);
801  std::string error = utils::RunFunctionAndReturnError(
802      func.get(), "[{}]", browser());
803  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
804}
805
806IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
807                       NonInteractiveMintAdviceSuccess) {
808  SignIn("primary@example.com");
809  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
810  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
811  func->set_extension(extension.get());
812  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
813  std::string error =
814      utils::RunFunctionAndReturnError(func.get(), "[{}]", browser());
815  EXPECT_EQ(std::string(errors::kNoGrant), error);
816  EXPECT_FALSE(func->login_ui_shown());
817  EXPECT_FALSE(func->scope_ui_shown());
818
819  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
820            GetCachedToken(std::string()).status());
821}
822
823IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
824                       NonInteractiveMintBadCredentials) {
825  SignIn("primary@example.com");
826  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
827  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
828  func->set_mint_token_result(
829      TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS);
830  std::string error =
831      utils::RunFunctionAndReturnError(func.get(), "[{}]", browser());
832  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
833  EXPECT_FALSE(func->login_ui_shown());
834  EXPECT_FALSE(func->scope_ui_shown());
835}
836
837IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
838                       NonInteractiveMintServiceError) {
839  SignIn("primary@example.com");
840  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
841  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
842  func->set_mint_token_result(
843      TestOAuth2MintTokenFlow::MINT_TOKEN_SERVICE_ERROR);
844  std::string error =
845      utils::RunFunctionAndReturnError(func.get(), "[{}]", browser());
846  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
847  EXPECT_FALSE(func->login_ui_shown());
848  EXPECT_FALSE(func->scope_ui_shown());
849}
850
851IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
852                       NoOptionsSuccess) {
853  SignIn("primary@example.com");
854#if defined(OS_WIN) && defined(USE_ASH)
855  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
856  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
857    return;
858#endif
859
860  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
861  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
862  func->set_extension(extension.get());
863  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
864  scoped_ptr<base::Value> value(
865      utils::RunFunctionAndReturnSingleResult(func.get(), "[]", browser()));
866  std::string access_token;
867  EXPECT_TRUE(value->GetAsString(&access_token));
868  EXPECT_EQ(std::string(kAccessToken), access_token);
869  EXPECT_FALSE(func->login_ui_shown());
870  EXPECT_FALSE(func->scope_ui_shown());
871  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
872            GetCachedToken(std::string()).status());
873}
874
875IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
876                       NonInteractiveSuccess) {
877  SignIn("primary@example.com");
878#if defined(OS_WIN) && defined(USE_ASH)
879  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
880  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
881    return;
882#endif
883
884  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
885  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
886  func->set_extension(extension.get());
887  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
888  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
889      func.get(), "[{}]", browser()));
890  std::string access_token;
891  EXPECT_TRUE(value->GetAsString(&access_token));
892  EXPECT_EQ(std::string(kAccessToken), access_token);
893  EXPECT_FALSE(func->login_ui_shown());
894  EXPECT_FALSE(func->scope_ui_shown());
895  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
896            GetCachedToken(std::string()).status());
897}
898
899IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
900                       InteractiveLoginCanceled) {
901  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
902  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
903  func->set_login_ui_result(false);
904  std::string error = utils::RunFunctionAndReturnError(
905      func.get(), "[{\"interactive\": true}]", browser());
906  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
907  EXPECT_TRUE(func->login_ui_shown());
908  EXPECT_FALSE(func->scope_ui_shown());
909}
910
911IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
912                       InteractiveMintBadCredentialsLoginCanceled) {
913  SignIn("primary@example.com");
914  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
915  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
916  func->set_mint_token_result(
917      TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS);
918  func->set_login_ui_result(false);
919  std::string error = utils::RunFunctionAndReturnError(
920      func.get(), "[{\"interactive\": true}]", browser());
921  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
922  EXPECT_TRUE(func->login_ui_shown());
923  EXPECT_FALSE(func->scope_ui_shown());
924}
925
926IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
927                       InteractiveLoginSuccessNoToken) {
928  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
929  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
930  func->set_login_ui_result(false);
931  std::string error = utils::RunFunctionAndReturnError(
932      func.get(), "[{\"interactive\": true}]", browser());
933  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
934  EXPECT_TRUE(func->login_ui_shown());
935  EXPECT_FALSE(func->scope_ui_shown());
936}
937
938IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
939                       InteractiveLoginSuccessMintFailure) {
940  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
941  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
942  func->set_login_ui_result(true);
943  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE);
944  std::string error = utils::RunFunctionAndReturnError(
945      func.get(), "[{\"interactive\": true}]", browser());
946  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
947  EXPECT_TRUE(func->login_ui_shown());
948  EXPECT_FALSE(func->scope_ui_shown());
949}
950
951IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
952                       InteractiveLoginSuccessLoginAccessTokenFailure) {
953  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
954  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
955  func->set_login_ui_result(true);
956  func->set_login_access_token_result(false);
957  std::string error = utils::RunFunctionAndReturnError(
958      func.get(), "[{\"interactive\": true}]", browser());
959  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
960  EXPECT_TRUE(func->login_ui_shown());
961  EXPECT_FALSE(func->scope_ui_shown());
962}
963
964IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
965                       InteractiveLoginSuccessMintSuccess) {
966  // TODO(courage): verify that account_id in token service requests
967  // is correct once manual token minting for tests is implemented.
968  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
969  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
970  func->set_login_ui_result(true);
971  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
972  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
973      func.get(), "[{\"interactive\": true}]", browser()));
974  std::string access_token;
975  EXPECT_TRUE(value->GetAsString(&access_token));
976  EXPECT_EQ(std::string(kAccessToken), access_token);
977  EXPECT_TRUE(func->login_ui_shown());
978  EXPECT_FALSE(func->scope_ui_shown());
979}
980
981IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
982                       InteractiveLoginSuccessApprovalAborted) {
983  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
984  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
985  func->set_login_ui_result(true);
986  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
987  func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED);
988  std::string error = utils::RunFunctionAndReturnError(
989      func.get(), "[{\"interactive\": true}]", browser());
990  EXPECT_EQ(std::string(errors::kUserRejected), error);
991  EXPECT_TRUE(func->login_ui_shown());
992  EXPECT_TRUE(func->scope_ui_shown());
993}
994
995IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
996                       InteractiveLoginSuccessApprovalSuccess) {
997  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
998  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
999  func->set_extension(extension.get());
1000  func->set_login_ui_result(true);
1001  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1002
1003  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1004      func.get(), "[{\"interactive\": true}]", browser()));
1005  std::string access_token;
1006  EXPECT_TRUE(value->GetAsString(&access_token));
1007  EXPECT_EQ(std::string(kAccessToken), access_token);
1008  EXPECT_TRUE(func->login_ui_shown());
1009  EXPECT_TRUE(func->scope_ui_shown());
1010}
1011
1012IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1013                       InteractiveApprovalAborted) {
1014  SignIn("primary@example.com");
1015  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1016  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1017  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1018  func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED);
1019  std::string error = utils::RunFunctionAndReturnError(
1020      func.get(), "[{\"interactive\": true}]", browser());
1021  EXPECT_EQ(std::string(errors::kUserRejected), error);
1022  EXPECT_FALSE(func->login_ui_shown());
1023  EXPECT_TRUE(func->scope_ui_shown());
1024}
1025
1026IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1027                       InteractiveApprovalLoadFailed) {
1028  SignIn("primary@example.com");
1029  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1030  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1031  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1032  func->set_scope_ui_failure(GaiaWebAuthFlow::LOAD_FAILED);
1033  std::string error = utils::RunFunctionAndReturnError(
1034      func.get(), "[{\"interactive\": true}]", browser());
1035  EXPECT_EQ(std::string(errors::kPageLoadFailure), error);
1036  EXPECT_FALSE(func->login_ui_shown());
1037  EXPECT_TRUE(func->scope_ui_shown());
1038}
1039
1040IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1041                       InteractiveApprovalInvalidRedirect) {
1042  SignIn("primary@example.com");
1043  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1044  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1045  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1046  func->set_scope_ui_failure(GaiaWebAuthFlow::INVALID_REDIRECT);
1047  std::string error = utils::RunFunctionAndReturnError(
1048      func.get(), "[{\"interactive\": true}]", browser());
1049  EXPECT_EQ(std::string(errors::kInvalidRedirect), error);
1050  EXPECT_FALSE(func->login_ui_shown());
1051  EXPECT_TRUE(func->scope_ui_shown());
1052}
1053
1054IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1055                       InteractiveApprovalConnectionFailure) {
1056  SignIn("primary@example.com");
1057  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1058  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1059  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1060  func->set_scope_ui_failure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR);
1061  std::string error = utils::RunFunctionAndReturnError(
1062      func.get(), "[{\"interactive\": true}]", browser());
1063  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
1064  EXPECT_FALSE(func->login_ui_shown());
1065  EXPECT_TRUE(func->scope_ui_shown());
1066}
1067
1068IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1069                       InteractiveApprovalOAuthErrors) {
1070  SignIn("primary@example.com");
1071  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1072
1073  std::map<std::string, std::string> error_map;
1074  error_map.insert(std::make_pair("access_denied", errors::kUserRejected));
1075  error_map.insert(std::make_pair("invalid_scope", errors::kInvalidScopes));
1076  error_map.insert(std::make_pair(
1077      "unmapped_error", std::string(errors::kAuthFailure) + "unmapped_error"));
1078
1079  for (std::map<std::string, std::string>::const_iterator
1080           it = error_map.begin();
1081       it != error_map.end();
1082       ++it) {
1083    scoped_refptr<FakeGetAuthTokenFunction> func(
1084        new FakeGetAuthTokenFunction());
1085    func->set_extension(extension.get());
1086    // Make sure we don't get a cached issue_advice result, which would cause
1087    // flow to be leaked.
1088    id_api()->EraseAllCachedTokens();
1089    func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1090    func->set_scope_ui_oauth_error(it->first);
1091    std::string error = utils::RunFunctionAndReturnError(
1092        func.get(), "[{\"interactive\": true}]", browser());
1093    EXPECT_EQ(it->second, error);
1094    EXPECT_FALSE(func->login_ui_shown());
1095    EXPECT_TRUE(func->scope_ui_shown());
1096  }
1097}
1098
1099IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1100                       InteractiveApprovalSuccess) {
1101  SignIn("primary@example.com");
1102  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1103  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1104  func->set_extension(extension.get());
1105  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1106
1107  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1108      func.get(), "[{\"interactive\": true}]", browser()));
1109  std::string access_token;
1110  EXPECT_TRUE(value->GetAsString(&access_token));
1111  EXPECT_EQ(std::string(kAccessToken), access_token);
1112  EXPECT_FALSE(func->login_ui_shown());
1113  EXPECT_TRUE(func->scope_ui_shown());
1114
1115  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1116            GetCachedToken(std::string()).status());
1117}
1118
1119IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {
1120  SignIn("primary@example.com");
1121  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1122  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1123  func->set_extension(extension.get());
1124
1125  // Create a fake request to block the queue.
1126  MockQueuedMintRequest queued_request;
1127  IdentityMintRequestQueue::MintType type =
1128      IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
1129
1130  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
1131  QueueRequestStart(type, &queued_request);
1132
1133  // The real request will start processing, but wait in the queue behind
1134  // the blocker.
1135  RunFunctionAsync(func.get(), "[{}]");
1136  // Verify that we have fetched the login token at this point.
1137  testing::Mock::VerifyAndClearExpectations(func.get());
1138
1139  // The flow will be created after the first queued request clears.
1140  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1141
1142  QueueRequestComplete(type, &queued_request);
1143
1144  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1145  std::string access_token;
1146  EXPECT_TRUE(value->GetAsString(&access_token));
1147  EXPECT_EQ(std::string(kAccessToken), access_token);
1148  EXPECT_FALSE(func->login_ui_shown());
1149  EXPECT_FALSE(func->scope_ui_shown());
1150}
1151
1152IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) {
1153  SignIn("primary@example.com");
1154  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1155  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1156  func->set_extension(extension.get());
1157
1158  // Create a fake request to block the queue.
1159  MockQueuedMintRequest queued_request;
1160  IdentityMintRequestQueue::MintType type =
1161      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
1162
1163  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
1164  QueueRequestStart(type, &queued_request);
1165
1166  // The real request will start processing, but wait in the queue behind
1167  // the blocker.
1168  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1169  RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
1170  // Verify that we have fetched the login token and run the first flow.
1171  testing::Mock::VerifyAndClearExpectations(func.get());
1172  EXPECT_FALSE(func->scope_ui_shown());
1173
1174  // The UI will be displayed and a token retrieved after the first
1175  // queued request clears.
1176  QueueRequestComplete(type, &queued_request);
1177
1178  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1179  std::string access_token;
1180  EXPECT_TRUE(value->GetAsString(&access_token));
1181  EXPECT_EQ(std::string(kAccessToken), access_token);
1182  EXPECT_FALSE(func->login_ui_shown());
1183  EXPECT_TRUE(func->scope_ui_shown());
1184}
1185
1186IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueueShutdown) {
1187  SignIn("primary@example.com");
1188  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1189  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1190  func->set_extension(extension.get());
1191
1192  // Create a fake request to block the queue.
1193  MockQueuedMintRequest queued_request;
1194  IdentityMintRequestQueue::MintType type =
1195      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
1196
1197  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
1198  QueueRequestStart(type, &queued_request);
1199
1200  // The real request will start processing, but wait in the queue behind
1201  // the blocker.
1202  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1203  RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
1204  // Verify that we have fetched the login token and run the first flow.
1205  testing::Mock::VerifyAndClearExpectations(func.get());
1206  EXPECT_FALSE(func->scope_ui_shown());
1207
1208  // After the request is canceled, the function will complete.
1209  func->OnShutdown();
1210  EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get()));
1211  EXPECT_FALSE(func->login_ui_shown());
1212  EXPECT_FALSE(func->scope_ui_shown());
1213
1214  QueueRequestComplete(type, &queued_request);
1215}
1216
1217IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) {
1218  SignIn("primary@example.com");
1219  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1220  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1221  func->set_extension(extension.get());
1222
1223  scoped_ptr<TestHangOAuth2MintTokenFlow> flow(
1224      new TestHangOAuth2MintTokenFlow());
1225  func->set_mint_token_flow(flow.PassAs<OAuth2MintTokenFlow>());
1226  RunFunctionAsync(func.get(), "[{\"interactive\": false}]");
1227
1228  // After the request is canceled, the function will complete.
1229  func->OnShutdown();
1230  EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get()));
1231}
1232
1233IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1234                       InteractiveQueuedNoninteractiveFails) {
1235  SignIn("primary@example.com");
1236  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1237  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1238  func->set_extension(extension.get());
1239
1240  // Create a fake request to block the interactive queue.
1241  MockQueuedMintRequest queued_request;
1242  IdentityMintRequestQueue::MintType type =
1243      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
1244
1245  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
1246  QueueRequestStart(type, &queued_request);
1247
1248  // Non-interactive requests fail without hitting GAIA, because a
1249  // consent UI is known to be up.
1250  std::string error = utils::RunFunctionAndReturnError(
1251      func.get(), "[{}]", browser());
1252  EXPECT_EQ(std::string(errors::kNoGrant), error);
1253  EXPECT_FALSE(func->login_ui_shown());
1254  EXPECT_FALSE(func->scope_ui_shown());
1255
1256  QueueRequestComplete(type, &queued_request);
1257}
1258
1259IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1260                       NonInteractiveCacheHit) {
1261  SignIn("primary@example.com");
1262  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1263  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1264  func->set_extension(extension.get());
1265
1266  // pre-populate the cache with a token
1267  IdentityTokenCacheValue token(kAccessToken,
1268                                base::TimeDelta::FromSeconds(3600));
1269  SetCachedToken(token);
1270
1271  // Get a token. Should not require a GAIA request.
1272  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1273      func.get(), "[{}]", browser()));
1274  std::string access_token;
1275  EXPECT_TRUE(value->GetAsString(&access_token));
1276  EXPECT_EQ(std::string(kAccessToken), access_token);
1277  EXPECT_FALSE(func->login_ui_shown());
1278  EXPECT_FALSE(func->scope_ui_shown());
1279}
1280
1281IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1282                       NonInteractiveIssueAdviceCacheHit) {
1283  SignIn("primary@example.com");
1284  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1285  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1286  func->set_extension(extension.get());
1287
1288  // pre-populate the cache with advice
1289  IssueAdviceInfo info;
1290  IdentityTokenCacheValue token(info);
1291  SetCachedToken(token);
1292
1293  // Should return an error without a GAIA request.
1294  std::string error = utils::RunFunctionAndReturnError(
1295      func.get(), "[{}]", browser());
1296  EXPECT_EQ(std::string(errors::kNoGrant), error);
1297  EXPECT_FALSE(func->login_ui_shown());
1298  EXPECT_FALSE(func->scope_ui_shown());
1299}
1300
1301IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1302                       InteractiveCacheHit) {
1303  SignIn("primary@example.com");
1304  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1305  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1306  func->set_extension(extension.get());
1307
1308  // Create a fake request to block the queue.
1309  MockQueuedMintRequest queued_request;
1310  IdentityMintRequestQueue::MintType type =
1311      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
1312
1313  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
1314  QueueRequestStart(type, &queued_request);
1315
1316  // The real request will start processing, but wait in the queue behind
1317  // the blocker.
1318  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1319  RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
1320
1321  // Populate the cache with a token while the request is blocked.
1322  IdentityTokenCacheValue token(kAccessToken,
1323                                base::TimeDelta::FromSeconds(3600));
1324  SetCachedToken(token);
1325
1326  // When we wake up the request, it returns the cached token without
1327  // displaying a UI, or hitting GAIA.
1328
1329  QueueRequestComplete(type, &queued_request);
1330
1331  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1332  std::string access_token;
1333  EXPECT_TRUE(value->GetAsString(&access_token));
1334  EXPECT_EQ(std::string(kAccessToken), access_token);
1335  EXPECT_FALSE(func->login_ui_shown());
1336  EXPECT_FALSE(func->scope_ui_shown());
1337}
1338
1339IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1340                       LoginInvalidatesTokenCache) {
1341  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1342  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1343  func->set_extension(extension.get());
1344
1345  // pre-populate the cache with a token
1346  IdentityTokenCacheValue token(kAccessToken,
1347                                base::TimeDelta::FromSeconds(3600));
1348  SetCachedToken(token);
1349
1350  // Because the user is not signed in, the token will be removed,
1351  // and we'll hit GAIA for new tokens.
1352  func->set_login_ui_result(true);
1353  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1354
1355  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1356      func.get(), "[{\"interactive\": true}]", browser()));
1357  std::string access_token;
1358  EXPECT_TRUE(value->GetAsString(&access_token));
1359  EXPECT_EQ(std::string(kAccessToken), access_token);
1360  EXPECT_TRUE(func->login_ui_shown());
1361  EXPECT_TRUE(func->scope_ui_shown());
1362  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1363            GetCachedToken(std::string()).status());
1364}
1365
1366IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) {
1367  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1368  scoped_refptr<const Extension> extension(
1369      CreateExtension(SCOPES | AS_COMPONENT));
1370  func->set_extension(extension.get());
1371  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get());
1372  EXPECT_TRUE(oauth2_info.client_id.empty());
1373  EXPECT_FALSE(func->GetOAuth2ClientId().empty());
1374  EXPECT_NE("client1", func->GetOAuth2ClientId());
1375}
1376
1377IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) {
1378  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1379  scoped_refptr<const Extension> extension(
1380      CreateExtension(CLIENT_ID | SCOPES | AS_COMPONENT));
1381  func->set_extension(extension.get());
1382  EXPECT_EQ("client1", func->GetOAuth2ClientId());
1383}
1384
1385IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiDefaultUser) {
1386  SignIn("primary@example.com");
1387  SetAccountState(CreateIds("primary@example.com", "1"), true);
1388  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1389
1390  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1391  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1392  func->set_extension(extension.get());
1393  func->set_auto_login_access_token(false);
1394  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1395
1396  RunFunctionAsync(func.get(), "[{}]");
1397
1398  IssueLoginAccessTokenForAccount("primary@example.com");
1399
1400  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1401  std::string access_token;
1402  EXPECT_TRUE(value->GetAsString(&access_token));
1403  EXPECT_EQ(std::string(kAccessToken), access_token);
1404  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1405            GetCachedToken(std::string()).status());
1406  EXPECT_EQ("access_token-primary@example.com", func->login_access_token());
1407}
1408
1409IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiPrimaryUser) {
1410  SignIn("primary@example.com");
1411  IssueLoginRefreshTokenForAccount("secondary@example.com");
1412  SetAccountState(CreateIds("primary@example.com", "1"), true);
1413  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1414
1415  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1416  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1417  func->set_extension(extension.get());
1418  func->set_auto_login_access_token(false);
1419  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1420
1421  RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"1\" } }]");
1422
1423  IssueLoginAccessTokenForAccount("primary@example.com");
1424
1425  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1426  std::string access_token;
1427  EXPECT_TRUE(value->GetAsString(&access_token));
1428  EXPECT_EQ(std::string(kAccessToken), access_token);
1429  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1430            GetCachedToken(std::string()).status());
1431  EXPECT_EQ("access_token-primary@example.com", func->login_access_token());
1432}
1433
1434IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryUser) {
1435  SignIn("primary@example.com");
1436  IssueLoginRefreshTokenForAccount("secondary@example.com");
1437  SetAccountState(CreateIds("primary@example.com", "1"), true);
1438  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1439
1440  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1441  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1442  func->set_extension(extension.get());
1443  func->set_auto_login_access_token(false);
1444  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1445
1446  RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"2\" } }]");
1447
1448  IssueLoginAccessTokenForAccount("secondary@example.com");
1449
1450  scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
1451  std::string access_token;
1452  EXPECT_TRUE(value->GetAsString(&access_token));
1453  EXPECT_EQ(std::string(kAccessToken), access_token);
1454  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1455            GetCachedToken("secondary@example.com").status());
1456  EXPECT_EQ("access_token-secondary@example.com", func->login_access_token());
1457}
1458
1459IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiUnknownUser) {
1460  SignIn("primary@example.com");
1461  IssueLoginRefreshTokenForAccount("secondary@example.com");
1462  SetAccountState(CreateIds("primary@example.com", "1"), true);
1463  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1464
1465  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1466  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1467  func->set_extension(extension.get());
1468  func->set_auto_login_access_token(false);
1469
1470  std::string error = utils::RunFunctionAndReturnError(
1471      func.get(), "[{\"account\": { \"id\": \"3\" } }]", browser());
1472  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
1473}
1474
1475IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1476                       MultiSecondaryNonInteractiveMintFailure) {
1477  SignIn("primary@example.com");
1478  IssueLoginRefreshTokenForAccount("secondary@example.com");
1479  SetAccountState(CreateIds("primary@example.com", "1"), true);
1480  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1481
1482  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1483  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1484  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE);
1485  std::string error = utils::RunFunctionAndReturnError(
1486      func.get(), "[{\"account\": { \"id\": \"2\" } }]", browser());
1487  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
1488  EXPECT_FALSE(func->login_ui_shown());
1489  EXPECT_FALSE(func->scope_ui_shown());
1490}
1491
1492IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1493                       MultiSecondaryNonInteractiveLoginAccessTokenFailure) {
1494  SignIn("primary@example.com");
1495  IssueLoginRefreshTokenForAccount("secondary@example.com");
1496  SetAccountState(CreateIds("primary@example.com", "1"), true);
1497  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1498
1499  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1500  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1501  func->set_login_access_token_result(false);
1502  std::string error = utils::RunFunctionAndReturnError(
1503      func.get(), "[{\"account\": { \"id\": \"2\" } }]", browser());
1504  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
1505}
1506
1507IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
1508                       MultiSecondaryInteractiveApprovalAborted) {
1509  SignIn("primary@example.com");
1510  IssueLoginRefreshTokenForAccount("secondary@example.com");
1511  SetAccountState(CreateIds("primary@example.com", "1"), true);
1512  SetAccountState(CreateIds("secondary@example.com", "2"), true);
1513
1514  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1515  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
1516  func->set_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS);
1517  func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED);
1518  std::string error = utils::RunFunctionAndReturnError(
1519      func.get(),
1520      "[{\"account\": { \"id\": \"2\" }, \"interactive\": true}]",
1521      browser());
1522  EXPECT_EQ(std::string(errors::kUserRejected), error);
1523  EXPECT_FALSE(func->login_ui_shown());
1524  EXPECT_TRUE(func->scope_ui_shown());
1525}
1526
1527IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesDefault) {
1528  SignIn("primary@example.com");
1529  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1530  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1531  func->set_extension(extension.get());
1532  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1533  scoped_ptr<base::Value> value(
1534      utils::RunFunctionAndReturnSingleResult(func.get(), "[{}]", browser()));
1535  std::string access_token;
1536  EXPECT_TRUE(value->GetAsString(&access_token));
1537  EXPECT_EQ(std::string(kAccessToken), access_token);
1538
1539  const ExtensionTokenKey* token_key = func->GetExtensionTokenKeyForTest();
1540  EXPECT_EQ(2ul, token_key->scopes.size());
1541  EXPECT_TRUE(ContainsKey(token_key->scopes, "scope1"));
1542  EXPECT_TRUE(ContainsKey(token_key->scopes, "scope2"));
1543}
1544
1545IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmpty) {
1546  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1547  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1548  func->set_extension(extension.get());
1549
1550  std::string error(utils::RunFunctionAndReturnError(
1551      func.get(), "[{\"scopes\": []}]", browser()));
1552
1553  EXPECT_EQ(errors::kInvalidScopes, error);
1554}
1555
1556IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmail) {
1557  SignIn("primary@example.com");
1558  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1559  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1560  func->set_extension(extension.get());
1561  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1562  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1563      func.get(), "[{\"scopes\": [\"email\"]}]", browser()));
1564  std::string access_token;
1565  EXPECT_TRUE(value->GetAsString(&access_token));
1566  EXPECT_EQ(std::string(kAccessToken), access_token);
1567
1568  const ExtensionTokenKey* token_key = func->GetExtensionTokenKeyForTest();
1569  EXPECT_EQ(1ul, token_key->scopes.size());
1570  EXPECT_TRUE(ContainsKey(token_key->scopes, "email"));
1571}
1572
1573IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmailFooBar) {
1574  SignIn("primary@example.com");
1575  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
1576  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
1577  func->set_extension(extension.get());
1578  func->set_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);
1579  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1580      func.get(), "[{\"scopes\": [\"email\", \"foo\", \"bar\"]}]", browser()));
1581  std::string access_token;
1582  EXPECT_TRUE(value->GetAsString(&access_token));
1583  EXPECT_EQ(std::string(kAccessToken), access_token);
1584
1585  const ExtensionTokenKey* token_key = func->GetExtensionTokenKeyForTest();
1586  EXPECT_EQ(3ul, token_key->scopes.size());
1587  EXPECT_TRUE(ContainsKey(token_key->scopes, "email"));
1588  EXPECT_TRUE(ContainsKey(token_key->scopes, "foo"));
1589  EXPECT_TRUE(ContainsKey(token_key->scopes, "bar"));
1590}
1591
1592class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {
1593 protected:
1594  bool InvalidateDefaultToken() {
1595    scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func(
1596        new IdentityRemoveCachedAuthTokenFunction);
1597    func->set_extension(utils::CreateEmptyExtension(kExtensionId).get());
1598    return utils::RunFunction(
1599        func.get(),
1600        std::string("[{\"token\": \"") + kAccessToken + "\"}]",
1601        browser(),
1602        extension_function_test_utils::NONE);
1603  }
1604
1605  IdentityAPI* id_api() {
1606    return IdentityAPI::GetFactoryInstance()->Get(browser()->profile());
1607  }
1608
1609  void SetCachedToken(IdentityTokenCacheValue& token_data) {
1610    ExtensionTokenKey key(extensions::id_util::GenerateId(kExtensionId),
1611                          "test@example.com",
1612                          std::set<std::string>());
1613    id_api()->SetCachedToken(key, token_data);
1614  }
1615
1616  const IdentityTokenCacheValue& GetCachedToken() {
1617    return id_api()->GetCachedToken(
1618        ExtensionTokenKey(extensions::id_util::GenerateId(kExtensionId),
1619                          "test@example.com",
1620                          std::set<std::string>()));
1621  }
1622};
1623
1624IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {
1625  EXPECT_TRUE(InvalidateDefaultToken());
1626  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
1627            GetCachedToken().status());
1628}
1629
1630IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) {
1631  IssueAdviceInfo info;
1632  IdentityTokenCacheValue advice(info);
1633  SetCachedToken(advice);
1634  EXPECT_TRUE(InvalidateDefaultToken());
1635  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
1636            GetCachedToken().status());
1637}
1638
1639IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) {
1640  IdentityTokenCacheValue token("non_matching_token",
1641                                base::TimeDelta::FromSeconds(3600));
1642  SetCachedToken(token);
1643  EXPECT_TRUE(InvalidateDefaultToken());
1644  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1645            GetCachedToken().status());
1646  EXPECT_EQ("non_matching_token", GetCachedToken().token());
1647}
1648
1649IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) {
1650  IdentityTokenCacheValue token(kAccessToken,
1651                                base::TimeDelta::FromSeconds(3600));
1652  SetCachedToken(token);
1653  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
1654            GetCachedToken().status());
1655  EXPECT_TRUE(InvalidateDefaultToken());
1656  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
1657            GetCachedToken().status());
1658}
1659
1660class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest {
1661 public:
1662  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1663    AsyncExtensionBrowserTest::SetUpCommandLine(command_line);
1664    // Reduce performance test variance by disabling background networking.
1665    command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
1666  }
1667};
1668
1669IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) {
1670  net::SpawnedTestServer https_server(
1671      net::SpawnedTestServer::TYPE_HTTPS,
1672      net::SpawnedTestServer::kLocalhost,
1673      base::FilePath(FILE_PATH_LITERAL(
1674          "chrome/test/data/extensions/api_test/identity")));
1675  ASSERT_TRUE(https_server.Start());
1676  GURL auth_url(https_server.GetURL("files/interaction_required.html"));
1677
1678  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1679      new IdentityLaunchWebAuthFlowFunction());
1680  scoped_refptr<Extension> empty_extension(
1681      utils::CreateEmptyExtension());
1682  function->set_extension(empty_extension.get());
1683
1684  WaitForGURLAndCloseWindow popup_observer(auth_url);
1685
1686  std::string args = "[{\"interactive\": true, \"url\": \"" +
1687      auth_url.spec() + "\"}]";
1688  RunFunctionAsync(function.get(), args);
1689
1690  popup_observer.Wait();
1691  popup_observer.CloseEmbedderWebContents();
1692
1693  EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get()));
1694}
1695
1696IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) {
1697  net::SpawnedTestServer https_server(
1698      net::SpawnedTestServer::TYPE_HTTPS,
1699      net::SpawnedTestServer::kLocalhost,
1700      base::FilePath(FILE_PATH_LITERAL(
1701          "chrome/test/data/extensions/api_test/identity")));
1702  ASSERT_TRUE(https_server.Start());
1703  GURL auth_url(https_server.GetURL("files/interaction_required.html"));
1704
1705  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1706      new IdentityLaunchWebAuthFlowFunction());
1707  scoped_refptr<Extension> empty_extension(
1708      utils::CreateEmptyExtension());
1709  function->set_extension(empty_extension.get());
1710
1711  std::string args = "[{\"interactive\": false, \"url\": \"" +
1712      auth_url.spec() + "\"}]";
1713  std::string error =
1714      utils::RunFunctionAndReturnError(function.get(), args, browser());
1715
1716  EXPECT_EQ(std::string(errors::kInteractionRequired), error);
1717}
1718
1719IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) {
1720  net::SpawnedTestServer https_server(
1721      net::SpawnedTestServer::TYPE_HTTPS,
1722      net::SpawnedTestServer::kLocalhost,
1723      base::FilePath(FILE_PATH_LITERAL(
1724          "chrome/test/data/extensions/api_test/identity")));
1725  ASSERT_TRUE(https_server.Start());
1726  GURL auth_url(https_server.GetURL("files/five_hundred.html"));
1727
1728  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1729      new IdentityLaunchWebAuthFlowFunction());
1730  scoped_refptr<Extension> empty_extension(
1731      utils::CreateEmptyExtension());
1732  function->set_extension(empty_extension.get());
1733
1734  std::string args = "[{\"interactive\": true, \"url\": \"" +
1735      auth_url.spec() + "\"}]";
1736  std::string error =
1737      utils::RunFunctionAndReturnError(function.get(), args, browser());
1738
1739  EXPECT_EQ(std::string(errors::kPageLoadFailure), error);
1740}
1741
1742IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {
1743#if defined(OS_WIN) && defined(USE_ASH)
1744  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
1745  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
1746    return;
1747#endif
1748
1749  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1750      new IdentityLaunchWebAuthFlowFunction());
1751  scoped_refptr<Extension> empty_extension(
1752      utils::CreateEmptyExtension());
1753  function->set_extension(empty_extension.get());
1754
1755  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
1756  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1757      function.get(),
1758      "[{\"interactive\": false,"
1759      "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
1760      browser()));
1761
1762  std::string url;
1763  EXPECT_TRUE(value->GetAsString(&url));
1764  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
1765            url);
1766}
1767
1768IN_PROC_BROWSER_TEST_F(
1769    LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) {
1770#if defined(OS_WIN) && defined(USE_ASH)
1771  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
1772  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
1773    return;
1774#endif
1775
1776  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1777      new IdentityLaunchWebAuthFlowFunction());
1778  scoped_refptr<Extension> empty_extension(
1779      utils::CreateEmptyExtension());
1780  function->set_extension(empty_extension.get());
1781
1782  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
1783  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1784      function.get(),
1785      "[{\"interactive\": true,"
1786      "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
1787      browser()));
1788
1789  std::string url;
1790  EXPECT_TRUE(value->GetAsString(&url));
1791  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
1792            url);
1793}
1794
1795IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
1796                       DISABLED_InteractiveSecondNavigationSuccess) {
1797  net::SpawnedTestServer https_server(
1798      net::SpawnedTestServer::TYPE_HTTPS,
1799      net::SpawnedTestServer::kLocalhost,
1800      base::FilePath(FILE_PATH_LITERAL(
1801          "chrome/test/data/extensions/api_test/identity")));
1802  ASSERT_TRUE(https_server.Start());
1803  GURL auth_url(https_server.GetURL("files/redirect_to_chromiumapp.html"));
1804
1805  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1806      new IdentityLaunchWebAuthFlowFunction());
1807  scoped_refptr<Extension> empty_extension(
1808      utils::CreateEmptyExtension());
1809  function->set_extension(empty_extension.get());
1810
1811  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
1812  std::string args = "[{\"interactive\": true, \"url\": \"" +
1813      auth_url.spec() + "\"}]";
1814  scoped_ptr<base::Value> value(
1815      utils::RunFunctionAndReturnSingleResult(function.get(), args, browser()));
1816
1817  std::string url;
1818  EXPECT_TRUE(value->GetAsString(&url));
1819  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
1820            url);
1821}
1822
1823}  // namespace extensions
1824
1825// Tests the chrome.identity API implemented by custom JS bindings .
1826IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) {
1827  ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_;
1828}
1829