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: 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 ProfileOAuth2TokenService* token_service = 375 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile()); 376 return token_service->GetPrimaryAccountId(); 377 } 378 379 void SetCachedToken(const IdentityTokenCacheValue& token_data) { 380 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 381 id_api()->SetCachedToken(key, token_data); 382 } 383 384 const IdentityTokenCacheValue& GetCachedToken() { 385 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 386 return id_api()->GetCachedToken(key); 387 } 388 389 void QueueRequestStart(IdentityMintRequestQueue::MintType type, 390 IdentityMintRequestQueue::Request* request) { 391 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 392 id_api()->mint_queue()->RequestStart(type, key, request); 393 } 394 395 void QueueRequestComplete(IdentityMintRequestQueue::MintType type, 396 IdentityMintRequestQueue::Request* request) { 397 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 398 id_api()->mint_queue()->RequestComplete(type, key, request); 399 } 400 401 private: 402 std::string extension_id_; 403 std::set<std::string> oauth_scopes_; 404 }; 405 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 (at) 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 (at) 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 } 1254