identity_apitest.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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 <set> 6#include <string> 7#include <vector> 8 9#include "base/command_line.h" 10#include "base/strings/string_util.h" 11#include "base/strings/stringprintf.h" 12#include "base/values.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/extensions/api/identity/identity_api.h" 15#include "chrome/browser/extensions/component_loader.h" 16#include "chrome/browser/extensions/extension_apitest.h" 17#include "chrome/browser/extensions/extension_browsertest.h" 18#include "chrome/browser/extensions/extension_function_test_utils.h" 19#include "chrome/browser/extensions/extension_service.h" 20#include "chrome/browser/guest_view/guest_view_base.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/signin/fake_profile_oauth2_token_service.h" 23#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" 24#include "chrome/browser/signin/fake_signin_manager.h" 25#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 26#include "chrome/browser/signin/signin_manager_factory.h" 27#include "chrome/browser/ui/browser.h" 28#include "chrome/browser/ui/browser_window.h" 29#include "chrome/common/chrome_switches.h" 30#include "chrome/common/extensions/api/identity.h" 31#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 32#include "chrome/test/base/in_process_browser_test.h" 33#include "chrome/test/base/test_switches.h" 34#include "components/signin/core/browser/signin_manager.h" 35#include "components/signin/core/common/profile_management_switches.h" 36#include "content/public/browser/notification_service.h" 37#include "content/public/browser/notification_source.h" 38#include "content/public/test/test_utils.h" 39#include "extensions/common/id_util.h" 40#include "google_apis/gaia/google_service_auth_error.h" 41#include "google_apis/gaia/oauth2_mint_token_flow.h" 42#include "grit/browser_resources.h" 43#include "net/test/spawned_test_server/spawned_test_server.h" 44#include "testing/gmock/include/gmock/gmock.h" 45#include "testing/gtest/include/gtest/gtest.h" 46#include "url/gurl.h" 47 48using testing::_; 49using testing::Return; 50using testing::ReturnRef; 51 52namespace extensions { 53 54namespace { 55 56namespace errors = identity_constants; 57namespace utils = extension_function_test_utils; 58 59static const char kAccessToken[] = "auth_token"; 60static const char kExtensionId[] = "ext_id"; 61 62// This helps us be able to wait until an UIThreadExtensionFunction calls 63// SendResponse. 64class SendResponseDelegate 65 : public UIThreadExtensionFunction::DelegateForTests { 66 public: 67 SendResponseDelegate() : should_post_quit_(false) {} 68 69 virtual ~SendResponseDelegate() {} 70 71 void set_should_post_quit(bool should_quit) { 72 should_post_quit_ = should_quit; 73 } 74 75 bool HasResponse() { 76 return response_.get() != NULL; 77 } 78 79 bool GetResponse() { 80 EXPECT_TRUE(HasResponse()); 81 return *response_.get(); 82 } 83 84 virtual void OnSendResponse(UIThreadExtensionFunction* function, 85 bool success, 86 bool bad_message) OVERRIDE { 87 ASSERT_FALSE(bad_message); 88 ASSERT_FALSE(HasResponse()); 89 response_.reset(new bool); 90 *response_ = success; 91 if (should_post_quit_) { 92 base::MessageLoopForUI::current()->Quit(); 93 } 94 } 95 96 private: 97 scoped_ptr<bool> response_; 98 bool should_post_quit_; 99}; 100 101class AsyncExtensionBrowserTest : public ExtensionBrowserTest { 102 protected: 103 // Asynchronous function runner allows tests to manipulate the browser window 104 // after the call happens. 105 void RunFunctionAsync( 106 UIThreadExtensionFunction* function, 107 const std::string& args) { 108 response_delegate_.reset(new SendResponseDelegate); 109 function->set_test_delegate(response_delegate_.get()); 110 scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args)); 111 EXPECT_TRUE(parsed_args.get()) << 112 "Could not parse extension function arguments: " << args; 113 function->SetArgs(parsed_args.get()); 114 115 if (!function->GetExtension()) { 116 scoped_refptr<Extension> empty_extension( 117 utils::CreateEmptyExtension()); 118 function->set_extension(empty_extension.get()); 119 } 120 121 function->set_browser_context(browser()->profile()); 122 function->set_has_callback(true); 123 function->Run()->Execute(); 124 } 125 126 std::string WaitForError(UIThreadExtensionFunction* function) { 127 RunMessageLoopUntilResponse(); 128 EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; 129 return function->GetError(); 130 } 131 132 base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) { 133 RunMessageLoopUntilResponse(); 134 EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " 135 << function->GetError(); 136 const base::Value* single_result = NULL; 137 if (function->GetResultList() != NULL && 138 function->GetResultList()->Get(0, &single_result)) { 139 return single_result->DeepCopy(); 140 } 141 return NULL; 142 } 143 144 private: 145 void RunMessageLoopUntilResponse() { 146 // If the RunAsync of |function| didn't already call SendResponse, run the 147 // message loop until they do. 148 if (!response_delegate_->HasResponse()) { 149 response_delegate_->set_should_post_quit(true); 150 content::RunMessageLoop(); 151 } 152 EXPECT_TRUE(response_delegate_->HasResponse()); 153 } 154 155 scoped_ptr<SendResponseDelegate> response_delegate_; 156}; 157 158class TestHangOAuth2MintTokenFlow : public OAuth2MintTokenFlow { 159 public: 160 TestHangOAuth2MintTokenFlow() 161 : OAuth2MintTokenFlow(NULL, NULL, OAuth2MintTokenFlow::Parameters()) {} 162 163 virtual void Start() OVERRIDE { 164 // Do nothing, simulating a hanging network call. 165 } 166}; 167 168class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow { 169 public: 170 enum ResultType { 171 ISSUE_ADVICE_SUCCESS, 172 MINT_TOKEN_SUCCESS, 173 MINT_TOKEN_FAILURE, 174 MINT_TOKEN_BAD_CREDENTIALS, 175 MINT_TOKEN_SERVICE_ERROR 176 }; 177 178 TestOAuth2MintTokenFlow(ResultType result, 179 OAuth2MintTokenFlow::Delegate* delegate) 180 : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()), 181 result_(result), 182 delegate_(delegate) { 183 } 184 185 virtual void Start() OVERRIDE { 186 switch (result_) { 187 case ISSUE_ADVICE_SUCCESS: { 188 IssueAdviceInfo info; 189 delegate_->OnIssueAdviceSuccess(info); 190 break; 191 } 192 case MINT_TOKEN_SUCCESS: { 193 delegate_->OnMintTokenSuccess(kAccessToken, 3600); 194 break; 195 } 196 case MINT_TOKEN_FAILURE: { 197 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 198 delegate_->OnMintTokenFailure(error); 199 break; 200 } 201 case MINT_TOKEN_BAD_CREDENTIALS: { 202 GoogleServiceAuthError error( 203 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 204 delegate_->OnMintTokenFailure(error); 205 break; 206 } 207 case MINT_TOKEN_SERVICE_ERROR: { 208 GoogleServiceAuthError error = 209 GoogleServiceAuthError::FromServiceError("invalid_scope"); 210 delegate_->OnMintTokenFailure(error); 211 break; 212 } 213 } 214 } 215 216 private: 217 ResultType result_; 218 OAuth2MintTokenFlow::Delegate* delegate_; 219}; 220 221// Waits for a specific GURL to generate a NOTIFICATION_LOAD_STOP event and 222// saves a pointer to the window embedding the WebContents, which can be later 223// closed. 224class WaitForGURLAndCloseWindow : public content::WindowedNotificationObserver { 225 public: 226 explicit WaitForGURLAndCloseWindow(GURL url) 227 : WindowedNotificationObserver( 228 content::NOTIFICATION_LOAD_STOP, 229 content::NotificationService::AllSources()), 230 url_(url) {} 231 232 // NotificationObserver: 233 virtual void Observe(int type, 234 const content::NotificationSource& source, 235 const content::NotificationDetails& details) OVERRIDE { 236 content::NavigationController* web_auth_flow_controller = 237 content::Source<content::NavigationController>(source).ptr(); 238 content::WebContents* web_contents = 239 web_auth_flow_controller->GetWebContents(); 240 241 if (web_contents->GetURL() == url_) { 242 // It is safe to keep the pointer here, because we know in a test, that 243 // the WebContents won't go away before CloseEmbedderWebContents is 244 // called. Don't copy this code to production. 245 GuestViewBase* guest = GuestViewBase::FromWebContents(web_contents); 246 embedder_web_contents_ = guest->embedder_web_contents(); 247 // Condtionally invoke parent class so that Wait will not exit 248 // until the target URL arrives. 249 content::WindowedNotificationObserver::Observe(type, source, details); 250 } 251 } 252 253 // Closes the window embedding the WebContents. The action is separated from 254 // the Observe method to make sure the list of observers is not deleted, 255 // while some event is already being processed. (That causes ASAN failures.) 256 void CloseEmbedderWebContents() { 257 if (embedder_web_contents_) 258 embedder_web_contents_->Close(); 259 } 260 261 private: 262 GURL url_; 263 content::WebContents* embedder_web_contents_; 264}; 265 266} // namespace 267 268class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction { 269 public: 270 MockGetAuthTokenFunction() : login_access_token_result_(true), 271 login_ui_result_(true), 272 scope_ui_result_(true), 273 login_ui_shown_(false), 274 scope_ui_shown_(false) { 275 } 276 277 void set_login_access_token_result(bool result) { 278 login_access_token_result_ = result; 279 } 280 281 void set_login_ui_result(bool result) { 282 login_ui_result_ = result; 283 } 284 285 void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) { 286 scope_ui_result_ = false; 287 scope_ui_failure_ = failure; 288 } 289 290 void set_scope_ui_oauth_error(const std::string& oauth_error) { 291 scope_ui_result_ = false; 292 scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR; 293 scope_ui_oauth_error_ = oauth_error; 294 } 295 296 bool login_ui_shown() const { 297 return login_ui_shown_; 298 } 299 300 bool scope_ui_shown() const { 301 return scope_ui_shown_; 302 } 303 304 virtual void StartLoginAccessTokenRequest() OVERRIDE { 305 if (login_access_token_result_) { 306 OnGetTokenSuccess(login_token_request_.get(), "access_token", 307 base::Time::Now() + base::TimeDelta::FromHours(1LL)); 308 } else { 309 GoogleServiceAuthError error( 310 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 311 OnGetTokenFailure(login_token_request_.get(), error); 312 } 313 } 314 315 virtual void ShowLoginPopup() OVERRIDE { 316 EXPECT_FALSE(login_ui_shown_); 317 login_ui_shown_ = true; 318 if (login_ui_result_) 319 SigninSuccess(); 320 else 321 SigninFailed(); 322 } 323 324 virtual void ShowOAuthApprovalDialog( 325 const IssueAdviceInfo& issue_advice) OVERRIDE { 326 scope_ui_shown_ = true; 327 328 if (scope_ui_result_) { 329 OnGaiaFlowCompleted(kAccessToken, "3600"); 330 } else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) { 331 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 332 OnGaiaFlowFailure(scope_ui_failure_, error, ""); 333 } else { 334 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 335 OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_); 336 } 337 } 338 339 MOCK_CONST_METHOD0(HasLoginToken, bool()); 340 MOCK_METHOD1(CreateMintTokenFlow, 341 OAuth2MintTokenFlow* (const std::string& login_access_token)); 342 343 private: 344 ~MockGetAuthTokenFunction() {} 345 bool login_access_token_result_; 346 bool login_ui_result_; 347 bool scope_ui_result_; 348 GaiaWebAuthFlow::Failure scope_ui_failure_; 349 std::string scope_ui_oauth_error_; 350 bool login_ui_shown_; 351 bool scope_ui_shown_; 352}; 353 354// TODO(courage): Replace MockGetAuthTokenFunction with 355// FakeGetAuthTokenFunction in all tests. 356 357class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction { 358 public: 359 FakeGetAuthTokenFunction() 360 : login_access_token_result_(true), 361 login_ui_result_(true), 362 scope_ui_result_(true), 363 login_ui_shown_(false), 364 scope_ui_shown_(false) {} 365 366 void set_login_access_token_result(bool result) { 367 login_access_token_result_ = result; 368 } 369 370 void set_login_ui_result(bool result) { login_ui_result_ = result; } 371 372 void set_mint_token_flow(scoped_ptr<OAuth2MintTokenFlow> flow) { 373 flow_ = flow.Pass(); 374 } 375 376 void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) { 377 scope_ui_result_ = false; 378 scope_ui_failure_ = failure; 379 } 380 381 void set_scope_ui_oauth_error(const std::string& oauth_error) { 382 scope_ui_result_ = false; 383 scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR; 384 scope_ui_oauth_error_ = oauth_error; 385 } 386 387 bool login_ui_shown() const { return login_ui_shown_; } 388 389 bool scope_ui_shown() const { return scope_ui_shown_; } 390 391 std::string login_access_token() const { return login_access_token_; } 392 393 virtual void ShowLoginPopup() OVERRIDE { 394 EXPECT_FALSE(login_ui_shown_); 395 login_ui_shown_ = true; 396 if (login_ui_result_) 397 SigninSuccess(); 398 else 399 SigninFailed(); 400 } 401 402 virtual void ShowOAuthApprovalDialog( 403 const IssueAdviceInfo& issue_advice) OVERRIDE { 404 scope_ui_shown_ = true; 405 406 if (scope_ui_result_) { 407 OnGaiaFlowCompleted(kAccessToken, "3600"); 408 } else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) { 409 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 410 OnGaiaFlowFailure(scope_ui_failure_, error, ""); 411 } else { 412 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 413 OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_); 414 } 415 } 416 417 virtual OAuth2MintTokenFlow* CreateMintTokenFlow( 418 const std::string& login_access_token) OVERRIDE { 419 EXPECT_TRUE(login_access_token_.empty()); 420 login_access_token_ = login_access_token; 421 return flow_.release(); 422 } 423 424 private: 425 virtual ~FakeGetAuthTokenFunction() {} 426 bool login_access_token_result_; 427 bool login_ui_result_; 428 bool scope_ui_result_; 429 GaiaWebAuthFlow::Failure scope_ui_failure_; 430 std::string scope_ui_oauth_error_; 431 bool login_ui_shown_; 432 bool scope_ui_shown_; 433 434 scoped_ptr<OAuth2MintTokenFlow> flow_; 435 436 std::string login_access_token_; 437}; 438 439class MockQueuedMintRequest : public IdentityMintRequestQueue::Request { 440 public: 441 MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType)); 442}; 443 444AccountIds CreateIds(std::string email, std::string obfid) { 445 AccountIds ids; 446 ids.account_key = email; 447 ids.email = email; 448 ids.gaia = obfid; 449 return ids; 450} 451 452class IdentityGetAccountsFunctionTest : public ExtensionBrowserTest { 453 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 454 ExtensionBrowserTest::SetUpCommandLine(command_line); 455 command_line->AppendSwitch(switches::kExtensionsMultiAccount); 456 } 457 458 protected: 459 void SetAccountState(AccountIds ids, bool is_signed_in) { 460 IdentityAPI::GetFactoryInstance()->Get(profile())->SetAccountStateForTest( 461 ids, is_signed_in); 462 } 463 464 testing::AssertionResult ExpectGetAccounts( 465 const std::vector<std::string>& accounts) { 466 scoped_refptr<IdentityGetAccountsFunction> func( 467 new IdentityGetAccountsFunction); 468 func->set_extension(utils::CreateEmptyExtension(kExtensionId).get()); 469 if (!utils::RunFunction( 470 func.get(), std::string("[]"), browser(), utils::NONE)) { 471 return GenerateFailureResult(accounts, NULL) 472 << "getAccounts did not return a result."; 473 } 474 const base::ListValue* callback_arguments = func->GetResultList(); 475 if (!callback_arguments) 476 return GenerateFailureResult(accounts, NULL) << "NULL result"; 477 478 if (callback_arguments->GetSize() != 1) { 479 return GenerateFailureResult(accounts, NULL) 480 << "Expected 1 argument but got " << callback_arguments->GetSize(); 481 } 482 483 const base::ListValue* results; 484 if (!callback_arguments->GetList(0, &results)) 485 GenerateFailureResult(accounts, NULL) << "Result was not an array"; 486 487 std::set<std::string> result_ids; 488 for (base::ListValue::const_iterator it = results->begin(); 489 it != results->end(); 490 ++it) { 491 scoped_ptr<api::identity::AccountInfo> info = 492 api::identity::AccountInfo::FromValue(**it); 493 if (info.get()) 494 result_ids.insert(info->id); 495 else 496 return GenerateFailureResult(accounts, results); 497 } 498 499 for (std::vector<std::string>::const_iterator it = accounts.begin(); 500 it != accounts.end(); 501 ++it) { 502 if (result_ids.find(*it) == result_ids.end()) 503 return GenerateFailureResult(accounts, results); 504 } 505 506 return testing::AssertionResult(true); 507 } 508 509 testing::AssertionResult GenerateFailureResult( 510 const ::std::vector<std::string>& accounts, 511 const base::ListValue* results) { 512 testing::Message msg("Expected: "); 513 for (std::vector<std::string>::const_iterator it = accounts.begin(); 514 it != accounts.end(); 515 ++it) { 516 msg << *it << " "; 517 } 518 msg << "Actual: "; 519 if (!results) { 520 msg << "NULL"; 521 } else { 522 for (base::ListValue::const_iterator it = results->begin(); 523 it != results->end(); 524 ++it) { 525 scoped_ptr<api::identity::AccountInfo> info = 526 api::identity::AccountInfo::FromValue(**it); 527 if (info.get()) 528 msg << info->id << " "; 529 else 530 msg << *it << "<-" << (*it)->GetType() << " "; 531 } 532 } 533 534 return testing::AssertionFailure(msg); 535 } 536}; 537 538IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, MultiAccountOn) { 539 EXPECT_TRUE(switches::IsExtensionsMultiAccount()); 540} 541 542IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoneSignedIn) { 543 EXPECT_TRUE(ExpectGetAccounts(std::vector<std::string>())); 544} 545 546IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, 547 PrimaryAccountSignedIn) { 548 SetAccountState(CreateIds("primary@example.com", "1"), true); 549 std::vector<std::string> primary; 550 primary.push_back("1"); 551 EXPECT_TRUE(ExpectGetAccounts(primary)); 552} 553 554IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, TwoAccountsSignedIn) { 555 SetAccountState(CreateIds("primary@example.com", "1"), true); 556 SetAccountState(CreateIds("secondary@example.com", "2"), true); 557 std::vector<std::string> two_accounts; 558 two_accounts.push_back("1"); 559 two_accounts.push_back("2"); 560 EXPECT_TRUE(ExpectGetAccounts(two_accounts)); 561} 562 563class IdentityOldProfilesGetAccountsFunctionTest 564 : public IdentityGetAccountsFunctionTest { 565 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 566 // Don't add the multi-account switch that parent class would have. 567 } 568}; 569 570IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest, 571 MultiAccountOff) { 572 EXPECT_FALSE(switches::IsExtensionsMultiAccount()); 573} 574 575IN_PROC_BROWSER_TEST_F(IdentityOldProfilesGetAccountsFunctionTest, 576 TwoAccountsSignedIn) { 577 SetAccountState(CreateIds("primary@example.com", "1"), true); 578 SetAccountState(CreateIds("secondary@example.com", "2"), true); 579 std::vector<std::string> only_primary; 580 only_primary.push_back("1"); 581 EXPECT_TRUE(ExpectGetAccounts(only_primary)); 582} 583 584class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest { 585 public: 586 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 587 AsyncExtensionBrowserTest::SetUpCommandLine(command_line); 588 command_line->AppendSwitch(switches::kExtensionsMultiAccount); 589 } 590 591 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 592 AsyncExtensionBrowserTest::SetUpInProcessBrowserTestFixture(); 593 594 will_create_browser_context_services_subscription_ = 595 BrowserContextDependencyManager::GetInstance() 596 ->RegisterWillCreateBrowserContextServicesCallbackForTesting( 597 base::Bind(&GetAuthTokenFunctionTest:: 598 OnWillCreateBrowserContextServices, 599 base::Unretained(this))) 600 .Pass(); 601 } 602 603 void OnWillCreateBrowserContextServices(content::BrowserContext* context) { 604 // Replace the signin manager and token service with fakes. Do this ahead of 605 // creating the browser so that a bunch of classes don't register as 606 // observers and end up needing to unregister when the fake is substituted. 607 SigninManagerFactory::GetInstance()->SetTestingFactory( 608 context, &FakeSigninManagerBase::Build); 609 ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory( 610 context, &BuildFakeProfileOAuth2TokenService); 611 } 612 613 virtual void SetUpOnMainThread() OVERRIDE { 614 AsyncExtensionBrowserTest::SetUpOnMainThread(); 615 616 // Grab references to the fake signin manager and token service. 617 signin_manager_ = static_cast<FakeSigninManagerForTesting*>( 618 SigninManagerFactory::GetInstance()->GetForProfile(profile())); 619 ASSERT_TRUE(signin_manager_); 620 token_service_ = static_cast<FakeProfileOAuth2TokenService*>( 621 ProfileOAuth2TokenServiceFactory::GetInstance()->GetForProfile( 622 profile())); 623 ASSERT_TRUE(token_service_); 624 } 625 626 void SignIn(const std::string account_key) { 627#if defined(OS_CHROMEOS) 628 signin_manager_->SetAuthenticatedUsername(account_key); 629#else 630 signin_manager_->SignIn(account_key, "password"); 631#endif 632 } 633 634 void SetAccountState(AccountIds ids, bool is_signed_in) { 635 IdentityAPI::GetFactoryInstance()->Get(profile())->SetAccountStateForTest( 636 ids, is_signed_in); 637 } 638 639 protected: 640 enum OAuth2Fields { 641 NONE = 0, 642 CLIENT_ID = 1, 643 SCOPES = 2, 644 AS_COMPONENT = 4 645 }; 646 647 FakeSigninManagerForTesting* signin_manager_; 648 FakeProfileOAuth2TokenService* token_service_; 649 650 virtual ~GetAuthTokenFunctionTest() {} 651 652 // Helper to create an extension with specific OAuth2Info fields set. 653 // |fields_to_set| should be computed by using fields of Oauth2Fields enum. 654 const Extension* CreateExtension(int fields_to_set) { 655 const Extension* ext; 656 base::FilePath manifest_path = 657 test_data_dir_.AppendASCII("platform_apps/oauth2"); 658 base::FilePath component_manifest_path = 659 test_data_dir_.AppendASCII("packaged_app/component_oauth2"); 660 if ((fields_to_set & AS_COMPONENT) == 0) 661 ext = LoadExtension(manifest_path); 662 else 663 ext = LoadExtensionAsComponent(component_manifest_path); 664 OAuth2Info& oauth2_info = 665 const_cast<OAuth2Info&>(OAuth2Info::GetOAuth2Info(ext)); 666 if ((fields_to_set & CLIENT_ID) != 0) 667 oauth2_info.client_id = "client1"; 668 if ((fields_to_set & SCOPES) != 0) { 669 oauth2_info.scopes.push_back("scope1"); 670 oauth2_info.scopes.push_back("scope2"); 671 } 672 673 extension_id_ = ext->id(); 674 oauth_scopes_ = std::set<std::string>(oauth2_info.scopes.begin(), 675 oauth2_info.scopes.end()); 676 return ext; 677 } 678 679 IdentityAPI* id_api() { 680 return IdentityAPI::GetFactoryInstance()->Get(browser()->profile()); 681 } 682 683 const std::string GetPrimaryAccountId() { 684 SigninManagerBase* signin_manager = 685 SigninManagerFactory::GetForProfile(browser()->profile()); 686 return signin_manager->GetAuthenticatedAccountId(); 687 } 688 689 void SetCachedToken(const IdentityTokenCacheValue& token_data) { 690 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 691 id_api()->SetCachedToken(key, token_data); 692 } 693 694 const IdentityTokenCacheValue& GetCachedToken(std::string account_id) { 695 if (account_id.empty()) 696 account_id = GetPrimaryAccountId(); 697 ExtensionTokenKey key(extension_id_, account_id, oauth_scopes_); 698 return id_api()->GetCachedToken(key); 699 } 700 701 void QueueRequestStart(IdentityMintRequestQueue::MintType type, 702 IdentityMintRequestQueue::Request* request) { 703 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 704 id_api()->mint_queue()->RequestStart(type, key, request); 705 } 706 707 void QueueRequestComplete(IdentityMintRequestQueue::MintType type, 708 IdentityMintRequestQueue::Request* request) { 709 ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_); 710 id_api()->mint_queue()->RequestComplete(type, key, request); 711 } 712 713 private: 714 std::string extension_id_; 715 std::set<std::string> oauth_scopes_; 716 717 scoped_ptr<base::CallbackList<void(content::BrowserContext*)>::Subscription> 718 will_create_browser_context_services_subscription_; 719}; 720 721IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 722 NoClientId) { 723 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 724 func->set_extension(CreateExtension(SCOPES)); 725 std::string error = utils::RunFunctionAndReturnError( 726 func.get(), "[{}]", browser()); 727 EXPECT_EQ(std::string(errors::kInvalidClientId), error); 728 EXPECT_FALSE(func->login_ui_shown()); 729 EXPECT_FALSE(func->scope_ui_shown()); 730} 731 732IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 733 NoScopes) { 734 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 735 func->set_extension(CreateExtension(CLIENT_ID)); 736 std::string error = utils::RunFunctionAndReturnError( 737 func.get(), "[{}]", browser()); 738 EXPECT_EQ(std::string(errors::kInvalidScopes), error); 739 EXPECT_FALSE(func->login_ui_shown()); 740 EXPECT_FALSE(func->scope_ui_shown()); 741} 742 743IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 744 NonInteractiveNotSignedIn) { 745 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 746 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 747 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 748 std::string error = utils::RunFunctionAndReturnError( 749 func.get(), "[{}]", browser()); 750 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 751 EXPECT_FALSE(func->login_ui_shown()); 752 EXPECT_FALSE(func->scope_ui_shown()); 753} 754 755IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 756 NonInteractiveMintFailure) { 757 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 758 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 759 EXPECT_CALL(*func.get(), HasLoginToken()) 760 .WillOnce(Return(true)); 761 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 762 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 763 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 764 std::string error = utils::RunFunctionAndReturnError( 765 func.get(), "[{}]", browser()); 766 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 767 EXPECT_FALSE(func->login_ui_shown()); 768 EXPECT_FALSE(func->scope_ui_shown()); 769} 770 771IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 772 NonInteractiveLoginAccessTokenFailure) { 773 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 774 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 775 EXPECT_CALL(*func.get(), HasLoginToken()) 776 .WillOnce(Return(true)); 777 func->set_login_access_token_result(false); 778 std::string error = utils::RunFunctionAndReturnError( 779 func.get(), "[{}]", browser()); 780 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 781} 782 783IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 784 NonInteractiveMintAdviceSuccess) { 785 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 786 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 787 func->set_extension(extension.get()); 788 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 789 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 790 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 791 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 792 std::string error = utils::RunFunctionAndReturnError( 793 func.get(), "[{}]", browser()); 794 EXPECT_EQ(std::string(errors::kNoGrant), error); 795 EXPECT_FALSE(func->login_ui_shown()); 796 EXPECT_FALSE(func->scope_ui_shown()); 797 798 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 799 GetCachedToken(std::string()).status()); 800} 801 802IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 803 NonInteractiveMintBadCredentials) { 804 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 805 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 806 EXPECT_CALL(*func.get(), HasLoginToken()) 807 .WillOnce(Return(true)); 808 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 809 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 810 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 811 std::string error = utils::RunFunctionAndReturnError( 812 func.get(), "[{}]", browser()); 813 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 814 EXPECT_FALSE(func->login_ui_shown()); 815 EXPECT_FALSE(func->scope_ui_shown()); 816 817 EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, 818 id_api()->GetAuthStatusForTest().state()); 819} 820 821IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 822 NonInteractiveMintServiceError) { 823 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 824 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 825 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 826 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 827 TestOAuth2MintTokenFlow::MINT_TOKEN_SERVICE_ERROR, func.get()); 828 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 829 std::string error = 830 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 831 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 832 EXPECT_FALSE(func->login_ui_shown()); 833 EXPECT_FALSE(func->scope_ui_shown()); 834 835 EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), 836 id_api()->GetAuthStatusForTest()); 837} 838 839IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 840 NonInteractiveSuccess) { 841#if defined(OS_WIN) && defined(USE_ASH) 842 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 843 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 844 return; 845#endif 846 847 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 848 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 849 func->set_extension(extension.get()); 850 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 851 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 852 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 853 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 854 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 855 func.get(), "[{}]", browser())); 856 std::string access_token; 857 EXPECT_TRUE(value->GetAsString(&access_token)); 858 EXPECT_EQ(std::string(kAccessToken), access_token); 859 EXPECT_FALSE(func->login_ui_shown()); 860 EXPECT_FALSE(func->scope_ui_shown()); 861 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 862 GetCachedToken(std::string()).status()); 863} 864 865IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 866 InteractiveLoginCanceled) { 867 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 868 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 869 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 870 func->set_login_ui_result(false); 871 std::string error = utils::RunFunctionAndReturnError( 872 func.get(), "[{\"interactive\": true}]", browser()); 873 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 874 EXPECT_TRUE(func->login_ui_shown()); 875 EXPECT_FALSE(func->scope_ui_shown()); 876} 877 878IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 879 InteractiveMintBadCredentialsLoginCanceled) { 880 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 881 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 882 EXPECT_CALL(*func.get(), HasLoginToken()) 883 .WillOnce(Return(true)); 884 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 885 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 886 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 887 func->set_login_ui_result(false); 888 std::string error = utils::RunFunctionAndReturnError( 889 func.get(), "[{\"interactive\": true}]", browser()); 890 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 891 EXPECT_TRUE(func->login_ui_shown()); 892 EXPECT_FALSE(func->scope_ui_shown()); 893} 894 895IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 896 InteractiveLoginSuccessNoToken) { 897 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 898 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 899 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 900 func->set_login_ui_result(false); 901 std::string error = utils::RunFunctionAndReturnError( 902 func.get(), "[{\"interactive\": true}]", browser()); 903 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 904 EXPECT_TRUE(func->login_ui_shown()); 905 EXPECT_FALSE(func->scope_ui_shown()); 906} 907 908IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 909 InteractiveLoginSuccessMintFailure) { 910 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 911 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 912 EXPECT_CALL(*func.get(), HasLoginToken()) 913 .WillOnce(Return(false)); 914 func->set_login_ui_result(true); 915 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 916 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 917 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 918 std::string error = utils::RunFunctionAndReturnError( 919 func.get(), "[{\"interactive\": true}]", browser()); 920 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 921 EXPECT_TRUE(func->login_ui_shown()); 922 EXPECT_FALSE(func->scope_ui_shown()); 923} 924 925IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 926 InteractiveLoginSuccessLoginAccessTokenFailure) { 927 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 928 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 929 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 930 func->set_login_ui_result(true); 931 func->set_login_access_token_result(false); 932 std::string error = utils::RunFunctionAndReturnError( 933 func.get(), "[{\"interactive\": true}]", browser()); 934 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 935 EXPECT_TRUE(func->login_ui_shown()); 936 EXPECT_FALSE(func->scope_ui_shown()); 937} 938 939IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 940 InteractiveLoginSuccessMintSuccess) { 941 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 942 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 943 EXPECT_CALL(*func.get(), HasLoginToken()) 944 .WillOnce(Return(false)); 945 func->set_login_ui_result(true); 946 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 947 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 948 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 949 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 950 func.get(), "[{\"interactive\": true}]", browser())); 951 std::string access_token; 952 EXPECT_TRUE(value->GetAsString(&access_token)); 953 EXPECT_EQ(std::string(kAccessToken), access_token); 954 EXPECT_TRUE(func->login_ui_shown()); 955 EXPECT_FALSE(func->scope_ui_shown()); 956} 957 958IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 959 InteractiveLoginSuccessApprovalAborted) { 960 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 961 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 962 EXPECT_CALL(*func.get(), HasLoginToken()) 963 .WillOnce(Return(false)); 964 func->set_login_ui_result(true); 965 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 966 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 967 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 968 func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED); 969 std::string error = utils::RunFunctionAndReturnError( 970 func.get(), "[{\"interactive\": true}]", browser()); 971 EXPECT_EQ(std::string(errors::kUserRejected), error); 972 EXPECT_TRUE(func->login_ui_shown()); 973 EXPECT_TRUE(func->scope_ui_shown()); 974} 975 976IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 977 InteractiveLoginSuccessApprovalSuccess) { 978 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 979 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 980 func->set_extension(extension.get()); 981 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 982 func->set_login_ui_result(true); 983 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 984 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 985 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 986 .WillOnce(Return(flow)); 987 988 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 989 func.get(), "[{\"interactive\": true}]", browser())); 990 std::string access_token; 991 EXPECT_TRUE(value->GetAsString(&access_token)); 992 EXPECT_EQ(std::string(kAccessToken), access_token); 993 EXPECT_TRUE(func->login_ui_shown()); 994 EXPECT_TRUE(func->scope_ui_shown()); 995} 996 997IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 998 InteractiveApprovalAborted) { 999 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1000 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 1001 EXPECT_CALL(*func.get(), HasLoginToken()) 1002 .WillOnce(Return(true)); 1003 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1004 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1005 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1006 func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED); 1007 std::string error = utils::RunFunctionAndReturnError( 1008 func.get(), "[{\"interactive\": true}]", browser()); 1009 EXPECT_EQ(std::string(errors::kUserRejected), error); 1010 EXPECT_FALSE(func->login_ui_shown()); 1011 EXPECT_TRUE(func->scope_ui_shown()); 1012} 1013 1014IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1015 InteractiveApprovalLoadFailed) { 1016 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1017 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 1018 EXPECT_CALL(*func.get(), HasLoginToken()) 1019 .WillOnce(Return(true)); 1020 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1021 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1022 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1023 func->set_scope_ui_failure(GaiaWebAuthFlow::LOAD_FAILED); 1024 std::string error = utils::RunFunctionAndReturnError( 1025 func.get(), "[{\"interactive\": true}]", browser()); 1026 EXPECT_EQ(std::string(errors::kPageLoadFailure), error); 1027 EXPECT_FALSE(func->login_ui_shown()); 1028 EXPECT_TRUE(func->scope_ui_shown()); 1029} 1030 1031IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1032 InteractiveApprovalInvalidRedirect) { 1033 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1034 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 1035 EXPECT_CALL(*func.get(), HasLoginToken()) 1036 .WillOnce(Return(true)); 1037 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1038 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1039 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1040 func->set_scope_ui_failure(GaiaWebAuthFlow::INVALID_REDIRECT); 1041 std::string error = utils::RunFunctionAndReturnError( 1042 func.get(), "[{\"interactive\": true}]", browser()); 1043 EXPECT_EQ(std::string(errors::kInvalidRedirect), error); 1044 EXPECT_FALSE(func->login_ui_shown()); 1045 EXPECT_TRUE(func->scope_ui_shown()); 1046} 1047 1048IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1049 InteractiveApprovalConnectionFailure) { 1050 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1051 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 1052 EXPECT_CALL(*func.get(), HasLoginToken()) 1053 .WillOnce(Return(true)); 1054 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1055 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1056 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1057 func->set_scope_ui_failure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR); 1058 std::string error = utils::RunFunctionAndReturnError( 1059 func.get(), "[{\"interactive\": true}]", browser()); 1060 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 1061 EXPECT_FALSE(func->login_ui_shown()); 1062 EXPECT_TRUE(func->scope_ui_shown()); 1063} 1064 1065IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1066 InteractiveApprovalOAuthErrors) { 1067 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1068 1069 std::map<std::string, std::string> error_map; 1070 error_map.insert(std::make_pair("access_denied", errors::kUserRejected)); 1071 error_map.insert(std::make_pair("invalid_scope", errors::kInvalidScopes)); 1072 error_map.insert(std::make_pair( 1073 "unmapped_error", std::string(errors::kAuthFailure) + "unmapped_error")); 1074 1075 for (std::map<std::string, std::string>::const_iterator 1076 it = error_map.begin(); 1077 it != error_map.end(); 1078 ++it) { 1079 scoped_refptr<MockGetAuthTokenFunction> func( 1080 new MockGetAuthTokenFunction()); 1081 func->set_extension(extension.get()); 1082 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1083 // Make sure we don't get a cached issue_advice result, which would cause 1084 // flow to be leaked. 1085 id_api()->EraseAllCachedTokens(); 1086 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1087 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1088 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1089 func->set_scope_ui_oauth_error(it->first); 1090 std::string error = utils::RunFunctionAndReturnError( 1091 func.get(), "[{\"interactive\": true}]", browser()); 1092 EXPECT_EQ(it->second, error); 1093 EXPECT_FALSE(func->login_ui_shown()); 1094 EXPECT_TRUE(func->scope_ui_shown()); 1095 } 1096} 1097 1098IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1099 InteractiveApprovalSuccess) { 1100 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1101 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1102 func->set_extension(extension.get()); 1103 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1104 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1105 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1106 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 1107 .WillOnce(Return(flow)); 1108 1109 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1110 func.get(), "[{\"interactive\": true}]", browser())); 1111 std::string access_token; 1112 EXPECT_TRUE(value->GetAsString(&access_token)); 1113 EXPECT_EQ(std::string(kAccessToken), access_token); 1114 EXPECT_FALSE(func->login_ui_shown()); 1115 EXPECT_TRUE(func->scope_ui_shown()); 1116 1117 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1118 GetCachedToken(std::string()).status()); 1119} 1120 1121IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) { 1122 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1123 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1124 func->set_extension(extension.get()); 1125 1126 // Create a fake request to block the queue. 1127 MockQueuedMintRequest queued_request; 1128 IdentityMintRequestQueue::MintType type = 1129 IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE; 1130 1131 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 1132 QueueRequestStart(type, &queued_request); 1133 1134 // The real request will start processing, but wait in the queue behind 1135 // the blocker. 1136 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1137 RunFunctionAsync(func.get(), "[{}]"); 1138 // Verify that we have fetched the login token at this point. 1139 testing::Mock::VerifyAndClearExpectations(func.get()); 1140 1141 // The flow will be created after the first queued request clears. 1142 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1143 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 1144 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1145 1146 QueueRequestComplete(type, &queued_request); 1147 1148 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1149 std::string access_token; 1150 EXPECT_TRUE(value->GetAsString(&access_token)); 1151 EXPECT_EQ(std::string(kAccessToken), access_token); 1152 EXPECT_FALSE(func->login_ui_shown()); 1153 EXPECT_FALSE(func->scope_ui_shown()); 1154} 1155 1156IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) { 1157 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1158 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1159 func->set_extension(extension.get()); 1160 1161 // Create a fake request to block the queue. 1162 MockQueuedMintRequest queued_request; 1163 IdentityMintRequestQueue::MintType type = 1164 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 1165 1166 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 1167 QueueRequestStart(type, &queued_request); 1168 1169 // The real request will start processing, but wait in the queue behind 1170 // the blocker. 1171 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1172 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 1173 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1174 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)); 1175 RunFunctionAsync(func.get(), "[{\"interactive\": true}]"); 1176 // Verify that we have fetched the login token and run the first flow. 1177 testing::Mock::VerifyAndClearExpectations(func.get()); 1178 EXPECT_FALSE(func->scope_ui_shown()); 1179 1180 // The UI will be displayed and a token retrieved after the first 1181 // queued request clears. 1182 QueueRequestComplete(type, &queued_request); 1183 1184 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1185 std::string access_token; 1186 EXPECT_TRUE(value->GetAsString(&access_token)); 1187 EXPECT_EQ(std::string(kAccessToken), access_token); 1188 EXPECT_FALSE(func->login_ui_shown()); 1189 EXPECT_TRUE(func->scope_ui_shown()); 1190} 1191 1192IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueueShutdown) { 1193 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1194 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1195 func->set_extension(extension.get()); 1196 1197 // Create a fake request to block the queue. 1198 MockQueuedMintRequest queued_request; 1199 IdentityMintRequestQueue::MintType type = 1200 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 1201 1202 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 1203 QueueRequestStart(type, &queued_request); 1204 1205 // The real request will start processing, but wait in the queue behind 1206 // the blocker. 1207 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1208 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 1209 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1210 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)); 1211 RunFunctionAsync(func.get(), "[{\"interactive\": true}]"); 1212 // Verify that we have fetched the login token and run the first flow. 1213 testing::Mock::VerifyAndClearExpectations(func.get()); 1214 EXPECT_FALSE(func->scope_ui_shown()); 1215 1216 // After the request is canceled, the function will complete. 1217 func->OnShutdown(); 1218 EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get())); 1219 EXPECT_FALSE(func->login_ui_shown()); 1220 EXPECT_FALSE(func->scope_ui_shown()); 1221 1222 QueueRequestComplete(type, &queued_request); 1223} 1224 1225IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) { 1226 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1227 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1228 func->set_extension(extension.get()); 1229 1230 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1231 TestHangOAuth2MintTokenFlow* flow = new TestHangOAuth2MintTokenFlow(); 1232 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1233 RunFunctionAsync(func.get(), "[{\"interactive\": false}]"); 1234 1235 // After the request is canceled, the function will complete. 1236 func->OnShutdown(); 1237 EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get())); 1238} 1239 1240IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1241 InteractiveQueuedNoninteractiveFails) { 1242 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1243 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1244 func->set_extension(extension.get()); 1245 1246 // Create a fake request to block the interactive queue. 1247 MockQueuedMintRequest queued_request; 1248 IdentityMintRequestQueue::MintType type = 1249 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 1250 1251 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 1252 QueueRequestStart(type, &queued_request); 1253 1254 // Non-interactive requests fail without hitting GAIA, because a 1255 // consent UI is known to be up. 1256 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1257 std::string error = utils::RunFunctionAndReturnError( 1258 func.get(), "[{}]", browser()); 1259 EXPECT_EQ(std::string(errors::kNoGrant), error); 1260 EXPECT_FALSE(func->login_ui_shown()); 1261 EXPECT_FALSE(func->scope_ui_shown()); 1262 1263 QueueRequestComplete(type, &queued_request); 1264} 1265 1266IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1267 NonInteractiveCacheHit) { 1268 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1269 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1270 func->set_extension(extension.get()); 1271 1272 // pre-populate the cache with a token 1273 IdentityTokenCacheValue token(kAccessToken, 1274 base::TimeDelta::FromSeconds(3600)); 1275 SetCachedToken(token); 1276 1277 // Get a token. Should not require a GAIA request. 1278 EXPECT_CALL(*func.get(), HasLoginToken()) 1279 .WillOnce(Return(true)); 1280 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1281 func.get(), "[{}]", browser())); 1282 std::string access_token; 1283 EXPECT_TRUE(value->GetAsString(&access_token)); 1284 EXPECT_EQ(std::string(kAccessToken), access_token); 1285 EXPECT_FALSE(func->login_ui_shown()); 1286 EXPECT_FALSE(func->scope_ui_shown()); 1287} 1288 1289IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1290 NonInteractiveIssueAdviceCacheHit) { 1291 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1292 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1293 func->set_extension(extension.get()); 1294 1295 // pre-populate the cache with advice 1296 IssueAdviceInfo info; 1297 IdentityTokenCacheValue token(info); 1298 SetCachedToken(token); 1299 1300 // Should return an error without a GAIA request. 1301 EXPECT_CALL(*func.get(), HasLoginToken()) 1302 .WillOnce(Return(true)); 1303 std::string error = utils::RunFunctionAndReturnError( 1304 func.get(), "[{}]", browser()); 1305 EXPECT_EQ(std::string(errors::kNoGrant), error); 1306 EXPECT_FALSE(func->login_ui_shown()); 1307 EXPECT_FALSE(func->scope_ui_shown()); 1308} 1309 1310IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1311 InteractiveCacheHit) { 1312 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1313 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1314 func->set_extension(extension.get()); 1315 1316 // Create a fake request to block the queue. 1317 MockQueuedMintRequest queued_request; 1318 IdentityMintRequestQueue::MintType type = 1319 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 1320 1321 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 1322 QueueRequestStart(type, &queued_request); 1323 1324 // The real request will start processing, but wait in the queue behind 1325 // the blocker. 1326 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 1327 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1328 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1329 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 1330 RunFunctionAsync(func.get(), "[{\"interactive\": true}]"); 1331 1332 // Populate the cache with a token while the request is blocked. 1333 IdentityTokenCacheValue token(kAccessToken, 1334 base::TimeDelta::FromSeconds(3600)); 1335 SetCachedToken(token); 1336 1337 // When we wake up the request, it returns the cached token without 1338 // displaying a UI, or hitting GAIA. 1339 1340 QueueRequestComplete(type, &queued_request); 1341 1342 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1343 std::string access_token; 1344 EXPECT_TRUE(value->GetAsString(&access_token)); 1345 EXPECT_EQ(std::string(kAccessToken), access_token); 1346 EXPECT_FALSE(func->login_ui_shown()); 1347 EXPECT_FALSE(func->scope_ui_shown()); 1348} 1349 1350IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 1351 LoginInvalidatesTokenCache) { 1352 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1353 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1354 func->set_extension(extension.get()); 1355 1356 // pre-populate the cache with a token 1357 IdentityTokenCacheValue token(kAccessToken, 1358 base::TimeDelta::FromSeconds(3600)); 1359 SetCachedToken(token); 1360 1361 // Because the user is not signed in, the token will be removed, 1362 // and we'll hit GAIA for new tokens. 1363 EXPECT_CALL(*func.get(), HasLoginToken()) 1364 .WillOnce(Return(false)); 1365 func->set_login_ui_result(true); 1366 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 1367 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 1368 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 1369 .WillOnce(Return(flow)); 1370 1371 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1372 func.get(), "[{\"interactive\": true}]", browser())); 1373 std::string access_token; 1374 EXPECT_TRUE(value->GetAsString(&access_token)); 1375 EXPECT_EQ(std::string(kAccessToken), access_token); 1376 EXPECT_TRUE(func->login_ui_shown()); 1377 EXPECT_TRUE(func->scope_ui_shown()); 1378 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1379 GetCachedToken(std::string()).status()); 1380} 1381 1382IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) { 1383 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1384 scoped_refptr<const Extension> extension( 1385 CreateExtension(SCOPES | AS_COMPONENT)); 1386 func->set_extension(extension.get()); 1387 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 1388 EXPECT_TRUE(oauth2_info.client_id.empty()); 1389 EXPECT_FALSE(func->GetOAuth2ClientId().empty()); 1390 EXPECT_NE("client1", func->GetOAuth2ClientId()); 1391} 1392 1393IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) { 1394 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1395 scoped_refptr<const Extension> extension( 1396 CreateExtension(CLIENT_ID | SCOPES | AS_COMPONENT)); 1397 func->set_extension(extension.get()); 1398 EXPECT_EQ("client1", func->GetOAuth2ClientId()); 1399} 1400 1401IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiDefaultUser) { 1402 SignIn("primary@example.com"); 1403 token_service_->IssueRefreshTokenForUser("primary@example.com", 1404 "refresh_token"); 1405 SetAccountState(CreateIds("primary@example.com", "1"), true); 1406 SetAccountState(CreateIds("secondary@example.com", "2"), true); 1407 1408 scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); 1409 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1410 func->set_extension(extension.get()); 1411 func->set_mint_token_flow( 1412 scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow( 1413 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()))); 1414 1415 RunFunctionAsync(func.get(), "[{}]"); 1416 1417 token_service_->IssueAllTokensForAccount( 1418 "primary@example.com", 1419 "access_token-primary@example.com", 1420 base::Time::Now() + base::TimeDelta::FromSeconds(3600)); 1421 1422 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1423 std::string access_token; 1424 EXPECT_TRUE(value->GetAsString(&access_token)); 1425 EXPECT_EQ(std::string(kAccessToken), access_token); 1426 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1427 GetCachedToken(std::string()).status()); 1428 EXPECT_EQ("access_token-primary@example.com", func->login_access_token()); 1429} 1430 1431IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiPrimaryUser) { 1432 SignIn("primary@example.com"); 1433 token_service_->IssueRefreshTokenForUser("primary@example.com", 1434 "refresh_token"); 1435 SetAccountState(CreateIds("primary@example.com", "1"), true); 1436 SetAccountState(CreateIds("secondary@example.com", "2"), true); 1437 1438 scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); 1439 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1440 func->set_extension(extension.get()); 1441 func->set_mint_token_flow( 1442 scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow( 1443 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()))); 1444 1445 RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"1\" } }]"); 1446 1447 token_service_->IssueAllTokensForAccount( 1448 "primary@example.com", 1449 "access_token-primary@example.com", 1450 base::Time::Now() + base::TimeDelta::FromSeconds(3600)); 1451 1452 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1453 std::string access_token; 1454 EXPECT_TRUE(value->GetAsString(&access_token)); 1455 EXPECT_EQ(std::string(kAccessToken), access_token); 1456 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1457 GetCachedToken(std::string()).status()); 1458 EXPECT_EQ("access_token-primary@example.com", func->login_access_token()); 1459} 1460 1461IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, MultiSecondaryUser) { 1462 SignIn("primary@example.com"); 1463 token_service_->IssueRefreshTokenForUser("secondary@example.com", 1464 "refresh_token"); 1465 SetAccountState(CreateIds("primary@example.com", "1"), true); 1466 SetAccountState(CreateIds("secondary@example.com", "2"), true); 1467 1468 scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); 1469 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 1470 func->set_extension(extension.get()); 1471 func->set_mint_token_flow( 1472 scoped_ptr<OAuth2MintTokenFlow>(new TestOAuth2MintTokenFlow( 1473 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()))); 1474 1475 RunFunctionAsync(func.get(), "[{\"account\": { \"id\": \"2\" } }]"); 1476 1477 token_service_->IssueAllTokensForAccount( 1478 "secondary@example.com", 1479 "access_token-secondary@example.com", 1480 base::Time::Now() + base::TimeDelta::FromSeconds(3600)); 1481 1482 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 1483 std::string access_token; 1484 EXPECT_TRUE(value->GetAsString(&access_token)); 1485 EXPECT_EQ(std::string(kAccessToken), access_token); 1486 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1487 GetCachedToken("secondary@example.com").status()); 1488 EXPECT_EQ("access_token-secondary@example.com", func->login_access_token()); 1489} 1490 1491// TODO(courage): negative cases for secondary accounts 1492 1493class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest { 1494 protected: 1495 bool InvalidateDefaultToken() { 1496 scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func( 1497 new IdentityRemoveCachedAuthTokenFunction); 1498 func->set_extension(utils::CreateEmptyExtension(kExtensionId).get()); 1499 return utils::RunFunction( 1500 func.get(), 1501 std::string("[{\"token\": \"") + kAccessToken + "\"}]", 1502 browser(), 1503 extension_function_test_utils::NONE); 1504 } 1505 1506 IdentityAPI* id_api() { 1507 return IdentityAPI::GetFactoryInstance()->Get(browser()->profile()); 1508 } 1509 1510 void SetCachedToken(IdentityTokenCacheValue& token_data) { 1511 ExtensionTokenKey key(extensions::id_util::GenerateId(kExtensionId), 1512 "test@example.com", 1513 std::set<std::string>()); 1514 id_api()->SetCachedToken(key, token_data); 1515 } 1516 1517 const IdentityTokenCacheValue& GetCachedToken() { 1518 return id_api()->GetCachedToken( 1519 ExtensionTokenKey(extensions::id_util::GenerateId(kExtensionId), 1520 "test@example.com", 1521 std::set<std::string>())); 1522 } 1523}; 1524 1525IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) { 1526 EXPECT_TRUE(InvalidateDefaultToken()); 1527 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 1528 GetCachedToken().status()); 1529} 1530 1531IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) { 1532 IssueAdviceInfo info; 1533 IdentityTokenCacheValue advice(info); 1534 SetCachedToken(advice); 1535 EXPECT_TRUE(InvalidateDefaultToken()); 1536 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 1537 GetCachedToken().status()); 1538} 1539 1540IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) { 1541 IdentityTokenCacheValue token("non_matching_token", 1542 base::TimeDelta::FromSeconds(3600)); 1543 SetCachedToken(token); 1544 EXPECT_TRUE(InvalidateDefaultToken()); 1545 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1546 GetCachedToken().status()); 1547 EXPECT_EQ("non_matching_token", GetCachedToken().token()); 1548} 1549 1550IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) { 1551 IdentityTokenCacheValue token(kAccessToken, 1552 base::TimeDelta::FromSeconds(3600)); 1553 SetCachedToken(token); 1554 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1555 GetCachedToken().status()); 1556 EXPECT_TRUE(InvalidateDefaultToken()); 1557 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 1558 GetCachedToken().status()); 1559} 1560 1561class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest { 1562 public: 1563 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 1564 AsyncExtensionBrowserTest::SetUpCommandLine(command_line); 1565 // Reduce performance test variance by disabling background networking. 1566 command_line->AppendSwitch(switches::kDisableBackgroundNetworking); 1567 } 1568}; 1569 1570IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) { 1571 net::SpawnedTestServer https_server( 1572 net::SpawnedTestServer::TYPE_HTTPS, 1573 net::SpawnedTestServer::kLocalhost, 1574 base::FilePath(FILE_PATH_LITERAL( 1575 "chrome/test/data/extensions/api_test/identity"))); 1576 ASSERT_TRUE(https_server.Start()); 1577 GURL auth_url(https_server.GetURL("files/interaction_required.html")); 1578 1579 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1580 new IdentityLaunchWebAuthFlowFunction()); 1581 scoped_refptr<Extension> empty_extension( 1582 utils::CreateEmptyExtension()); 1583 function->set_extension(empty_extension.get()); 1584 1585 WaitForGURLAndCloseWindow popup_observer(auth_url); 1586 1587 std::string args = "[{\"interactive\": true, \"url\": \"" + 1588 auth_url.spec() + "\"}]"; 1589 RunFunctionAsync(function.get(), args); 1590 1591 popup_observer.Wait(); 1592 popup_observer.CloseEmbedderWebContents(); 1593 1594 EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get())); 1595} 1596 1597IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) { 1598 net::SpawnedTestServer https_server( 1599 net::SpawnedTestServer::TYPE_HTTPS, 1600 net::SpawnedTestServer::kLocalhost, 1601 base::FilePath(FILE_PATH_LITERAL( 1602 "chrome/test/data/extensions/api_test/identity"))); 1603 ASSERT_TRUE(https_server.Start()); 1604 GURL auth_url(https_server.GetURL("files/interaction_required.html")); 1605 1606 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1607 new IdentityLaunchWebAuthFlowFunction()); 1608 scoped_refptr<Extension> empty_extension( 1609 utils::CreateEmptyExtension()); 1610 function->set_extension(empty_extension.get()); 1611 1612 std::string args = "[{\"interactive\": false, \"url\": \"" + 1613 auth_url.spec() + "\"}]"; 1614 std::string error = 1615 utils::RunFunctionAndReturnError(function.get(), args, browser()); 1616 1617 EXPECT_EQ(std::string(errors::kInteractionRequired), error); 1618} 1619 1620IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) { 1621 net::SpawnedTestServer https_server( 1622 net::SpawnedTestServer::TYPE_HTTPS, 1623 net::SpawnedTestServer::kLocalhost, 1624 base::FilePath(FILE_PATH_LITERAL( 1625 "chrome/test/data/extensions/api_test/identity"))); 1626 ASSERT_TRUE(https_server.Start()); 1627 GURL auth_url(https_server.GetURL("files/five_hundred.html")); 1628 1629 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1630 new IdentityLaunchWebAuthFlowFunction()); 1631 scoped_refptr<Extension> empty_extension( 1632 utils::CreateEmptyExtension()); 1633 function->set_extension(empty_extension.get()); 1634 1635 std::string args = "[{\"interactive\": true, \"url\": \"" + 1636 auth_url.spec() + "\"}]"; 1637 std::string error = 1638 utils::RunFunctionAndReturnError(function.get(), args, browser()); 1639 1640 EXPECT_EQ(std::string(errors::kPageLoadFailure), error); 1641} 1642 1643IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) { 1644#if defined(OS_WIN) && defined(USE_ASH) 1645 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 1646 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 1647 return; 1648#endif 1649 1650 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1651 new IdentityLaunchWebAuthFlowFunction()); 1652 scoped_refptr<Extension> empty_extension( 1653 utils::CreateEmptyExtension()); 1654 function->set_extension(empty_extension.get()); 1655 1656 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1657 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1658 function.get(), 1659 "[{\"interactive\": false," 1660 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 1661 browser())); 1662 1663 std::string url; 1664 EXPECT_TRUE(value->GetAsString(&url)); 1665 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1666 url); 1667} 1668 1669IN_PROC_BROWSER_TEST_F( 1670 LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) { 1671#if defined(OS_WIN) && defined(USE_ASH) 1672 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 1673 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 1674 return; 1675#endif 1676 1677 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1678 new IdentityLaunchWebAuthFlowFunction()); 1679 scoped_refptr<Extension> empty_extension( 1680 utils::CreateEmptyExtension()); 1681 function->set_extension(empty_extension.get()); 1682 1683 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1684 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1685 function.get(), 1686 "[{\"interactive\": true," 1687 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 1688 browser())); 1689 1690 std::string url; 1691 EXPECT_TRUE(value->GetAsString(&url)); 1692 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1693 url); 1694} 1695 1696IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, 1697 DISABLED_InteractiveSecondNavigationSuccess) { 1698 net::SpawnedTestServer https_server( 1699 net::SpawnedTestServer::TYPE_HTTPS, 1700 net::SpawnedTestServer::kLocalhost, 1701 base::FilePath(FILE_PATH_LITERAL( 1702 "chrome/test/data/extensions/api_test/identity"))); 1703 ASSERT_TRUE(https_server.Start()); 1704 GURL auth_url(https_server.GetURL("files/redirect_to_chromiumapp.html")); 1705 1706 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1707 new IdentityLaunchWebAuthFlowFunction()); 1708 scoped_refptr<Extension> empty_extension( 1709 utils::CreateEmptyExtension()); 1710 function->set_extension(empty_extension.get()); 1711 1712 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1713 std::string args = "[{\"interactive\": true, \"url\": \"" + 1714 auth_url.spec() + "\"}]"; 1715 scoped_ptr<base::Value> value( 1716 utils::RunFunctionAndReturnSingleResult(function.get(), args, browser())); 1717 1718 std::string url; 1719 EXPECT_TRUE(value->GetAsString(&url)); 1720 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1721 url); 1722} 1723 1724} // namespace extensions 1725 1726// Tests the chrome.identity API implemented by custom JS bindings . 1727IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) { 1728 ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_; 1729} 1730