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