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