1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/command_line.h" 6 #include "base/strings/string_util.h" 7 #include "base/strings/stringprintf.h" 8 #include "base/values.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/extensions/api/identity/identity_api.h" 11 #include "chrome/browser/extensions/component_loader.h" 12 #include "chrome/browser/extensions/extension_apitest.h" 13 #include "chrome/browser/extensions/extension_browsertest.h" 14 #include "chrome/browser/extensions/extension_function_test_utils.h" 15 #include "chrome/browser/extensions/extension_service.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 20 #include "chrome/test/base/in_process_browser_test.h" 21 #include "chrome/test/base/test_switches.h" 22 #include "content/public/browser/notification_service.h" 23 #include "content/public/browser/notification_source.h" 24 #include "content/public/test/test_utils.h" 25 #include "extensions/common/id_util.h" 26 #include "google_apis/gaia/google_service_auth_error.h" 27 #include "google_apis/gaia/oauth2_mint_token_flow.h" 28 #include "grit/browser_resources.h" 29 #include "net/test/spawned_test_server/spawned_test_server.h" 30 #include "testing/gmock/include/gmock/gmock.h" 31 #include "testing/gtest/include/gtest/gtest.h" 32 #include "url/gurl.h" 33 34 using testing::_; 35 using testing::Return; 36 using testing::ReturnRef; 37 38 namespace extensions { 39 40 namespace { 41 42 namespace errors = identity_constants; 43 namespace utils = extension_function_test_utils; 44 45 static const char kAccessToken[] = "auth_token"; 46 static const char kExtensionId[] = "ext_id"; 47 48 // This helps us be able to wait until an AsyncExtensionFunction calls 49 // SendResponse. 50 class SendResponseDelegate 51 : public UIThreadExtensionFunction::DelegateForTests { 52 public: 53 SendResponseDelegate() : should_post_quit_(false) {} 54 55 virtual ~SendResponseDelegate() {} 56 57 void set_should_post_quit(bool should_quit) { 58 should_post_quit_ = should_quit; 59 } 60 61 bool HasResponse() { 62 return response_.get() != NULL; 63 } 64 65 bool GetResponse() { 66 EXPECT_TRUE(HasResponse()); 67 return *response_.get(); 68 } 69 70 virtual void OnSendResponse(UIThreadExtensionFunction* function, 71 bool success, 72 bool bad_message) OVERRIDE { 73 ASSERT_FALSE(bad_message); 74 ASSERT_FALSE(HasResponse()); 75 response_.reset(new bool); 76 *response_ = success; 77 if (should_post_quit_) { 78 base::MessageLoopForUI::current()->Quit(); 79 } 80 } 81 82 private: 83 scoped_ptr<bool> response_; 84 bool should_post_quit_; 85 }; 86 87 class AsyncExtensionBrowserTest : public ExtensionBrowserTest { 88 protected: 89 // Asynchronous function runner allows tests to manipulate the browser window 90 // after the call happens. 91 void RunFunctionAsync( 92 UIThreadExtensionFunction* function, 93 const std::string& args) { 94 response_delegate_.reset(new SendResponseDelegate); 95 function->set_test_delegate(response_delegate_.get()); 96 scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args)); 97 EXPECT_TRUE(parsed_args.get()) << 98 "Could not parse extension function arguments: " << args; 99 function->SetArgs(parsed_args.get()); 100 101 if (!function->GetExtension()) { 102 scoped_refptr<Extension> empty_extension( 103 utils::CreateEmptyExtension()); 104 function->set_extension(empty_extension.get()); 105 } 106 107 function->set_profile(browser()->profile()); 108 function->set_has_callback(true); 109 function->Run(); 110 } 111 112 std::string WaitForError(UIThreadExtensionFunction* function) { 113 RunMessageLoopUntilResponse(); 114 EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; 115 return function->GetError(); 116 } 117 118 base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) { 119 RunMessageLoopUntilResponse(); 120 EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " 121 << function->GetError(); 122 const base::Value* single_result = NULL; 123 if (function->GetResultList() != NULL && 124 function->GetResultList()->Get(0, &single_result)) { 125 return single_result->DeepCopy(); 126 } 127 return NULL; 128 } 129 130 private: 131 void RunMessageLoopUntilResponse() { 132 // If the RunImpl of |function| didn't already call SendResponse, run the 133 // message loop until they do. 134 if (!response_delegate_->HasResponse()) { 135 response_delegate_->set_should_post_quit(true); 136 content::RunMessageLoop(); 137 } 138 EXPECT_TRUE(response_delegate_->HasResponse()); 139 } 140 141 scoped_ptr<SendResponseDelegate> response_delegate_; 142 }; 143 144 class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow { 145 public: 146 enum ResultType { 147 ISSUE_ADVICE_SUCCESS, 148 MINT_TOKEN_SUCCESS, 149 MINT_TOKEN_FAILURE, 150 MINT_TOKEN_BAD_CREDENTIALS 151 }; 152 153 TestOAuth2MintTokenFlow(ResultType result, 154 OAuth2MintTokenFlow::Delegate* delegate) 155 : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()), 156 result_(result), 157 delegate_(delegate) { 158 } 159 160 virtual void Start() OVERRIDE { 161 switch (result_) { 162 case ISSUE_ADVICE_SUCCESS: { 163 IssueAdviceInfo info; 164 delegate_->OnIssueAdviceSuccess(info); 165 break; 166 } 167 case MINT_TOKEN_SUCCESS: { 168 delegate_->OnMintTokenSuccess(kAccessToken, 3600); 169 break; 170 } 171 case MINT_TOKEN_FAILURE: { 172 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 173 delegate_->OnMintTokenFailure(error); 174 break; 175 } 176 case MINT_TOKEN_BAD_CREDENTIALS: { 177 GoogleServiceAuthError error( 178 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 179 delegate_->OnMintTokenFailure(error); 180 break; 181 } 182 } 183 } 184 185 private: 186 ResultType result_; 187 OAuth2MintTokenFlow::Delegate* delegate_; 188 }; 189 190 BrowserContextKeyedService* IdentityAPITestFactory( 191 content::BrowserContext* profile) { 192 return new IdentityAPI(static_cast<Profile*>(profile)); 193 } 194 195 // Waits for a specific GURL to generate a NOTIFICATION_LOAD_STOP event and 196 // saves a pointer to the window embedding the WebContents, which can be later 197 // closed. 198 class WaitForGURLAndCloseWindow : public content::WindowedNotificationObserver { 199 public: 200 explicit WaitForGURLAndCloseWindow(GURL url) 201 : WindowedNotificationObserver( 202 content::NOTIFICATION_LOAD_STOP, 203 content::NotificationService::AllSources()), 204 url_(url) {} 205 206 // NotificationObserver: 207 virtual void Observe(int type, 208 const content::NotificationSource& source, 209 const content::NotificationDetails& details) OVERRIDE { 210 content::NavigationController* web_auth_flow_controller = 211 content::Source<content::NavigationController>(source).ptr(); 212 content::WebContents* web_contents = 213 web_auth_flow_controller->GetWebContents(); 214 215 if (web_contents->GetURL() == url_) { 216 // It is safe to keep the pointer here, because we know in a test, that 217 // the WebContents won't go away before CloseEmbedderWebContents is 218 // called. Don't copy this code to production. 219 embedder_web_contents_ = web_contents->GetEmbedderWebContents(); 220 // Condtionally invoke parent class so that Wait will not exit 221 // until the target URL arrives. 222 content::WindowedNotificationObserver::Observe(type, source, details); 223 } 224 } 225 226 // Closes the window embedding the WebContents. The action is separated from 227 // the Observe method to make sure the list of observers is not deleted, 228 // while some event is already being processed. (That causes ASAN failures.) 229 void CloseEmbedderWebContents() { 230 if (embedder_web_contents_) 231 embedder_web_contents_->Close(); 232 } 233 234 private: 235 GURL url_; 236 content::WebContents* embedder_web_contents_; 237 }; 238 239 } // namespace 240 241 class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction { 242 public: 243 MockGetAuthTokenFunction() : login_access_token_result_(true), 244 login_ui_result_(true), 245 scope_ui_result_(true), 246 login_ui_shown_(false), 247 scope_ui_shown_(false) { 248 } 249 250 void set_login_access_token_result(bool result) { 251 login_access_token_result_ = result; 252 } 253 254 void set_login_ui_result(bool result) { 255 login_ui_result_ = result; 256 } 257 258 void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) { 259 scope_ui_result_ = false; 260 scope_ui_failure_ = failure; 261 } 262 263 void set_scope_ui_oauth_error(const std::string& oauth_error) { 264 scope_ui_result_ = false; 265 scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR; 266 scope_ui_oauth_error_ = oauth_error; 267 } 268 269 bool login_ui_shown() const { 270 return login_ui_shown_; 271 } 272 273 bool scope_ui_shown() const { 274 return scope_ui_shown_; 275 } 276 277 virtual void StartLoginAccessTokenRequest() OVERRIDE { 278 if (login_access_token_result_) { 279 OnGetTokenSuccess(login_token_request_.get(), "access_token", 280 base::Time::Now() + base::TimeDelta::FromHours(1LL)); 281 } else { 282 GoogleServiceAuthError error( 283 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 284 OnGetTokenFailure(login_token_request_.get(), error); 285 } 286 } 287 288 virtual void ShowLoginPopup() OVERRIDE { 289 EXPECT_FALSE(login_ui_shown_); 290 login_ui_shown_ = true; 291 if (login_ui_result_) 292 SigninSuccess(); 293 else 294 SigninFailed(); 295 } 296 297 virtual void ShowOAuthApprovalDialog( 298 const IssueAdviceInfo& issue_advice) OVERRIDE { 299 scope_ui_shown_ = true; 300 301 if (scope_ui_result_) { 302 OnGaiaFlowCompleted(kAccessToken, "3600"); 303 } else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) { 304 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 305 OnGaiaFlowFailure(scope_ui_failure_, error, ""); 306 } else { 307 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 308 OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_); 309 } 310 } 311 312 MOCK_CONST_METHOD0(HasLoginToken, bool()); 313 MOCK_METHOD1(CreateMintTokenFlow, 314 OAuth2MintTokenFlow* (const std::string& login_access_token)); 315 316 private: 317 ~MockGetAuthTokenFunction() {} 318 bool login_access_token_result_; 319 bool login_ui_result_; 320 bool scope_ui_result_; 321 GaiaWebAuthFlow::Failure scope_ui_failure_; 322 std::string scope_ui_oauth_error_; 323 bool login_ui_shown_; 324 bool scope_ui_shown_; 325 }; 326 327 class MockQueuedMintRequest : public IdentityMintRequestQueue::Request { 328 public: 329 MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType)); 330 }; 331 332 class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest { 333 protected: 334 enum OAuth2Fields { 335 NONE = 0, 336 CLIENT_ID = 1, 337 SCOPES = 2, 338 AS_COMPONENT = 4 339 }; 340 341 virtual ~GetAuthTokenFunctionTest() {} 342 343 // Helper to create an extension with specific OAuth2Info fields set. 344 // |fields_to_set| should be computed by using fields of Oauth2Fields enum. 345 const Extension* CreateExtension(int fields_to_set) { 346 const Extension* ext; 347 base::FilePath manifest_path = 348 test_data_dir_.AppendASCII("platform_apps/oauth2"); 349 base::FilePath component_manifest_path = 350 test_data_dir_.AppendASCII("packaged_app/component_oauth2"); 351 if ((fields_to_set & AS_COMPONENT) == 0) 352 ext = LoadExtension(manifest_path); 353 else 354 ext = LoadExtensionAsComponent(component_manifest_path); 355 OAuth2Info& oauth2_info = 356 const_cast<OAuth2Info&>(OAuth2Info::GetOAuth2Info(ext)); 357 if ((fields_to_set & CLIENT_ID) != 0) 358 oauth2_info.client_id = "client1"; 359 if ((fields_to_set & SCOPES) != 0) { 360 oauth2_info.scopes.push_back("scope1"); 361 oauth2_info.scopes.push_back("scope2"); 362 } 363 return ext; 364 } 365 366 IdentityAPI* id_api() { 367 return IdentityAPI::GetFactoryInstance()->GetForProfile( 368 browser()->profile()); 369 } 370 }; 371 372 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 373 NoClientId) { 374 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 375 func->set_extension(CreateExtension(SCOPES)); 376 std::string error = utils::RunFunctionAndReturnError( 377 func.get(), "[{}]", browser()); 378 EXPECT_EQ(std::string(errors::kInvalidClientId), error); 379 EXPECT_FALSE(func->login_ui_shown()); 380 EXPECT_FALSE(func->scope_ui_shown()); 381 } 382 383 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 384 NoScopes) { 385 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 386 func->set_extension(CreateExtension(CLIENT_ID)); 387 std::string error = utils::RunFunctionAndReturnError( 388 func.get(), "[{}]", browser()); 389 EXPECT_EQ(std::string(errors::kInvalidScopes), error); 390 EXPECT_FALSE(func->login_ui_shown()); 391 EXPECT_FALSE(func->scope_ui_shown()); 392 } 393 394 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 395 NonInteractiveNotSignedIn) { 396 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 397 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 398 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 399 std::string error = utils::RunFunctionAndReturnError( 400 func.get(), "[{}]", browser()); 401 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 402 EXPECT_FALSE(func->login_ui_shown()); 403 EXPECT_FALSE(func->scope_ui_shown()); 404 } 405 406 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 407 NonInteractiveMintFailure) { 408 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 409 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 410 EXPECT_CALL(*func.get(), HasLoginToken()) 411 .WillOnce(Return(true)); 412 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 413 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 414 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 415 std::string error = utils::RunFunctionAndReturnError( 416 func.get(), "[{}]", browser()); 417 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 418 EXPECT_FALSE(func->login_ui_shown()); 419 EXPECT_FALSE(func->scope_ui_shown()); 420 } 421 422 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 423 NonInteractiveLoginAccessTokenFailure) { 424 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 425 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 426 EXPECT_CALL(*func.get(), HasLoginToken()) 427 .WillOnce(Return(true)); 428 func->set_login_access_token_result(false); 429 std::string error = utils::RunFunctionAndReturnError( 430 func.get(), "[{}]", browser()); 431 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 432 } 433 434 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 435 NonInteractiveMintAdviceSuccess) { 436 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 437 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 438 func->set_extension(extension.get()); 439 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 440 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 441 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 442 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 443 std::string error = utils::RunFunctionAndReturnError( 444 func.get(), "[{}]", browser()); 445 EXPECT_EQ(std::string(errors::kNoGrant), error); 446 EXPECT_FALSE(func->login_ui_shown()); 447 EXPECT_FALSE(func->scope_ui_shown()); 448 449 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 450 EXPECT_EQ( 451 IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 452 id_api()->GetCachedToken(extension->id(), oauth2_info.scopes).status()); 453 } 454 455 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 456 NonInteractiveMintBadCredentials) { 457 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 458 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 459 EXPECT_CALL(*func.get(), HasLoginToken()) 460 .WillOnce(Return(true)); 461 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 462 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 463 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 464 std::string error = utils::RunFunctionAndReturnError( 465 func.get(), "[{}]", browser()); 466 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 467 EXPECT_FALSE(func->login_ui_shown()); 468 EXPECT_FALSE(func->scope_ui_shown()); 469 } 470 471 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 472 NonInteractiveSuccess) { 473 #if defined(OS_WIN) && defined(USE_ASH) 474 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 475 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 476 return; 477 #endif 478 479 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 480 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 481 func->set_extension(extension.get()); 482 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 483 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 484 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 485 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 486 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 487 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 488 func.get(), "[{}]", browser())); 489 std::string access_token; 490 EXPECT_TRUE(value->GetAsString(&access_token)); 491 EXPECT_EQ(std::string(kAccessToken), access_token); 492 EXPECT_FALSE(func->login_ui_shown()); 493 EXPECT_FALSE(func->scope_ui_shown()); 494 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 495 id_api()->GetCachedToken(extension->id(), 496 oauth2_info.scopes).status()); 497 } 498 499 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 500 InteractiveLoginCanceled) { 501 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 502 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 503 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 504 func->set_login_ui_result(false); 505 std::string error = utils::RunFunctionAndReturnError( 506 func.get(), "[{\"interactive\": true}]", browser()); 507 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 508 EXPECT_TRUE(func->login_ui_shown()); 509 EXPECT_FALSE(func->scope_ui_shown()); 510 } 511 512 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 513 InteractiveMintBadCredentialsLoginCanceled) { 514 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 515 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 516 EXPECT_CALL(*func.get(), HasLoginToken()) 517 .WillOnce(Return(true)); 518 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 519 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 520 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 521 func->set_login_ui_result(false); 522 std::string error = utils::RunFunctionAndReturnError( 523 func.get(), "[{\"interactive\": true}]", browser()); 524 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 525 EXPECT_TRUE(func->login_ui_shown()); 526 EXPECT_FALSE(func->scope_ui_shown()); 527 } 528 529 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 530 InteractiveLoginSuccessNoToken) { 531 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 532 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 533 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 534 func->set_login_ui_result(false); 535 std::string error = utils::RunFunctionAndReturnError( 536 func.get(), "[{\"interactive\": true}]", browser()); 537 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 538 EXPECT_TRUE(func->login_ui_shown()); 539 EXPECT_FALSE(func->scope_ui_shown()); 540 } 541 542 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 543 InteractiveLoginSuccessMintFailure) { 544 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 545 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 546 EXPECT_CALL(*func.get(), HasLoginToken()) 547 .WillOnce(Return(false)); 548 func->set_login_ui_result(true); 549 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 550 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 551 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 552 std::string error = utils::RunFunctionAndReturnError( 553 func.get(), "[{\"interactive\": true}]", browser()); 554 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 555 EXPECT_TRUE(func->login_ui_shown()); 556 EXPECT_FALSE(func->scope_ui_shown()); 557 } 558 559 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 560 InteractiveLoginSuccessLoginAccessTokenFailure) { 561 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 562 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 563 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 564 func->set_login_ui_result(true); 565 func->set_login_access_token_result(false); 566 std::string error = utils::RunFunctionAndReturnError( 567 func.get(), "[{\"interactive\": true}]", browser()); 568 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 569 EXPECT_TRUE(func->login_ui_shown()); 570 EXPECT_FALSE(func->scope_ui_shown()); 571 } 572 573 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 574 InteractiveLoginSuccessMintSuccess) { 575 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 576 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 577 EXPECT_CALL(*func.get(), HasLoginToken()) 578 .WillOnce(Return(false)); 579 func->set_login_ui_result(true); 580 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 581 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 582 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 583 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 584 func.get(), "[{\"interactive\": true}]", browser())); 585 std::string access_token; 586 EXPECT_TRUE(value->GetAsString(&access_token)); 587 EXPECT_EQ(std::string(kAccessToken), access_token); 588 EXPECT_TRUE(func->login_ui_shown()); 589 EXPECT_FALSE(func->scope_ui_shown()); 590 } 591 592 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 593 InteractiveLoginSuccessApprovalAborted) { 594 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 595 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 596 EXPECT_CALL(*func.get(), HasLoginToken()) 597 .WillOnce(Return(false)); 598 func->set_login_ui_result(true); 599 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 600 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 601 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 602 func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED); 603 std::string error = utils::RunFunctionAndReturnError( 604 func.get(), "[{\"interactive\": true}]", browser()); 605 EXPECT_EQ(std::string(errors::kUserRejected), error); 606 EXPECT_TRUE(func->login_ui_shown()); 607 EXPECT_TRUE(func->scope_ui_shown()); 608 } 609 610 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 611 InteractiveLoginSuccessApprovalSuccess) { 612 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 613 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 614 func->set_extension(extension.get()); 615 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 616 func->set_login_ui_result(true); 617 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 618 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 619 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 620 .WillOnce(Return(flow)); 621 622 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 623 func.get(), "[{\"interactive\": true}]", browser())); 624 std::string access_token; 625 EXPECT_TRUE(value->GetAsString(&access_token)); 626 EXPECT_EQ(std::string(kAccessToken), access_token); 627 EXPECT_TRUE(func->login_ui_shown()); 628 EXPECT_TRUE(func->scope_ui_shown()); 629 } 630 631 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 632 InteractiveApprovalAborted) { 633 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 634 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 635 EXPECT_CALL(*func.get(), HasLoginToken()) 636 .WillOnce(Return(true)); 637 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 638 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 639 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 640 func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED); 641 std::string error = utils::RunFunctionAndReturnError( 642 func.get(), "[{\"interactive\": true}]", browser()); 643 EXPECT_EQ(std::string(errors::kUserRejected), error); 644 EXPECT_FALSE(func->login_ui_shown()); 645 EXPECT_TRUE(func->scope_ui_shown()); 646 } 647 648 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 649 InteractiveApprovalLoadFailed) { 650 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 651 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 652 EXPECT_CALL(*func.get(), HasLoginToken()) 653 .WillOnce(Return(true)); 654 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 655 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 656 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 657 func->set_scope_ui_failure(GaiaWebAuthFlow::LOAD_FAILED); 658 std::string error = utils::RunFunctionAndReturnError( 659 func.get(), "[{\"interactive\": true}]", browser()); 660 EXPECT_EQ(std::string(errors::kPageLoadFailure), error); 661 EXPECT_FALSE(func->login_ui_shown()); 662 EXPECT_TRUE(func->scope_ui_shown()); 663 } 664 665 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 666 InteractiveApprovalInvalidRedirect) { 667 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 668 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 669 EXPECT_CALL(*func.get(), HasLoginToken()) 670 .WillOnce(Return(true)); 671 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 672 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 673 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 674 func->set_scope_ui_failure(GaiaWebAuthFlow::INVALID_REDIRECT); 675 std::string error = utils::RunFunctionAndReturnError( 676 func.get(), "[{\"interactive\": true}]", browser()); 677 EXPECT_EQ(std::string(errors::kInvalidRedirect), error); 678 EXPECT_FALSE(func->login_ui_shown()); 679 EXPECT_TRUE(func->scope_ui_shown()); 680 } 681 682 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 683 InteractiveApprovalConnectionFailure) { 684 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 685 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 686 EXPECT_CALL(*func.get(), HasLoginToken()) 687 .WillOnce(Return(true)); 688 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 689 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 690 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 691 func->set_scope_ui_failure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR); 692 std::string error = utils::RunFunctionAndReturnError( 693 func.get(), "[{\"interactive\": true}]", browser()); 694 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 695 EXPECT_FALSE(func->login_ui_shown()); 696 EXPECT_TRUE(func->scope_ui_shown()); 697 } 698 699 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 700 InteractiveApprovalOAuthErrors) { 701 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 702 703 std::map<std::string, std::string> error_map; 704 error_map.insert(std::make_pair("access_denied", errors::kUserRejected)); 705 error_map.insert(std::make_pair("invalid_scope", errors::kInvalidScopes)); 706 error_map.insert(std::make_pair( 707 "unmapped_error", std::string(errors::kAuthFailure) + "unmapped_error")); 708 709 for (std::map<std::string, std::string>::const_iterator 710 it = error_map.begin(); 711 it != error_map.end(); 712 ++it) { 713 scoped_refptr<MockGetAuthTokenFunction> func( 714 new MockGetAuthTokenFunction()); 715 func->set_extension(extension.get()); 716 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 717 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 718 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 719 ON_CALL(*func.get(), CreateMintTokenFlow(_)).WillByDefault(Return(flow)); 720 func->set_scope_ui_oauth_error(it->first); 721 std::string error = utils::RunFunctionAndReturnError( 722 func.get(), "[{\"interactive\": true}]", browser()); 723 EXPECT_EQ(it->second, error); 724 EXPECT_FALSE(func->login_ui_shown()); 725 EXPECT_TRUE(func->scope_ui_shown()); 726 } 727 } 728 729 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 730 InteractiveApprovalSuccess) { 731 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 732 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 733 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 734 func->set_extension(extension.get()); 735 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 736 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 737 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 738 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 739 .WillOnce(Return(flow)); 740 741 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 742 func.get(), "[{\"interactive\": true}]", browser())); 743 std::string access_token; 744 EXPECT_TRUE(value->GetAsString(&access_token)); 745 EXPECT_EQ(std::string(kAccessToken), access_token); 746 EXPECT_FALSE(func->login_ui_shown()); 747 EXPECT_TRUE(func->scope_ui_shown()); 748 749 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 750 id_api()->GetCachedToken(extension->id(), 751 oauth2_info.scopes).status()); 752 } 753 754 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) { 755 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 756 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 757 func->set_extension(extension.get()); 758 759 // Create a fake request to block the queue. 760 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 761 std::set<std::string> scopes(oauth2_info.scopes.begin(), 762 oauth2_info.scopes.end()); 763 IdentityAPI* id_api = 764 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 765 browser()->profile()); 766 IdentityMintRequestQueue* queue = id_api->mint_queue(); 767 MockQueuedMintRequest queued_request; 768 IdentityMintRequestQueue::MintType type = 769 IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE; 770 771 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 772 queue->RequestStart(type, extension->id(), scopes, &queued_request); 773 774 // The real request will start processing, but wait in the queue behind 775 // the blocker. 776 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 777 RunFunctionAsync(func.get(), "[{}]"); 778 // Verify that we have fetched the login token at this point. 779 testing::Mock::VerifyAndClearExpectations(func.get()); 780 781 // The flow will be created after the first queued request clears. 782 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 783 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 784 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 785 786 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 787 788 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 789 std::string access_token; 790 EXPECT_TRUE(value->GetAsString(&access_token)); 791 EXPECT_EQ(std::string(kAccessToken), access_token); 792 EXPECT_FALSE(func->login_ui_shown()); 793 EXPECT_FALSE(func->scope_ui_shown()); 794 } 795 796 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) { 797 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 798 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 799 func->set_extension(extension.get()); 800 801 // Create a fake request to block the queue. 802 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 803 std::set<std::string> scopes(oauth2_info.scopes.begin(), 804 oauth2_info.scopes.end()); 805 IdentityAPI* id_api = 806 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 807 browser()->profile()); 808 IdentityMintRequestQueue* queue = id_api->mint_queue(); 809 MockQueuedMintRequest queued_request; 810 IdentityMintRequestQueue::MintType type = 811 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 812 813 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 814 queue->RequestStart(type, extension->id(), scopes, &queued_request); 815 816 // The real request will start processing, but wait in the queue behind 817 // the blocker. 818 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 819 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 820 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 821 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)); 822 RunFunctionAsync(func.get(), "[{\"interactive\": true}]"); 823 // Verify that we have fetched the login token and run the first flow. 824 testing::Mock::VerifyAndClearExpectations(func.get()); 825 EXPECT_FALSE(func->scope_ui_shown()); 826 827 // The UI will be displayed and a token retrieved after the first 828 // queued request clears. 829 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 830 831 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 832 std::string access_token; 833 EXPECT_TRUE(value->GetAsString(&access_token)); 834 EXPECT_EQ(std::string(kAccessToken), access_token); 835 EXPECT_FALSE(func->login_ui_shown()); 836 EXPECT_TRUE(func->scope_ui_shown()); 837 } 838 839 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 840 InteractiveQueuedNoninteractiveFails) { 841 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 842 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 843 func->set_extension(extension.get()); 844 845 // Create a fake request to block the interactive queue. 846 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 847 std::set<std::string> scopes(oauth2_info.scopes.begin(), 848 oauth2_info.scopes.end()); 849 IdentityAPI* id_api = 850 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 851 browser()->profile()); 852 IdentityMintRequestQueue* queue = id_api->mint_queue(); 853 MockQueuedMintRequest queued_request; 854 IdentityMintRequestQueue::MintType type = 855 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 856 857 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 858 queue->RequestStart(type, extension->id(), scopes, &queued_request); 859 860 // Non-interactive requests fail without hitting GAIA, because a 861 // consent UI is known to be up. 862 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 863 std::string error = utils::RunFunctionAndReturnError( 864 func.get(), "[{}]", browser()); 865 EXPECT_EQ(std::string(errors::kNoGrant), error); 866 EXPECT_FALSE(func->login_ui_shown()); 867 EXPECT_FALSE(func->scope_ui_shown()); 868 869 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 870 } 871 872 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 873 NonInteractiveCacheHit) { 874 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 875 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 876 func->set_extension(extension.get()); 877 878 // pre-populate the cache with a token 879 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 880 IdentityTokenCacheValue token(kAccessToken, 881 base::TimeDelta::FromSeconds(3600)); 882 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 883 884 // Get a token. Should not require a GAIA request. 885 EXPECT_CALL(*func.get(), HasLoginToken()) 886 .WillOnce(Return(true)); 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 } 895 896 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 897 NonInteractiveIssueAdviceCacheHit) { 898 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 899 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 900 func->set_extension(extension.get()); 901 902 // pre-populate the cache with advice 903 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 904 IssueAdviceInfo info; 905 IdentityTokenCacheValue token(info); 906 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 907 908 // Should return an error without a GAIA request. 909 EXPECT_CALL(*func.get(), HasLoginToken()) 910 .WillOnce(Return(true)); 911 std::string error = utils::RunFunctionAndReturnError( 912 func.get(), "[{}]", browser()); 913 EXPECT_EQ(std::string(errors::kNoGrant), error); 914 EXPECT_FALSE(func->login_ui_shown()); 915 EXPECT_FALSE(func->scope_ui_shown()); 916 } 917 918 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 919 InteractiveCacheHit) { 920 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 921 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 922 func->set_extension(extension.get()); 923 924 // Create a fake request to block the queue. 925 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 926 std::set<std::string> scopes(oauth2_info.scopes.begin(), 927 oauth2_info.scopes.end()); 928 IdentityMintRequestQueue* queue = id_api()->mint_queue(); 929 MockQueuedMintRequest queued_request; 930 IdentityMintRequestQueue::MintType type = 931 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 932 933 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 934 queue->RequestStart(type, extension->id(), scopes, &queued_request); 935 936 // The real request will start processing, but wait in the queue behind 937 // the blocker. 938 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 939 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 940 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 941 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 942 RunFunctionAsync(func.get(), "[{\"interactive\": true}]"); 943 944 // Populate the cache with a token while the request is blocked. 945 IdentityTokenCacheValue token(kAccessToken, 946 base::TimeDelta::FromSeconds(3600)); 947 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 948 949 // When we wake up the request, it returns the cached token without 950 // displaying a UI, or hitting GAIA. 951 952 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 953 954 scoped_ptr<base::Value> value(WaitForSingleResult(func.get())); 955 std::string access_token; 956 EXPECT_TRUE(value->GetAsString(&access_token)); 957 EXPECT_EQ(std::string(kAccessToken), access_token); 958 EXPECT_FALSE(func->login_ui_shown()); 959 EXPECT_FALSE(func->scope_ui_shown()); 960 } 961 962 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 963 LoginInvalidatesTokenCache) { 964 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 965 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 966 func->set_extension(extension.get()); 967 968 // pre-populate the cache with a token 969 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 970 IdentityTokenCacheValue token(kAccessToken, 971 base::TimeDelta::FromSeconds(3600)); 972 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 973 974 // Because the user is not signed in, the token will be removed, 975 // and we'll hit GAIA for new tokens. 976 EXPECT_CALL(*func.get(), HasLoginToken()) 977 .WillOnce(Return(false)); 978 func->set_login_ui_result(true); 979 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 980 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 981 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 982 .WillOnce(Return(flow)); 983 984 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 985 func.get(), "[{\"interactive\": true}]", browser())); 986 std::string access_token; 987 EXPECT_TRUE(value->GetAsString(&access_token)); 988 EXPECT_EQ(std::string(kAccessToken), access_token); 989 EXPECT_TRUE(func->login_ui_shown()); 990 EXPECT_TRUE(func->scope_ui_shown()); 991 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 992 id_api()->GetCachedToken(extension->id(), 993 oauth2_info.scopes).status()); 994 } 995 996 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) { 997 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 998 scoped_refptr<const Extension> extension( 999 CreateExtension(SCOPES | AS_COMPONENT)); 1000 func->set_extension(extension.get()); 1001 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 1002 EXPECT_TRUE(oauth2_info.client_id.empty()); 1003 EXPECT_FALSE(func->GetOAuth2ClientId().empty()); 1004 EXPECT_NE("client1", func->GetOAuth2ClientId()); 1005 } 1006 1007 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) { 1008 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 1009 scoped_refptr<const Extension> extension( 1010 CreateExtension(CLIENT_ID | SCOPES | AS_COMPONENT)); 1011 func->set_extension(extension.get()); 1012 EXPECT_EQ("client1", func->GetOAuth2ClientId()); 1013 } 1014 1015 class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest { 1016 protected: 1017 bool InvalidateDefaultToken() { 1018 scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func( 1019 new IdentityRemoveCachedAuthTokenFunction); 1020 func->set_extension(utils::CreateEmptyExtension(kExtensionId).get()); 1021 return utils::RunFunction( 1022 func.get(), 1023 std::string("[{\"token\": \"") + kAccessToken + "\"}]", 1024 browser(), 1025 extension_function_test_utils::NONE); 1026 } 1027 1028 IdentityAPI* id_api() { 1029 return IdentityAPI::GetFactoryInstance()->GetForProfile( 1030 browser()->profile()); 1031 } 1032 1033 void SetCachedToken(IdentityTokenCacheValue& token_data) { 1034 id_api()->SetCachedToken(extensions::id_util::GenerateId(kExtensionId), 1035 std::vector<std::string>(), token_data); 1036 } 1037 1038 const IdentityTokenCacheValue& GetCachedToken() { 1039 return id_api()->GetCachedToken( 1040 extensions::id_util::GenerateId(kExtensionId), 1041 std::vector<std::string>()); 1042 } 1043 }; 1044 1045 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) { 1046 EXPECT_TRUE(InvalidateDefaultToken()); 1047 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 1048 GetCachedToken().status()); 1049 } 1050 1051 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) { 1052 IssueAdviceInfo info; 1053 IdentityTokenCacheValue advice(info); 1054 SetCachedToken(advice); 1055 EXPECT_TRUE(InvalidateDefaultToken()); 1056 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 1057 GetCachedToken().status()); 1058 } 1059 1060 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) { 1061 IdentityTokenCacheValue token("non_matching_token", 1062 base::TimeDelta::FromSeconds(3600)); 1063 SetCachedToken(token); 1064 EXPECT_TRUE(InvalidateDefaultToken()); 1065 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 1066 GetCachedToken().status()); 1067 EXPECT_EQ("non_matching_token", GetCachedToken().token()); 1068 } 1069 1070 IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) { 1071 IdentityTokenCacheValue token(kAccessToken, 1072 base::TimeDelta::FromSeconds(3600)); 1073 SetCachedToken(token); 1074 EXPECT_TRUE(InvalidateDefaultToken()); 1075 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 1076 GetCachedToken().status()); 1077 } 1078 1079 class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest { 1080 public: 1081 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 1082 // Reduce performance test variance by disabling background networking. 1083 command_line->AppendSwitch(switches::kDisableBackgroundNetworking); 1084 } 1085 }; 1086 1087 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) { 1088 net::SpawnedTestServer https_server( 1089 net::SpawnedTestServer::TYPE_HTTPS, 1090 net::SpawnedTestServer::kLocalhost, 1091 base::FilePath(FILE_PATH_LITERAL( 1092 "chrome/test/data/extensions/api_test/identity"))); 1093 ASSERT_TRUE(https_server.Start()); 1094 GURL auth_url(https_server.GetURL("files/interaction_required.html")); 1095 1096 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1097 new IdentityLaunchWebAuthFlowFunction()); 1098 scoped_refptr<Extension> empty_extension( 1099 utils::CreateEmptyExtension()); 1100 function->set_extension(empty_extension.get()); 1101 1102 WaitForGURLAndCloseWindow popup_observer(auth_url); 1103 1104 std::string args = "[{\"interactive\": true, \"url\": \"" + 1105 auth_url.spec() + "\"}]"; 1106 RunFunctionAsync(function.get(), args); 1107 1108 popup_observer.Wait(); 1109 popup_observer.CloseEmbedderWebContents(); 1110 1111 EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get())); 1112 } 1113 1114 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) { 1115 net::SpawnedTestServer https_server( 1116 net::SpawnedTestServer::TYPE_HTTPS, 1117 net::SpawnedTestServer::kLocalhost, 1118 base::FilePath(FILE_PATH_LITERAL( 1119 "chrome/test/data/extensions/api_test/identity"))); 1120 ASSERT_TRUE(https_server.Start()); 1121 GURL auth_url(https_server.GetURL("files/interaction_required.html")); 1122 1123 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1124 new IdentityLaunchWebAuthFlowFunction()); 1125 scoped_refptr<Extension> empty_extension( 1126 utils::CreateEmptyExtension()); 1127 function->set_extension(empty_extension.get()); 1128 1129 std::string args = "[{\"interactive\": false, \"url\": \"" + 1130 auth_url.spec() + "\"}]"; 1131 std::string error = 1132 utils::RunFunctionAndReturnError(function.get(), args, browser()); 1133 1134 EXPECT_EQ(std::string(errors::kInteractionRequired), error); 1135 } 1136 1137 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) { 1138 net::SpawnedTestServer https_server( 1139 net::SpawnedTestServer::TYPE_HTTPS, 1140 net::SpawnedTestServer::kLocalhost, 1141 base::FilePath(FILE_PATH_LITERAL( 1142 "chrome/test/data/extensions/api_test/identity"))); 1143 ASSERT_TRUE(https_server.Start()); 1144 GURL auth_url(https_server.GetURL("files/five_hundred.html")); 1145 1146 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1147 new IdentityLaunchWebAuthFlowFunction()); 1148 scoped_refptr<Extension> empty_extension( 1149 utils::CreateEmptyExtension()); 1150 function->set_extension(empty_extension.get()); 1151 1152 std::string args = "[{\"interactive\": true, \"url\": \"" + 1153 auth_url.spec() + "\"}]"; 1154 std::string error = 1155 utils::RunFunctionAndReturnError(function.get(), args, browser()); 1156 1157 EXPECT_EQ(std::string(errors::kPageLoadFailure), error); 1158 } 1159 1160 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) { 1161 #if defined(OS_WIN) && defined(USE_ASH) 1162 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 1163 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 1164 return; 1165 #endif 1166 1167 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1168 new IdentityLaunchWebAuthFlowFunction()); 1169 scoped_refptr<Extension> empty_extension( 1170 utils::CreateEmptyExtension()); 1171 function->set_extension(empty_extension.get()); 1172 1173 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1174 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1175 function.get(), 1176 "[{\"interactive\": false," 1177 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 1178 browser())); 1179 1180 std::string url; 1181 EXPECT_TRUE(value->GetAsString(&url)); 1182 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1183 url); 1184 } 1185 1186 IN_PROC_BROWSER_TEST_F( 1187 LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) { 1188 #if defined(OS_WIN) && defined(USE_ASH) 1189 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 1190 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 1191 return; 1192 #endif 1193 1194 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1195 new IdentityLaunchWebAuthFlowFunction()); 1196 scoped_refptr<Extension> empty_extension( 1197 utils::CreateEmptyExtension()); 1198 function->set_extension(empty_extension.get()); 1199 1200 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1201 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1202 function.get(), 1203 "[{\"interactive\": true," 1204 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 1205 browser())); 1206 1207 std::string url; 1208 EXPECT_TRUE(value->GetAsString(&url)); 1209 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1210 url); 1211 } 1212 1213 IN_PROC_BROWSER_TEST_F( 1214 LaunchWebAuthFlowFunctionTest, InteractiveSecondNavigationSuccess) { 1215 net::SpawnedTestServer https_server( 1216 net::SpawnedTestServer::TYPE_HTTPS, 1217 net::SpawnedTestServer::kLocalhost, 1218 base::FilePath(FILE_PATH_LITERAL( 1219 "chrome/test/data/extensions/api_test/identity"))); 1220 ASSERT_TRUE(https_server.Start()); 1221 GURL auth_url(https_server.GetURL("files/redirect_to_chromiumapp.html")); 1222 1223 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 1224 new IdentityLaunchWebAuthFlowFunction()); 1225 scoped_refptr<Extension> empty_extension( 1226 utils::CreateEmptyExtension()); 1227 function->set_extension(empty_extension.get()); 1228 1229 function->InitFinalRedirectURLPrefixForTest("abcdefghij"); 1230 std::string args = "[{\"interactive\": true, \"url\": \"" + 1231 auth_url.spec() + "\"}]"; 1232 scoped_ptr<base::Value> value( 1233 utils::RunFunctionAndReturnSingleResult(function.get(), args, browser())); 1234 1235 std::string url; 1236 EXPECT_TRUE(value->GetAsString(&url)); 1237 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1238 url); 1239 } 1240 1241 } // namespace extensions 1242