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