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