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