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/experimental_identity_api.h" 10 #include "chrome/browser/extensions/api/identity/identity_api.h" 11 #include "chrome/browser/extensions/api/identity/web_auth_flow.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/ui/browser.h" 16 #include "chrome/browser/ui/browser_window.h" 17 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 18 #include "chrome/test/base/in_process_browser_test.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_source.h" 21 #include "content/public/test/test_utils.h" 22 #include "extensions/common/id_util.h" 23 #include "google_apis/gaia/google_service_auth_error.h" 24 #include "google_apis/gaia/oauth2_mint_token_flow.h" 25 #include "testing/gmock/include/gmock/gmock.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 #include "url/gurl.h" 28 29 using testing::_; 30 using testing::Return; 31 using testing::ReturnRef; 32 33 namespace extensions { 34 35 namespace { 36 37 namespace errors = identity_constants; 38 namespace utils = extension_function_test_utils; 39 40 static const char kAccessToken[] = "auth_token"; 41 static const char kExtensionId[] = "ext_id"; 42 43 // This helps us be able to wait until an AsyncExtensionFunction calls 44 // SendResponse. 45 class SendResponseDelegate : 46 public UIThreadExtensionFunction::DelegateForTests { 47 public: 48 SendResponseDelegate() : should_post_quit_(false) {} 49 50 virtual ~SendResponseDelegate() {} 51 52 void set_should_post_quit(bool should_quit) { 53 should_post_quit_ = should_quit; 54 } 55 56 bool HasResponse() { return response_.get() != NULL; } 57 58 bool GetResponse() { 59 EXPECT_TRUE(HasResponse()); 60 return *response_.get(); 61 } 62 63 virtual void OnSendResponse(UIThreadExtensionFunction* function, 64 bool success, 65 bool bad_message) OVERRIDE { 66 ASSERT_FALSE(bad_message); 67 ASSERT_FALSE(HasResponse()); 68 response_.reset(new bool); 69 *response_ = success; 70 if (should_post_quit_) { 71 base::MessageLoopForUI::current()->Quit(); 72 } 73 } 74 75 private: 76 scoped_ptr<bool> response_; 77 bool should_post_quit_; 78 }; 79 80 class AsyncExtensionBrowserTest : public ExtensionBrowserTest { 81 protected: 82 // Asynchronous function runner allows tests to manipulate the browser window 83 // after the call happens. 84 void RunFunctionAsync(UIThreadExtensionFunction* function, 85 const std::string& args) { 86 response_delegate_.reset(new SendResponseDelegate); 87 function->set_test_delegate(response_delegate_.get()); 88 scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args)); 89 EXPECT_TRUE(parsed_args.get()) 90 << "Could not parse extension function arguments: " << args; 91 function->SetArgs(parsed_args.get()); 92 93 if (!function->GetExtension()) { 94 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); 95 function->set_extension(empty_extension.get()); 96 } 97 98 function->set_profile(browser()->profile()); 99 function->set_has_callback(true); 100 function->Run(); 101 } 102 103 std::string WaitForError(UIThreadExtensionFunction* function) { 104 RunMessageLoopUntilResponse(); 105 EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; 106 return function->GetError(); 107 } 108 109 base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) { 110 RunMessageLoopUntilResponse(); 111 EXPECT_TRUE(function->GetError().empty()) 112 << "Unexpected error: " << function->GetError(); 113 const base::Value* single_result = NULL; 114 if (function->GetResultList() != NULL && 115 function->GetResultList()->Get(0, &single_result)) { 116 return single_result->DeepCopy(); 117 } 118 return NULL; 119 } 120 121 private: 122 void RunMessageLoopUntilResponse() { 123 // If the RunImpl of |function| didn't already call SendResponse, run the 124 // message loop until they do. 125 if (!response_delegate_->HasResponse()) { 126 response_delegate_->set_should_post_quit(true); 127 content::RunMessageLoop(); 128 } 129 EXPECT_TRUE(response_delegate_->HasResponse()); 130 } 131 132 scoped_ptr<SendResponseDelegate> response_delegate_; 133 }; 134 135 class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow { 136 public: 137 enum ResultType { 138 ISSUE_ADVICE_SUCCESS, 139 MINT_TOKEN_SUCCESS, 140 MINT_TOKEN_FAILURE, 141 MINT_TOKEN_BAD_CREDENTIALS 142 }; 143 144 TestOAuth2MintTokenFlow(ResultType result, 145 OAuth2MintTokenFlow::Delegate* delegate) 146 : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()), 147 result_(result), 148 delegate_(delegate) {} 149 150 virtual void Start() OVERRIDE { 151 switch (result_) { 152 case ISSUE_ADVICE_SUCCESS: { 153 IssueAdviceInfo info; 154 delegate_->OnIssueAdviceSuccess(info); 155 break; 156 } 157 case MINT_TOKEN_SUCCESS: { 158 delegate_->OnMintTokenSuccess(kAccessToken, 3600); 159 break; 160 } 161 case MINT_TOKEN_FAILURE: { 162 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 163 delegate_->OnMintTokenFailure(error); 164 break; 165 } 166 case MINT_TOKEN_BAD_CREDENTIALS: { 167 GoogleServiceAuthError error( 168 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 169 delegate_->OnMintTokenFailure(error); 170 break; 171 } 172 } 173 } 174 175 private: 176 ResultType result_; 177 OAuth2MintTokenFlow::Delegate* delegate_; 178 }; 179 180 BrowserContextKeyedService* IdentityAPITestFactory(Profile* profile) { 181 return new IdentityAPI(profile); 182 } 183 184 } // namespace 185 186 class ExperimentalMockGetAuthTokenFunction : 187 public ExperimentalIdentityGetAuthTokenFunction { 188 public: 189 ExperimentalMockGetAuthTokenFunction() 190 : login_access_token_result_(true), 191 login_ui_result_(true), 192 install_ui_result_(false), 193 login_ui_shown_(false), 194 install_ui_shown_(false) {} 195 196 void set_login_access_token_result(bool result) { 197 login_access_token_result_ = result; 198 } 199 200 void set_login_ui_result(bool result) { login_ui_result_ = result; } 201 202 void set_install_ui_result(bool result) { install_ui_result_ = result; } 203 204 bool login_ui_shown() const { return login_ui_shown_; } 205 206 bool install_ui_shown() const { return install_ui_shown_; } 207 208 virtual void StartLoginAccessTokenRequest() OVERRIDE { 209 if (login_access_token_result_) { 210 OnGetTokenSuccess(login_token_request_.get(), "access_token", 211 base::Time::Now() + base::TimeDelta::FromHours(1LL)); 212 } else { 213 GoogleServiceAuthError error( 214 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 215 OnGetTokenFailure(login_token_request_.get(), error); 216 } 217 } 218 219 virtual void ShowLoginPopup() OVERRIDE { 220 EXPECT_FALSE(login_ui_shown_); 221 login_ui_shown_ = true; 222 if (login_ui_result_) 223 SigninSuccess(); 224 else 225 SigninFailed(); 226 } 227 228 virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo& issue_advice) 229 OVERRIDE { 230 install_ui_shown_ = true; 231 // Call InstallUIProceed or InstallUIAbort based on the flag. 232 if (install_ui_result_) 233 InstallUIProceed(); 234 else 235 InstallUIAbort(true); 236 } 237 238 MOCK_CONST_METHOD0(HasLoginToken, bool()); 239 MOCK_METHOD1(CreateMintTokenFlow, 240 OAuth2MintTokenFlow*(const std::string& login_access_token)); 241 242 private: 243 ~ExperimentalMockGetAuthTokenFunction() {} 244 bool login_access_token_result_; 245 bool login_ui_result_; 246 bool install_ui_result_; 247 bool login_ui_shown_; 248 bool install_ui_shown_; 249 }; 250 251 class ExperimentalGetAuthTokenFunctionTest : public AsyncExtensionBrowserTest { 252 protected: 253 enum OAuth2Fields { 254 NONE = 0, 255 CLIENT_ID = 1, 256 SCOPES = 2 257 }; 258 259 virtual ~ExperimentalGetAuthTokenFunctionTest() {} 260 261 // Helper to create an extension with specific OAuth2Info fields set. 262 // |fields_to_set| should be computed by using fields of Oauth2Fields enum. 263 const Extension* CreateExtension(int fields_to_set) { 264 const Extension* ext = 265 LoadExtension(test_data_dir_.AppendASCII("platform_apps/oauth2")); 266 OAuth2Info& oauth2_info = 267 const_cast<OAuth2Info&>(OAuth2Info::GetOAuth2Info(ext)); 268 if ((fields_to_set & CLIENT_ID) != 0) 269 oauth2_info.client_id = "client1"; 270 if ((fields_to_set & SCOPES) != 0) { 271 oauth2_info.scopes.push_back("scope1"); 272 oauth2_info.scopes.push_back("scope2"); 273 } 274 return ext; 275 } 276 277 IdentityAPI* id_api() { 278 return IdentityAPI::GetFactoryInstance()->GetForProfile( 279 browser()->profile()); 280 } 281 }; 282 283 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, NoClientId) { 284 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 285 new ExperimentalMockGetAuthTokenFunction()); 286 func->set_extension(CreateExtension(SCOPES)); 287 std::string error = 288 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 289 EXPECT_EQ(std::string(errors::kInvalidClientId), error); 290 EXPECT_FALSE(func->login_ui_shown()); 291 EXPECT_FALSE(func->install_ui_shown()); 292 } 293 294 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, NoScopes) { 295 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 296 new ExperimentalMockGetAuthTokenFunction()); 297 func->set_extension(CreateExtension(CLIENT_ID)); 298 std::string error = 299 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 300 EXPECT_EQ(std::string(errors::kInvalidScopes), error); 301 EXPECT_FALSE(func->login_ui_shown()); 302 EXPECT_FALSE(func->install_ui_shown()); 303 } 304 305 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 306 NonInteractiveNotSignedIn) { 307 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 308 new ExperimentalMockGetAuthTokenFunction()); 309 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 310 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 311 std::string error = 312 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 313 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 314 EXPECT_FALSE(func->login_ui_shown()); 315 EXPECT_FALSE(func->install_ui_shown()); 316 } 317 318 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 319 NonInteractiveLoginAccessTokenFailure) { 320 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 321 new ExperimentalMockGetAuthTokenFunction()); 322 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 323 func->set_login_access_token_result(false); 324 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 325 std::string error = 326 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 327 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 328 } 329 330 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 331 NonInteractiveMintFailure) { 332 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 333 new ExperimentalMockGetAuthTokenFunction()); 334 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 335 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 336 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 337 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 338 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 339 std::string error = 340 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 341 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 342 EXPECT_FALSE(func->login_ui_shown()); 343 EXPECT_FALSE(func->install_ui_shown()); 344 } 345 346 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 347 NonInteractiveMintAdviceSuccess) { 348 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 349 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 350 new ExperimentalMockGetAuthTokenFunction()); 351 func->set_extension(extension.get()); 352 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 353 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 354 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 355 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 356 std::string error = 357 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 358 EXPECT_EQ(std::string(errors::kNoGrant), error); 359 EXPECT_FALSE(func->login_ui_shown()); 360 EXPECT_FALSE(func->install_ui_shown()); 361 362 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 363 EXPECT_EQ( 364 IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 365 id_api()->GetCachedToken(extension->id(), oauth2_info.scopes).status()); 366 } 367 368 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 369 NonInteractiveMintBadCredentials) { 370 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 371 new ExperimentalMockGetAuthTokenFunction()); 372 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 373 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 374 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 375 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 376 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 377 std::string error = 378 utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); 379 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 380 EXPECT_FALSE(func->login_ui_shown()); 381 EXPECT_FALSE(func->install_ui_shown()); 382 } 383 384 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 385 NonInteractiveSuccess) { 386 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 387 new ExperimentalMockGetAuthTokenFunction()); 388 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 389 func->set_extension(extension.get()); 390 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 391 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 392 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 393 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 394 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 395 scoped_ptr<base::Value> value( 396 utils::RunFunctionAndReturnSingleResult(func.get(), "[{}]", browser())); 397 std::string access_token; 398 EXPECT_TRUE(value->GetAsString(&access_token)); 399 EXPECT_EQ(std::string(kAccessToken), access_token); 400 EXPECT_FALSE(func->login_ui_shown()); 401 EXPECT_FALSE(func->install_ui_shown()); 402 EXPECT_EQ( 403 IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 404 id_api()->GetCachedToken(extension->id(), oauth2_info.scopes).status()); 405 } 406 407 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 408 InteractiveLoginCanceled) { 409 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 410 new ExperimentalMockGetAuthTokenFunction()); 411 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 412 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 413 func->set_login_ui_result(false); 414 std::string error = utils::RunFunctionAndReturnError( 415 func.get(), "[{\"interactive\": true}]", browser()); 416 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 417 EXPECT_TRUE(func->login_ui_shown()); 418 EXPECT_FALSE(func->install_ui_shown()); 419 } 420 421 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 422 InteractiveMintBadCredentialsLoginCanceled) { 423 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 424 new ExperimentalMockGetAuthTokenFunction()); 425 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 426 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 427 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 428 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 429 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 430 func->set_login_ui_result(false); 431 std::string error = utils::RunFunctionAndReturnError( 432 func.get(), "[{\"interactive\": true}]", browser()); 433 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 434 EXPECT_TRUE(func->login_ui_shown()); 435 EXPECT_FALSE(func->install_ui_shown()); 436 } 437 438 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 439 InteractiveLoginSuccessNoToken) { 440 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 441 new ExperimentalMockGetAuthTokenFunction()); 442 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 443 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 444 func->set_login_ui_result(false); 445 std::string error = utils::RunFunctionAndReturnError( 446 func.get(), "[{\"interactive\": true}]", browser()); 447 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 448 EXPECT_TRUE(func->login_ui_shown()); 449 EXPECT_FALSE(func->install_ui_shown()); 450 } 451 452 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 453 InteractiveLoginSuccessLoginAccessTokenFailure) { 454 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 455 new ExperimentalMockGetAuthTokenFunction()); 456 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 457 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 458 func->set_login_ui_result(true); 459 func->set_login_access_token_result(false); 460 std::string error = utils::RunFunctionAndReturnError( 461 func.get(), "[{\"interactive\": true}]", browser()); 462 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 463 } 464 465 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 466 InteractiveLoginSuccessMintFailure) { 467 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 468 new ExperimentalMockGetAuthTokenFunction()); 469 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 470 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 471 func->set_login_ui_result(true); 472 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 473 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 474 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 475 std::string error = utils::RunFunctionAndReturnError( 476 func.get(), "[{\"interactive\": true}]", browser()); 477 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 478 EXPECT_TRUE(func->login_ui_shown()); 479 EXPECT_FALSE(func->install_ui_shown()); 480 } 481 482 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 483 InteractiveLoginSuccessMintSuccess) { 484 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 485 new ExperimentalMockGetAuthTokenFunction()); 486 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 487 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 488 func->set_login_ui_result(true); 489 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 490 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 491 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 492 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 493 func.get(), "[{\"interactive\": true}]", browser())); 494 std::string access_token; 495 EXPECT_TRUE(value->GetAsString(&access_token)); 496 EXPECT_EQ(std::string(kAccessToken), access_token); 497 EXPECT_TRUE(func->login_ui_shown()); 498 EXPECT_FALSE(func->install_ui_shown()); 499 } 500 501 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 502 InteractiveLoginSuccessApprovalAborted) { 503 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 504 new ExperimentalMockGetAuthTokenFunction()); 505 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 506 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 507 func->set_login_ui_result(true); 508 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 509 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 510 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 511 func->set_install_ui_result(false); 512 std::string error = utils::RunFunctionAndReturnError( 513 func.get(), "[{\"interactive\": true}]", browser()); 514 EXPECT_EQ(std::string(errors::kUserRejected), error); 515 EXPECT_TRUE(func->login_ui_shown()); 516 EXPECT_TRUE(func->install_ui_shown()); 517 } 518 519 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 520 InteractiveLoginSuccessApprovalDoneMintFailure) { 521 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 522 new ExperimentalMockGetAuthTokenFunction()); 523 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 524 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 525 func->set_login_ui_result(true); 526 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 527 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 528 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 529 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 530 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)) 531 .WillOnce(Return(flow2)); 532 533 func->set_install_ui_result(true); 534 std::string error = utils::RunFunctionAndReturnError( 535 func.get(), "[{\"interactive\": true}]", browser()); 536 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 537 EXPECT_TRUE(func->login_ui_shown()); 538 EXPECT_TRUE(func->install_ui_shown()); 539 } 540 541 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 542 InteractiveLoginSuccessApprovalDoneMintSuccess) { 543 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 544 new ExperimentalMockGetAuthTokenFunction()); 545 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 546 func->set_extension(extension.get()); 547 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get()); 548 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 549 func->set_login_ui_result(true); 550 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 551 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 552 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 553 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 554 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)) 555 .WillOnce(Return(flow2)); 556 557 func->set_install_ui_result(true); 558 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 559 func.get(), "[{\"interactive\": true}]", browser())); 560 std::string access_token; 561 EXPECT_TRUE(value->GetAsString(&access_token)); 562 EXPECT_EQ(std::string(kAccessToken), access_token); 563 EXPECT_TRUE(func->login_ui_shown()); 564 EXPECT_TRUE(func->install_ui_shown()); 565 EXPECT_EQ( 566 IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 567 id_api()->GetCachedToken(extension->id(), oauth2_info.scopes).status()); 568 } 569 570 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 571 InteractiveApprovalAborted) { 572 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 573 new ExperimentalMockGetAuthTokenFunction()); 574 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 575 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 576 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 577 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 578 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 579 func->set_install_ui_result(false); 580 std::string error = utils::RunFunctionAndReturnError( 581 func.get(), "[{\"interactive\": true}]", browser()); 582 EXPECT_EQ(std::string(errors::kUserRejected), error); 583 EXPECT_FALSE(func->login_ui_shown()); 584 EXPECT_TRUE(func->install_ui_shown()); 585 } 586 587 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 588 InteractiveApprovalDoneMintSuccess) { 589 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 590 new ExperimentalMockGetAuthTokenFunction()); 591 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 592 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 593 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 594 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 595 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 596 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 597 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)) 598 .WillOnce(Return(flow2)); 599 600 func->set_install_ui_result(true); 601 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 602 func.get(), "[{\"interactive\": true}]", browser())); 603 std::string access_token; 604 EXPECT_TRUE(value->GetAsString(&access_token)); 605 EXPECT_EQ(std::string(kAccessToken), access_token); 606 EXPECT_FALSE(func->login_ui_shown()); 607 EXPECT_TRUE(func->install_ui_shown()); 608 } 609 610 IN_PROC_BROWSER_TEST_F(ExperimentalGetAuthTokenFunctionTest, 611 InteractiveApprovalDoneMintBadCredentials) { 612 scoped_refptr<ExperimentalMockGetAuthTokenFunction> func( 613 new ExperimentalMockGetAuthTokenFunction()); 614 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 615 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 616 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 617 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 618 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 619 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 620 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)) 621 .WillOnce(Return(flow2)); 622 623 func->set_install_ui_result(true); 624 std::string error = utils::RunFunctionAndReturnError( 625 func.get(), "[{\"interactive\": true}]", browser()); 626 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 627 EXPECT_FALSE(func->login_ui_shown()); 628 EXPECT_TRUE(func->install_ui_shown()); 629 } 630 631 class ExperimentalLaunchWebAuthFlowFunctionTest : 632 public AsyncExtensionBrowserTest { 633 protected: 634 void RunAndCheckBounds(const std::string& extra_params, 635 int expected_x, 636 int expected_y, 637 int expected_width, 638 int expected_height) { 639 content::WindowedNotificationObserver observer( 640 chrome::NOTIFICATION_BROWSER_WINDOW_READY, 641 content::NotificationService::AllSources()); 642 643 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 644 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 645 646 std::string args = base::StringPrintf( 647 "[{\"interactive\": true, \"url\": \"data:text/html,auth\"%s%s}]", 648 extra_params.length() ? "," : "", 649 extra_params.c_str()); 650 651 RunFunctionAsync(function.get(), args); 652 653 observer.Wait(); 654 655 Browser* web_auth_flow_browser = 656 content::Source<Browser>(observer.source()).ptr(); 657 EXPECT_EQ(expected_x, web_auth_flow_browser->override_bounds().x()); 658 EXPECT_EQ(expected_y, web_auth_flow_browser->override_bounds().y()); 659 EXPECT_EQ(expected_width, web_auth_flow_browser->override_bounds().width()); 660 EXPECT_EQ(expected_height, 661 web_auth_flow_browser->override_bounds().height()); 662 663 web_auth_flow_browser->window()->Close(); 664 } 665 }; 666 667 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, Bounds) { 668 RunAndCheckBounds(std::string(), 0, 0, 0, 0); 669 RunAndCheckBounds("\"width\": 100, \"height\": 200", 0, 0, 100, 200); 670 RunAndCheckBounds("\"left\": 100, \"top\": 200", 100, 200, 0, 0); 671 RunAndCheckBounds( 672 "\"left\": 100, \"top\": 200, \"width\": 300, \"height\": 400", 673 100, 674 200, 675 300, 676 400); 677 } 678 679 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, 680 UserCloseWindow) { 681 content::WindowedNotificationObserver observer( 682 chrome::NOTIFICATION_BROWSER_WINDOW_READY, 683 content::NotificationService::AllSources()); 684 685 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 686 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 687 688 RunFunctionAsync( 689 function.get(), 690 "[{\"interactive\": true, \"url\": \"data:text/html,auth\"}]"); 691 692 observer.Wait(); 693 Browser* web_auth_flow_browser = 694 content::Source<Browser>(observer.source()).ptr(); 695 web_auth_flow_browser->window()->Close(); 696 697 EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get())); 698 } 699 700 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, 701 InteractionRequired) { 702 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 703 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 704 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); 705 function->set_extension(empty_extension.get()); 706 707 std::string error = utils::RunFunctionAndReturnError( 708 function.get(), 709 "[{\"interactive\": false, \"url\": \"data:text/html,auth\"}]", 710 browser()); 711 712 EXPECT_EQ(std::string(errors::kInteractionRequired), error); 713 } 714 715 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, 716 NonInteractiveSuccess) { 717 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 718 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 719 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); 720 function->set_extension(empty_extension.get()); 721 722 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 723 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 724 function.get(), 725 "[{\"interactive\": false," 726 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 727 browser())); 728 729 std::string url; 730 EXPECT_TRUE(value->GetAsString(&url)); 731 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 732 url); 733 } 734 735 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, 736 InteractiveFirstNavigationSuccess) { 737 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 738 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 739 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); 740 function->set_extension(empty_extension.get()); 741 742 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 743 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 744 function.get(), 745 "[{\"interactive\": true," 746 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 747 browser())); 748 749 std::string url; 750 EXPECT_TRUE(value->GetAsString(&url)); 751 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 752 url); 753 } 754 755 IN_PROC_BROWSER_TEST_F(ExperimentalLaunchWebAuthFlowFunctionTest, 756 InteractiveSecondNavigationSuccess) { 757 scoped_refptr<ExperimentalIdentityLaunchWebAuthFlowFunction> function( 758 new ExperimentalIdentityLaunchWebAuthFlowFunction()); 759 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); 760 function->set_extension(empty_extension.get()); 761 762 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 763 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 764 function.get(), 765 "[{\"interactive\": true," 766 "\"url\": \"data:text/html,<script>window.location.replace('" 767 "https://abcdefghij.chromiumapp.org/callback#test')</script>\"}]", 768 browser())); 769 770 std::string url; 771 EXPECT_TRUE(value->GetAsString(&url)); 772 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 773 url); 774 } 775 776 } // namespace extensions 777