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