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