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