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