Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / identity / identity_apitest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/command_line.h"
6 #include "base/strings/string_util.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/values.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/api/identity/identity_api.h"
11 #include "chrome/browser/extensions/component_loader.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/browser/extensions/extension_browsertest.h"
14 #include "chrome/browser/extensions/extension_function_test_utils.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/signin/signin_manager.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
23 #include "chrome/test/base/in_process_browser_test.h"
24 #include "chrome/test/base/test_switches.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/test/test_utils.h"
28 #include "extensions/common/id_util.h"
29 #include "google_apis/gaia/google_service_auth_error.h"
30 #include "google_apis/gaia/oauth2_mint_token_flow.h"
31 #include "grit/browser_resources.h"
32 #include "net/test/spawned_test_server/spawned_test_server.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/gurl.h"
36
37 using testing::_;
38 using testing::Return;
39 using testing::ReturnRef;
40
41 namespace extensions {
42
43 namespace {
44
45 namespace errors = identity_constants;
46 namespace utils = extension_function_test_utils;
47
48 static const char kAccessToken[] = "auth_token";
49 static const char kExtensionId[] = "ext_id";
50
51 // This helps us be able to wait until an AsyncExtensionFunction calls
52 // SendResponse.
53 class 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
90 class 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
147 class 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.
196 class 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
239 class 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
325 class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {
326  public:
327   MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
328 };
329
330 class 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     SigninManagerBase* signin_manager =
375         SigninManagerFactory::GetForProfile(browser()->profile());
376     return signin_manager->GetAuthenticatedAccountId();
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
406 IN_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
417 IN_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
428 IN_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
440 IN_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
456 IN_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
468 IN_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
487 IN_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
503 IN_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
529 IN_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
542 IN_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
559 IN_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
572 IN_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
589 IN_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
603 IN_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
622 IN_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
640 IN_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
661 IN_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
678 IN_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
695 IN_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
712 IN_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
729 IN_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
762 IN_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
785 IN_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
820 IN_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
856 IN_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
882 IN_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
905 IN_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
926 IN_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
966 IN_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
998 IN_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
1009 IN_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
1017 class 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
1050 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {
1051   EXPECT_TRUE(InvalidateDefaultToken());
1052   EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
1053             GetCachedToken().status());
1054 }
1055
1056 IN_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
1065 IN_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
1075 IN_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
1086 class 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
1094 IN_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
1121 IN_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
1144 IN_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
1167 IN_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
1193 IN_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
1220 IN_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 .
1251 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) {
1252   ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_;
1253 }