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