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