1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/command_line.h"
6 #include "base/strings/string_util.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/values.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/api/identity/identity_api.h"
11 #include "chrome/browser/extensions/component_loader.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/browser/extensions/extension_browsertest.h"
14 #include "chrome/browser/extensions/extension_function_test_utils.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service.h"
18 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
23 #include "chrome/test/base/in_process_browser_test.h"
24 #include "chrome/test/base/test_switches.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/test/test_utils.h"
28 #include "extensions/common/id_util.h"
29 #include "google_apis/gaia/google_service_auth_error.h"
30 #include "google_apis/gaia/oauth2_mint_token_flow.h"
31 #include "grit/browser_resources.h"
32 #include "net/test/spawned_test_server/spawned_test_server.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/gurl.h"
36
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:
SendResponseDelegate()56 SendResponseDelegate() : should_post_quit_(false) {}
57
~SendResponseDelegate()58 virtual ~SendResponseDelegate() {}
59
set_should_post_quit(bool should_quit)60 void set_should_post_quit(bool should_quit) {
61 should_post_quit_ = should_quit;
62 }
63
HasResponse()64 bool HasResponse() {
65 return response_.get() != NULL;
66 }
67
GetResponse()68 bool GetResponse() {
69 EXPECT_TRUE(HasResponse());
70 return *response_.get();
71 }
72
OnSendResponse(UIThreadExtensionFunction * function,bool success,bool bad_message)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.
RunFunctionAsync(UIThreadExtensionFunction * function,const std::string & args)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
WaitForError(UIThreadExtensionFunction * function)115 std::string WaitForError(UIThreadExtensionFunction* function) {
116 RunMessageLoopUntilResponse();
117 EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
118 return function->GetError();
119 }
120
WaitForSingleResult(UIThreadExtensionFunction * function)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:
RunMessageLoopUntilResponse()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
TestOAuth2MintTokenFlow(ResultType result,OAuth2MintTokenFlow::Delegate * delegate)156 TestOAuth2MintTokenFlow(ResultType result,
157 OAuth2MintTokenFlow::Delegate* delegate)
158 : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()),
159 result_(result),
160 delegate_(delegate) {
161 }
162
Start()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:
WaitForGURLAndCloseWindow(GURL url)198 explicit WaitForGURLAndCloseWindow(GURL url)
199 : WindowedNotificationObserver(
200 content::NOTIFICATION_LOAD_STOP,
201 content::NotificationService::AllSources()),
202 url_(url) {}
203
204 // NotificationObserver:
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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.)
CloseEmbedderWebContents()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:
MockGetAuthTokenFunction()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
set_login_access_token_result(bool result)248 void set_login_access_token_result(bool result) {
249 login_access_token_result_ = result;
250 }
251
set_login_ui_result(bool result)252 void set_login_ui_result(bool result) {
253 login_ui_result_ = result;
254 }
255
set_scope_ui_failure(GaiaWebAuthFlow::Failure failure)256 void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) {
257 scope_ui_result_ = false;
258 scope_ui_failure_ = failure;
259 }
260
set_scope_ui_oauth_error(const std::string & oauth_error)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
login_ui_shown() const267 bool login_ui_shown() const {
268 return login_ui_shown_;
269 }
270
scope_ui_shown() const271 bool scope_ui_shown() const {
272 return scope_ui_shown_;
273 }
274
StartLoginAccessTokenRequest()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
ShowLoginPopup()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
ShowOAuthApprovalDialog(const IssueAdviceInfo & issue_advice)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:
~MockGetAuthTokenFunction()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
~GetAuthTokenFunctionTest()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.
CreateExtension(int fields_to_set)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
id_api()368 IdentityAPI* id_api() {
369 return IdentityAPI::GetFactoryInstance()->GetForProfile(
370 browser()->profile());
371 }
372
GetPrimaryAccountId()373 const std::string GetPrimaryAccountId() {
374 ProfileOAuth2TokenService* token_service =
375 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
376 return token_service->GetPrimaryAccountId();
377 }
378
SetCachedToken(const IdentityTokenCacheValue & token_data)379 void SetCachedToken(const IdentityTokenCacheValue& token_data) {
380 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
381 id_api()->SetCachedToken(key, token_data);
382 }
383
GetCachedToken()384 const IdentityTokenCacheValue& GetCachedToken() {
385 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
386 return id_api()->GetCachedToken(key);
387 }
388
QueueRequestStart(IdentityMintRequestQueue::MintType type,IdentityMintRequestQueue::Request * request)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
QueueRequestComplete(IdentityMintRequestQueue::MintType type,IdentityMintRequestQueue::Request * request)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NoClientId)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NoScopes)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveNotSignedIn)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveMintFailure)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveLoginAccessTokenFailure)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveMintAdviceSuccess)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveMintBadCredentials)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveSuccess)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginCanceled)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveMintBadCredentialsLoginCanceled)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessNoToken)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessMintFailure)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessLoginAccessTokenFailure)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessMintSuccess)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessApprovalAborted)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveLoginSuccessApprovalSuccess)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalAborted)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalLoadFailed)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalInvalidRedirect)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalConnectionFailure)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalOAuthErrors)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveApprovalSuccess)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NoninteractiveQueue)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveQueue)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveQueuedNoninteractiveFails)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveCacheHit)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,NonInteractiveIssueAdviceCacheHit)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,InteractiveCacheHit)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,LoginInvalidatesTokenCache)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,ComponentWithChromeClientId)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
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,ComponentWithNormalClientId)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:
InvalidateDefaultToken()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
id_api()1030 IdentityAPI* id_api() {
1031 return IdentityAPI::GetFactoryInstance()->GetForProfile(
1032 browser()->profile());
1033 }
1034
SetCachedToken(IdentityTokenCacheValue & token_data)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
GetCachedToken()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
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest,NotFound)1050 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {
1051 EXPECT_TRUE(InvalidateDefaultToken());
1052 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
1053 GetCachedToken().status());
1054 }
1055
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest,Advice)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
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest,NonMatchingToken)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
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest,MatchingToken)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:
SetUpCommandLine(CommandLine * command_line)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,UserCloseWindow)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,InteractionRequired)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,LoadFailed)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,NonInteractiveSuccess)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,InteractiveFirstNavigationSuccess)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
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,InteractiveSecondNavigationSuccess)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 .
IN_PROC_BROWSER_TEST_F(ExtensionApiTest,ChromeIdentityJsBindings)1251 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) {
1252 ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_;
1253 }
1254