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