identity_apitest.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 "base/string_util.h"
6#include "base/stringprintf.h"
7#include "base/values.h"
8#include "chrome/browser/extensions/api/identity/identity_api.h"
9#include "chrome/browser/extensions/extension_apitest.h"
10#include "chrome/browser/extensions/extension_browsertest.h"
11#include "chrome/browser/extensions/extension_function_test_utils.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/browser_window.h"
15#include "chrome/common/chrome_notification_types.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
18#include "chrome/test/base/in_process_browser_test.h"
19#include "content/public/browser/notification_service.h"
20#include "content/public/browser/notification_source.h"
21#include "content/public/test/test_utils.h"
22#include "extensions/common/id_util.h"
23#include "google_apis/gaia/google_service_auth_error.h"
24#include "google_apis/gaia/oauth2_mint_token_flow.h"
25#include "googleurl/src/gurl.h"
26#include "grit/browser_resources.h"
27#include "net/test/spawned_test_server/spawned_test_server.h"
28#include "testing/gmock/include/gmock/gmock.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31using testing::_;
32using testing::Return;
33using testing::ReturnRef;
34
35namespace extensions {
36
37namespace {
38
39namespace errors = identity_constants;
40namespace utils = extension_function_test_utils;
41
42static const char kAccessToken[] = "auth_token";
43static const char kExtensionId[] = "ext_id";
44
45// This helps us be able to wait until an AsyncExtensionFunction calls
46// SendResponse.
47class SendResponseDelegate
48    : public UIThreadExtensionFunction::DelegateForTests {
49 public:
50  SendResponseDelegate() : should_post_quit_(false) {}
51
52  virtual ~SendResponseDelegate() {}
53
54  void set_should_post_quit(bool should_quit) {
55    should_post_quit_ = should_quit;
56  }
57
58  bool HasResponse() {
59    return response_.get() != NULL;
60  }
61
62  bool GetResponse() {
63    EXPECT_TRUE(HasResponse());
64    return *response_.get();
65  }
66
67  virtual void OnSendResponse(UIThreadExtensionFunction* function,
68                              bool success,
69                              bool bad_message) OVERRIDE {
70    ASSERT_FALSE(bad_message);
71    ASSERT_FALSE(HasResponse());
72    response_.reset(new bool);
73    *response_ = success;
74    if (should_post_quit_) {
75      MessageLoopForUI::current()->Quit();
76    }
77  }
78
79 private:
80  scoped_ptr<bool> response_;
81  bool should_post_quit_;
82};
83
84class AsyncExtensionBrowserTest : public ExtensionBrowserTest {
85 protected:
86  // Asynchronous function runner allows tests to manipulate the browser window
87  // after the call happens.
88  void RunFunctionAsync(
89      UIThreadExtensionFunction* function,
90      const std::string& args) {
91    response_delegate_.reset(new SendResponseDelegate);
92    function->set_test_delegate(response_delegate_.get());
93    scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args));
94    EXPECT_TRUE(parsed_args.get()) <<
95        "Could not parse extension function arguments: " << args;
96    function->SetArgs(parsed_args.get());
97
98    if (!function->GetExtension()) {
99      scoped_refptr<Extension> empty_extension(
100          utils::CreateEmptyExtension());
101      function->set_extension(empty_extension.get());
102    }
103
104    function->set_profile(browser()->profile());
105    function->set_has_callback(true);
106    function->Run();
107  }
108
109  std::string WaitForError(UIThreadExtensionFunction* function) {
110    RunMessageLoopUntilResponse();
111    EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
112    return function->GetError();
113  }
114
115  base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) {
116    RunMessageLoopUntilResponse();
117    EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
118                                              << function->GetError();
119    const base::Value* single_result = NULL;
120    if (function->GetResultList() != NULL &&
121        function->GetResultList()->Get(0, &single_result)) {
122      return single_result->DeepCopy();
123    }
124    return NULL;
125  }
126
127 private:
128  void RunMessageLoopUntilResponse() {
129    // If the RunImpl of |function| didn't already call SendResponse, run the
130    // message loop until they do.
131    if (!response_delegate_->HasResponse()) {
132      response_delegate_->set_should_post_quit(true);
133      content::RunMessageLoop();
134    }
135    EXPECT_TRUE(response_delegate_->HasResponse());
136  }
137
138  scoped_ptr<SendResponseDelegate> response_delegate_;
139};
140
141class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow {
142 public:
143  enum ResultType {
144    ISSUE_ADVICE_SUCCESS,
145    MINT_TOKEN_SUCCESS,
146    MINT_TOKEN_FAILURE,
147    MINT_TOKEN_BAD_CREDENTIALS
148  };
149
150  TestOAuth2MintTokenFlow(ResultType result,
151                          OAuth2MintTokenFlow::Delegate* delegate)
152    : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()),
153      result_(result),
154      delegate_(delegate) {
155  }
156
157  virtual void Start() OVERRIDE {
158    switch (result_) {
159      case ISSUE_ADVICE_SUCCESS: {
160        IssueAdviceInfo info;
161        delegate_->OnIssueAdviceSuccess(info);
162        break;
163      }
164      case MINT_TOKEN_SUCCESS: {
165        delegate_->OnMintTokenSuccess(kAccessToken, 3600);
166        break;
167      }
168      case MINT_TOKEN_FAILURE: {
169        GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
170        delegate_->OnMintTokenFailure(error);
171        break;
172      }
173      case MINT_TOKEN_BAD_CREDENTIALS: {
174        GoogleServiceAuthError error(
175            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
176        delegate_->OnMintTokenFailure(error);
177        break;
178      }
179    }
180  }
181
182 private:
183  ResultType result_;
184  OAuth2MintTokenFlow::Delegate* delegate_;
185};
186
187ProfileKeyedService* IdentityAPITestFactory(content::BrowserContext* profile) {
188  return new IdentityAPI(static_cast<Profile*>(profile));
189}
190
191}  // namespace
192
193class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
194 public:
195  MockGetAuthTokenFunction() : login_ui_result_(true),
196                               install_ui_result_(false),
197                               login_ui_shown_(false),
198                               install_ui_shown_(false) {
199  }
200
201  void set_login_ui_result(bool result) {
202    login_ui_result_ = result;
203  }
204
205  void set_install_ui_result(bool result) {
206    install_ui_result_ = result;
207  }
208
209  bool login_ui_shown() const {
210    return login_ui_shown_;
211  }
212
213  bool install_ui_shown() const {
214    return install_ui_shown_;
215  }
216
217  virtual void ShowLoginPopup() OVERRIDE {
218    EXPECT_FALSE(login_ui_shown_);
219    login_ui_shown_ = true;
220    if (login_ui_result_)
221      SigninSuccess("fake_refresh_token");
222    else
223      SigninFailed();
224  }
225
226  virtual void ShowOAuthApprovalDialog(
227      const IssueAdviceInfo& issue_advice) OVERRIDE {
228    install_ui_shown_ = true;
229    // Call InstallUIProceed or InstallUIAbort based on the flag.
230    if (install_ui_result_)
231      InstallUIProceed();
232    else
233      InstallUIAbort(true);
234  }
235
236  MOCK_CONST_METHOD0(HasLoginToken, bool());
237  MOCK_METHOD1(CreateMintTokenFlow,
238               OAuth2MintTokenFlow* (OAuth2MintTokenFlow::Mode mode));
239
240 private:
241  ~MockGetAuthTokenFunction() {}
242  bool login_ui_result_;
243  bool install_ui_result_;
244  bool login_ui_shown_;
245  bool install_ui_shown_;
246};
247
248class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {
249 public:
250  MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
251};
252
253class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
254 protected:
255  enum OAuth2Fields {
256    NONE = 0,
257    CLIENT_ID = 1,
258    SCOPES = 2
259  };
260
261  virtual ~GetAuthTokenFunctionTest() {}
262
263  // Helper to create an extension with specific OAuth2Info fields set.
264  // |fields_to_set| should be computed by using fields of Oauth2Fields enum.
265  const Extension* CreateExtension(int fields_to_set) {
266    const Extension* ext = LoadExtension(
267        test_data_dir_.AppendASCII("platform_apps/oauth2"));
268    OAuth2Info& oauth2_info = const_cast<OAuth2Info&>(
269        OAuth2Info::GetOAuth2Info(ext));
270    if ((fields_to_set & CLIENT_ID) != 0)
271      oauth2_info.client_id = "client1";
272    if ((fields_to_set & SCOPES) != 0) {
273      oauth2_info.scopes.push_back("scope1");
274      oauth2_info.scopes.push_back("scope2");
275    }
276    return ext;
277  }
278
279  IdentityAPI* id_api() {
280    return IdentityAPI::GetFactoryInstance()->GetForProfile(
281        browser()->profile());
282  }
283};
284
285IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
286                       NoClientId) {
287  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
288  func->set_extension(CreateExtension(SCOPES));
289  std::string error = utils::RunFunctionAndReturnError(
290      func.get(), "[{}]", browser());
291  EXPECT_EQ(std::string(errors::kInvalidClientId), error);
292  EXPECT_FALSE(func->login_ui_shown());
293  EXPECT_FALSE(func->install_ui_shown());
294}
295
296IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
297                       NoScopes) {
298  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
299  func->set_extension(CreateExtension(CLIENT_ID));
300  std::string error = utils::RunFunctionAndReturnError(
301      func.get(), "[{}]", browser());
302  EXPECT_EQ(std::string(errors::kInvalidScopes), error);
303  EXPECT_FALSE(func->login_ui_shown());
304  EXPECT_FALSE(func->install_ui_shown());
305}
306
307IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
308                       NonInteractiveNotSignedIn) {
309  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
310  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
311  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
312  std::string error = utils::RunFunctionAndReturnError(
313      func.get(), "[{}]", browser());
314  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
315  EXPECT_FALSE(func->login_ui_shown());
316  EXPECT_FALSE(func->install_ui_shown());
317}
318
319IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
320                       NonInteractiveMintFailure) {
321  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
322  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
323  EXPECT_CALL(*func.get(), HasLoginToken())
324      .WillOnce(Return(true));
325  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
326      TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get());
327  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
328  std::string error = utils::RunFunctionAndReturnError(
329      func.get(), "[{}]", browser());
330  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
331  EXPECT_FALSE(func->login_ui_shown());
332  EXPECT_FALSE(func->install_ui_shown());
333}
334
335IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
336                       NonInteractiveMintAdviceSuccess) {
337  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
338  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
339  func->set_extension(extension);
340  EXPECT_CALL(*func.get(), HasLoginToken())
341      .WillOnce(Return(true));
342  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
343      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
344  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
345  std::string error = utils::RunFunctionAndReturnError(
346      func.get(), "[{}]", browser());
347  EXPECT_EQ(std::string(errors::kNoGrant), error);
348  EXPECT_FALSE(func->login_ui_shown());
349  EXPECT_FALSE(func->install_ui_shown());
350
351  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
352  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
353            id_api()->GetCachedToken(extension->id(),
354                                     oauth2_info.scopes).status());
355}
356
357IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
358                       NonInteractiveMintBadCredentials) {
359  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
360  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
361  EXPECT_CALL(*func.get(), HasLoginToken())
362      .WillOnce(Return(true));
363  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
364      TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get());
365  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
366  std::string error = utils::RunFunctionAndReturnError(
367      func.get(), "[{}]", browser());
368  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
369  EXPECT_FALSE(func->login_ui_shown());
370  EXPECT_FALSE(func->install_ui_shown());
371}
372
373IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
374                       NonInteractiveSuccess) {
375  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
376  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
377  func->set_extension(extension);
378  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
379  EXPECT_CALL(*func.get(), HasLoginToken())
380      .WillOnce(Return(true));
381  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
382      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
383  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
384  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
385      func.get(), "[{}]", browser()));
386  std::string access_token;
387  EXPECT_TRUE(value->GetAsString(&access_token));
388  EXPECT_EQ(std::string(kAccessToken), access_token);
389  EXPECT_FALSE(func->login_ui_shown());
390  EXPECT_FALSE(func->install_ui_shown());
391  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
392            id_api()->GetCachedToken(extension->id(),
393                                     oauth2_info.scopes).status());
394}
395
396IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
397                       InteractiveLoginCanceled) {
398  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
399  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
400  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
401  func->set_login_ui_result(false);
402  std::string error = utils::RunFunctionAndReturnError(
403      func.get(), "[{\"interactive\": true}]", browser());
404  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
405  EXPECT_TRUE(func->login_ui_shown());
406  EXPECT_FALSE(func->install_ui_shown());
407}
408
409IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
410                       InteractiveMintBadCredentialsLoginCanceled) {
411  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
412  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
413  EXPECT_CALL(*func.get(), HasLoginToken())
414      .WillOnce(Return(true));
415  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
416      TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get());
417  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
418  func->set_login_ui_result(false);
419  std::string error = utils::RunFunctionAndReturnError(
420      func.get(), "[{\"interactive\": true}]", browser());
421  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
422  EXPECT_TRUE(func->login_ui_shown());
423  EXPECT_FALSE(func->install_ui_shown());
424}
425
426IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
427                       InteractiveLoginSuccessNoToken) {
428  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
429  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
430  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
431  func->set_login_ui_result(false);
432  std::string error = utils::RunFunctionAndReturnError(
433      func.get(), "[{\"interactive\": true}]", browser());
434  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
435  EXPECT_TRUE(func->login_ui_shown());
436  EXPECT_FALSE(func->install_ui_shown());
437}
438
439IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
440                       InteractiveLoginSuccessMintFailure) {
441  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
442  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
443  EXPECT_CALL(*func.get(), HasLoginToken())
444      .WillOnce(Return(false));
445  func->set_login_ui_result(true);
446  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
447      TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get());
448  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
449  std::string error = utils::RunFunctionAndReturnError(
450      func.get(), "[{\"interactive\": true}]", browser());
451  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
452  EXPECT_TRUE(func->login_ui_shown());
453  EXPECT_FALSE(func->install_ui_shown());
454}
455
456IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
457                       InteractiveLoginSuccessMintSuccess) {
458  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
459  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
460  EXPECT_CALL(*func.get(), HasLoginToken())
461      .WillOnce(Return(false));
462  func->set_login_ui_result(true);
463  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
464      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
465  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
466  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
467      func.get(), "[{\"interactive\": true}]", browser()));
468  std::string access_token;
469  EXPECT_TRUE(value->GetAsString(&access_token));
470  EXPECT_EQ(std::string(kAccessToken), access_token);
471  EXPECT_TRUE(func->login_ui_shown());
472  EXPECT_FALSE(func->install_ui_shown());
473}
474
475IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
476                       InteractiveLoginSuccessApprovalAborted) {
477  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
478  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
479  EXPECT_CALL(*func.get(), HasLoginToken())
480      .WillOnce(Return(false));
481  func->set_login_ui_result(true);
482  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
483      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
484  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
485  func->set_install_ui_result(false);
486  std::string error = utils::RunFunctionAndReturnError(
487      func.get(), "[{\"interactive\": true}]", browser());
488  EXPECT_EQ(std::string(errors::kUserRejected), error);
489  EXPECT_TRUE(func->login_ui_shown());
490  EXPECT_TRUE(func->install_ui_shown());
491}
492
493IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
494                       InteractiveLoginSuccessApprovalDoneMintFailure) {
495  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
496  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
497  EXPECT_CALL(*func.get(), HasLoginToken())
498      .WillOnce(Return(false));
499  func->set_login_ui_result(true);
500  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
501      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
502  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
503      TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get());
504  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
505      .WillOnce(Return(flow1))
506      .WillOnce(Return(flow2));
507
508  func->set_install_ui_result(true);
509  std::string error = utils::RunFunctionAndReturnError(
510      func.get(), "[{\"interactive\": true}]", browser());
511  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
512  EXPECT_TRUE(func->login_ui_shown());
513  EXPECT_TRUE(func->install_ui_shown());
514}
515
516IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
517                       InteractiveLoginSuccessApprovalDoneMintSuccess) {
518  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
519  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
520  func->set_extension(extension);
521  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
522  EXPECT_CALL(*func.get(), HasLoginToken())
523      .WillOnce(Return(false));
524  func->set_login_ui_result(true);
525  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
526      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
527  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
528      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
529  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
530      .WillOnce(Return(flow1))
531      .WillOnce(Return(flow2));
532
533  func->set_install_ui_result(true);
534  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
535      func.get(), "[{\"interactive\": true}]", browser()));
536  std::string access_token;
537  EXPECT_TRUE(value->GetAsString(&access_token));
538  EXPECT_EQ(std::string(kAccessToken), access_token);
539  EXPECT_TRUE(func->login_ui_shown());
540  EXPECT_TRUE(func->install_ui_shown());
541  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
542            id_api()->GetCachedToken(extension->id(),
543                                     oauth2_info.scopes).status());
544}
545
546IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
547                       InteractiveApprovalAborted) {
548  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
549  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
550  EXPECT_CALL(*func.get(), HasLoginToken())
551      .WillOnce(Return(true));
552  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
553      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
554  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
555  func->set_install_ui_result(false);
556  std::string error = utils::RunFunctionAndReturnError(
557      func.get(), "[{\"interactive\": true}]", browser());
558  EXPECT_EQ(std::string(errors::kUserRejected), error);
559  EXPECT_FALSE(func->login_ui_shown());
560  EXPECT_TRUE(func->install_ui_shown());
561}
562
563IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
564                       InteractiveApprovalDoneMintSuccess) {
565  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
566  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
567  EXPECT_CALL(*func.get(), HasLoginToken())
568      .WillOnce(Return(true));
569  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
570      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
571  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
572      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
573  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
574      .WillOnce(Return(flow1))
575      .WillOnce(Return(flow2));
576
577  func->set_install_ui_result(true);
578  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
579      func.get(), "[{\"interactive\": true}]", browser()));
580  std::string access_token;
581  EXPECT_TRUE(value->GetAsString(&access_token));
582  EXPECT_EQ(std::string(kAccessToken), access_token);
583  EXPECT_FALSE(func->login_ui_shown());
584  EXPECT_TRUE(func->install_ui_shown());
585}
586
587IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
588                       InteractiveApprovalDoneMintBadCredentials) {
589  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
590  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
591  EXPECT_CALL(*func.get(), HasLoginToken())
592      .WillOnce(Return(true));
593  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
594      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
595  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
596      TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get());
597  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
598      .WillOnce(Return(flow1))
599      .WillOnce(Return(flow2));
600
601  func->set_install_ui_result(true);
602  std::string error = utils::RunFunctionAndReturnError(
603      func.get(), "[{\"interactive\": true}]", browser());
604  EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
605  EXPECT_FALSE(func->login_ui_shown());
606  EXPECT_TRUE(func->install_ui_shown());
607}
608
609IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {
610  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
611  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
612  func->set_extension(extension);
613
614  // Create a fake request to block the queue.
615  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
616  std::set<std::string> scopes(oauth2_info.scopes.begin(),
617                               oauth2_info.scopes.end());
618  IdentityAPI* id_api =
619      extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
620          browser()->profile());
621  IdentityMintRequestQueue* queue = id_api->mint_queue();
622  MockQueuedMintRequest queued_request;
623  IdentityMintRequestQueue::MintType type =
624      IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
625
626  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
627  queue->RequestStart(type, extension->id(), scopes, &queued_request);
628
629  // The real request will start processing, but wait in the queue behind
630  // the blocker.
631  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
632  RunFunctionAsync(func, "[{}]");
633  // Verify that we have fetched the login token at this point.
634  testing::Mock::VerifyAndClearExpectations(func);
635
636  // The flow will be created after the first queued request clears.
637  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
638      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
639  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
640
641  queue->RequestComplete(type, extension->id(), scopes, &queued_request);
642
643  scoped_ptr<base::Value> value(WaitForSingleResult(func));
644  std::string access_token;
645  EXPECT_TRUE(value->GetAsString(&access_token));
646  EXPECT_EQ(std::string(kAccessToken), access_token);
647  EXPECT_FALSE(func->login_ui_shown());
648  EXPECT_FALSE(func->install_ui_shown());
649}
650
651IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) {
652  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
653  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
654  func->set_extension(extension);
655
656  // Create a fake request to block the queue.
657  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
658  std::set<std::string> scopes(oauth2_info.scopes.begin(),
659                               oauth2_info.scopes.end());
660  IdentityAPI* id_api =
661      extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
662          browser()->profile());
663  IdentityMintRequestQueue* queue = id_api->mint_queue();
664  MockQueuedMintRequest queued_request;
665  IdentityMintRequestQueue::MintType type =
666      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
667
668  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
669  queue->RequestStart(type, extension->id(), scopes, &queued_request);
670
671  // The real request will start processing, but wait in the queue behind
672  // the blocker.
673  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
674  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
675      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
676  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1));
677  RunFunctionAsync(func, "[{\"interactive\": true}]");
678  // Verify that we have fetched the login token and run the first flow.
679  testing::Mock::VerifyAndClearExpectations(func);
680  EXPECT_FALSE(func->install_ui_shown());
681
682  // The UI will be displayed and the second flow will be created
683  // after the first queued request clears.
684  func->set_install_ui_result(true);
685  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
686      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
687  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow2));
688
689  queue->RequestComplete(type, extension->id(), scopes, &queued_request);
690
691  scoped_ptr<base::Value> value(WaitForSingleResult(func));
692  std::string access_token;
693  EXPECT_TRUE(value->GetAsString(&access_token));
694  EXPECT_EQ(std::string(kAccessToken), access_token);
695  EXPECT_FALSE(func->login_ui_shown());
696  EXPECT_TRUE(func->install_ui_shown());
697}
698
699IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
700                       InteractiveQueuedNoninteractiveFails) {
701  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
702  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
703  func->set_extension(extension);
704
705  // Create a fake request to block the interactive queue.
706  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
707  std::set<std::string> scopes(oauth2_info.scopes.begin(),
708                               oauth2_info.scopes.end());
709  IdentityAPI* id_api =
710      extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
711          browser()->profile());
712  IdentityMintRequestQueue* queue = id_api->mint_queue();
713  MockQueuedMintRequest queued_request;
714  IdentityMintRequestQueue::MintType type =
715      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
716
717  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
718  queue->RequestStart(type, extension->id(), scopes, &queued_request);
719
720  // Non-interactive requests fail without hitting GAIA, because a
721  // consent UI is known to be up.
722  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
723  std::string error = utils::RunFunctionAndReturnError(
724      func.get(), "[{}]", browser());
725  EXPECT_EQ(std::string(errors::kNoGrant), error);
726  EXPECT_FALSE(func->login_ui_shown());
727  EXPECT_FALSE(func->install_ui_shown());
728
729  queue->RequestComplete(type, extension->id(), scopes, &queued_request);
730}
731
732IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
733                       NonInteractiveCacheHit) {
734  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
735  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
736  func->set_extension(extension);
737
738  // pre-populate the cache with a token
739  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
740  IdentityTokenCacheValue token(kAccessToken,
741                                base::TimeDelta::FromSeconds(3600));
742  id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token);
743
744  // Get a token. Should not require a GAIA request.
745  EXPECT_CALL(*func.get(), HasLoginToken())
746      .WillOnce(Return(true));
747  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
748      func.get(), "[{}]", browser()));
749  std::string access_token;
750  EXPECT_TRUE(value->GetAsString(&access_token));
751  EXPECT_EQ(std::string(kAccessToken), access_token);
752  EXPECT_FALSE(func->login_ui_shown());
753  EXPECT_FALSE(func->install_ui_shown());
754}
755
756IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
757                       NonInteractiveIssueAdviceCacheHit) {
758  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
759  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
760  func->set_extension(extension);
761
762  // pre-populate the cache with advice
763  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
764  IssueAdviceInfo info;
765  IdentityTokenCacheValue token(info);
766  id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token);
767
768  // Should return an error without a GAIA request.
769  EXPECT_CALL(*func.get(), HasLoginToken())
770      .WillOnce(Return(true));
771  std::string error = utils::RunFunctionAndReturnError(
772      func.get(), "[{}]", browser());
773  EXPECT_EQ(std::string(errors::kNoGrant), error);
774  EXPECT_FALSE(func->login_ui_shown());
775  EXPECT_FALSE(func->install_ui_shown());
776}
777
778IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
779                       InteractiveCacheHit) {
780  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
781  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
782  func->set_extension(extension);
783
784  // Create a fake request to block the queue.
785  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
786  std::set<std::string> scopes(oauth2_info.scopes.begin(),
787                               oauth2_info.scopes.end());
788  IdentityMintRequestQueue* queue = id_api()->mint_queue();
789  MockQueuedMintRequest queued_request;
790  IdentityMintRequestQueue::MintType type =
791      IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
792
793  EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
794  queue->RequestStart(type, extension->id(), scopes, &queued_request);
795
796  // The real request will start processing, but wait in the queue behind
797  // the blocker.
798  EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
799  TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
800      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
801  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
802  RunFunctionAsync(func, "[{\"interactive\": true}]");
803
804  // Populate the cache with a token while the request is blocked.
805  IdentityTokenCacheValue token(kAccessToken,
806                                base::TimeDelta::FromSeconds(3600));
807  id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token);
808
809  // When we wake up the request, it returns the cached token without
810  // displaying a UI, or hitting GAIA.
811
812  queue->RequestComplete(type, extension->id(), scopes, &queued_request);
813
814  scoped_ptr<base::Value> value(WaitForSingleResult(func));
815  std::string access_token;
816  EXPECT_TRUE(value->GetAsString(&access_token));
817  EXPECT_EQ(std::string(kAccessToken), access_token);
818  EXPECT_FALSE(func->login_ui_shown());
819  EXPECT_FALSE(func->install_ui_shown());
820}
821
822IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
823                       LoginInvalidatesTokenCache) {
824  scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
825  scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
826  func->set_extension(extension);
827
828  // pre-populate the cache with a token
829  const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension);
830  IdentityTokenCacheValue token(kAccessToken,
831                                base::TimeDelta::FromSeconds(3600));
832  id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token);
833
834  // Because the user is not signed in, the token will be removed,
835  // and we'll hit GAIA for new tokens.
836  EXPECT_CALL(*func.get(), HasLoginToken())
837      .WillOnce(Return(false));
838  func->set_login_ui_result(true);
839  TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
840      TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
841  TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow(
842      TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
843  EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
844      .WillOnce(Return(flow1))
845      .WillOnce(Return(flow2));
846
847  func->set_install_ui_result(true);
848  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
849      func.get(), "[{\"interactive\": true}]", browser()));
850  std::string access_token;
851  EXPECT_TRUE(value->GetAsString(&access_token));
852  EXPECT_EQ(std::string(kAccessToken), access_token);
853  EXPECT_TRUE(func->login_ui_shown());
854  EXPECT_TRUE(func->install_ui_shown());
855  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
856            id_api()->GetCachedToken(extension->id(),
857                                     oauth2_info.scopes).status());
858}
859
860class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {
861 protected:
862  bool InvalidateDefaultToken() {
863    scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func(
864        new IdentityRemoveCachedAuthTokenFunction);
865    func->set_extension(utils::CreateEmptyExtension(kExtensionId));
866    return utils::RunFunction(
867        func, std::string("[{\"token\": \"") + kAccessToken + "\"}]", browser(),
868        extension_function_test_utils::NONE);
869  }
870
871  IdentityAPI* id_api() {
872    return IdentityAPI::GetFactoryInstance()->GetForProfile(
873        browser()->profile());
874  }
875
876  void SetCachedToken(IdentityTokenCacheValue& token_data) {
877    id_api()->SetCachedToken(extensions::id_util::GenerateId(kExtensionId),
878                             std::vector<std::string>(), token_data);
879  }
880
881  const IdentityTokenCacheValue& GetCachedToken() {
882    return id_api()->GetCachedToken(
883        extensions::id_util::GenerateId(kExtensionId),
884        std::vector<std::string>());
885  }
886};
887
888IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {
889  EXPECT_TRUE(InvalidateDefaultToken());
890  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
891            GetCachedToken().status());
892}
893
894IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) {
895  IssueAdviceInfo info;
896  IdentityTokenCacheValue advice(info);
897  SetCachedToken(advice);
898  EXPECT_TRUE(InvalidateDefaultToken());
899  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
900            GetCachedToken().status());
901}
902
903IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) {
904  IdentityTokenCacheValue token("non_matching_token",
905                                base::TimeDelta::FromSeconds(3600));
906  SetCachedToken(token);
907  EXPECT_TRUE(InvalidateDefaultToken());
908  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
909            GetCachedToken().status());
910  EXPECT_EQ("non_matching_token", GetCachedToken().token());
911}
912
913IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) {
914  IdentityTokenCacheValue token(kAccessToken,
915                                base::TimeDelta::FromSeconds(3600));
916  SetCachedToken(token);
917  EXPECT_TRUE(InvalidateDefaultToken());
918  EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
919            GetCachedToken().status());
920}
921
922class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest {
923 public:
924  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
925    // Reduce performance test variance by disabling background networking.
926    command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
927  }
928};
929
930IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) {
931  net::SpawnedTestServer https_server(
932      net::SpawnedTestServer::TYPE_HTTPS,
933      net::SpawnedTestServer::kLocalhost,
934      base::FilePath(FILE_PATH_LITERAL(
935          "chrome/test/data/extensions/api_test/identity")));
936  ASSERT_TRUE(https_server.Start());
937  GURL auth_url(https_server.GetURL("files/interaction_required.html"));
938
939  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
940      new IdentityLaunchWebAuthFlowFunction());
941  scoped_refptr<Extension> empty_extension(
942      utils::CreateEmptyExtension());
943  function->set_extension(empty_extension.get());
944
945  content::WindowedNotificationObserver popup_observer(
946      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
947      content::NotificationService::AllSources());
948
949  std::string args = "[{\"interactive\": true, \"url\": \"" +
950      auth_url.spec() + "\"}]";
951  RunFunctionAsync(function, args);
952
953  popup_observer.Wait();
954  content::Source<Browser>(popup_observer.source())->window()->Close();
955
956  EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function));
957}
958
959IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) {
960  net::SpawnedTestServer https_server(
961      net::SpawnedTestServer::TYPE_HTTPS,
962      net::SpawnedTestServer::kLocalhost,
963      base::FilePath(FILE_PATH_LITERAL(
964          "chrome/test/data/extensions/api_test/identity")));
965  ASSERT_TRUE(https_server.Start());
966  GURL auth_url(https_server.GetURL("files/interaction_required.html"));
967
968  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
969      new IdentityLaunchWebAuthFlowFunction());
970  scoped_refptr<Extension> empty_extension(
971      utils::CreateEmptyExtension());
972  function->set_extension(empty_extension.get());
973
974  std::string args = "[{\"interactive\": false, \"url\": \"" +
975      auth_url.spec() + "\"}]";
976  std::string error = utils::RunFunctionAndReturnError(function, args,
977                                                       browser());
978
979  EXPECT_EQ(std::string(errors::kInteractionRequired), error);
980}
981
982IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {
983  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
984      new IdentityLaunchWebAuthFlowFunction());
985  scoped_refptr<Extension> empty_extension(
986      utils::CreateEmptyExtension());
987  function->set_extension(empty_extension.get());
988
989  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
990  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
991      function,
992      "[{\"interactive\": false,"
993      "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
994      browser()));
995
996  std::string url;
997  EXPECT_TRUE(value->GetAsString(&url));
998  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
999            url);
1000}
1001
1002IN_PROC_BROWSER_TEST_F(
1003    LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) {
1004  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1005      new IdentityLaunchWebAuthFlowFunction());
1006  scoped_refptr<Extension> empty_extension(
1007      utils::CreateEmptyExtension());
1008  function->set_extension(empty_extension.get());
1009
1010  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
1011  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1012      function,
1013      "[{\"interactive\": true,"
1014      "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
1015      browser()));
1016
1017  std::string url;
1018  EXPECT_TRUE(value->GetAsString(&url));
1019  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
1020            url);
1021}
1022
1023IN_PROC_BROWSER_TEST_F(
1024    LaunchWebAuthFlowFunctionTest, InteractiveSecondNavigationSuccess) {
1025  net::SpawnedTestServer https_server(
1026      net::SpawnedTestServer::TYPE_HTTPS,
1027      net::SpawnedTestServer::kLocalhost,
1028      base::FilePath(FILE_PATH_LITERAL(
1029          "chrome/test/data/extensions/api_test/identity")));
1030  ASSERT_TRUE(https_server.Start());
1031  GURL auth_url(https_server.GetURL("files/redirect_to_chromiumapp.html"));
1032
1033  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
1034      new IdentityLaunchWebAuthFlowFunction());
1035  scoped_refptr<Extension> empty_extension(
1036      utils::CreateEmptyExtension());
1037  function->set_extension(empty_extension.get());
1038
1039  function->InitFinalRedirectURLPrefixForTest("abcdefghij");
1040  std::string args = "[{\"interactive\": true, \"url\": \"" +
1041      auth_url.spec() + "\"}]";
1042  scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
1043      function, args, browser()));
1044
1045  std::string url;
1046  EXPECT_TRUE(value->GetAsString(&url));
1047  EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
1048            url);
1049}
1050
1051}  // namespace extensions
1052