identity_apitest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/string_util.h" 6#include "base/stringprintf.h" 7#include "base/values.h" 8#include "chrome/browser/extensions/api/identity/identity_api.h" 9#include "chrome/browser/extensions/api/identity/web_auth_flow.h" 10#include "chrome/browser/extensions/extension_apitest.h" 11#include "chrome/browser/extensions/extension_browsertest.h" 12#include "chrome/browser/extensions/extension_function_test_utils.h" 13#include "chrome/browser/ui/browser.h" 14#include "chrome/browser/ui/browser_window.h" 15#include "chrome/common/chrome_notification_types.h" 16#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 17#include "chrome/test/base/in_process_browser_test.h" 18#include "content/public/browser/notification_service.h" 19#include "content/public/browser/notification_source.h" 20#include "content/public/test/test_utils.h" 21#include "extensions/common/id_util.h" 22#include "google_apis/gaia/google_service_auth_error.h" 23#include "google_apis/gaia/oauth2_mint_token_flow.h" 24#include "googleurl/src/gurl.h" 25#include "testing/gmock/include/gmock/gmock.h" 26#include "testing/gtest/include/gtest/gtest.h" 27 28using testing::_; 29using testing::Return; 30using testing::ReturnRef; 31 32namespace extensions { 33 34namespace { 35 36namespace errors = identity_constants; 37namespace utils = extension_function_test_utils; 38 39static const char kAccessToken[] = "auth_token"; 40static const char kExtensionId[] = "ext_id"; 41 42// This helps us be able to wait until an AsyncExtensionFunction calls 43// SendResponse. 44class SendResponseDelegate 45 : public UIThreadExtensionFunction::DelegateForTests { 46 public: 47 SendResponseDelegate() : should_post_quit_(false) {} 48 49 virtual ~SendResponseDelegate() {} 50 51 void set_should_post_quit(bool should_quit) { 52 should_post_quit_ = should_quit; 53 } 54 55 bool HasResponse() { 56 return response_.get() != NULL; 57 } 58 59 bool GetResponse() { 60 EXPECT_TRUE(HasResponse()); 61 return *response_.get(); 62 } 63 64 virtual void OnSendResponse(UIThreadExtensionFunction* function, 65 bool success, 66 bool bad_message) OVERRIDE { 67 ASSERT_FALSE(bad_message); 68 ASSERT_FALSE(HasResponse()); 69 response_.reset(new bool); 70 *response_ = success; 71 if (should_post_quit_) { 72 MessageLoopForUI::current()->Quit(); 73 } 74 } 75 76 private: 77 scoped_ptr<bool> response_; 78 bool should_post_quit_; 79}; 80 81class AsyncExtensionBrowserTest : public ExtensionBrowserTest { 82 protected: 83 // Asynchronous function runner allows tests to manipulate the browser window 84 // after the call happens. 85 void RunFunctionAsync( 86 UIThreadExtensionFunction* function, 87 const std::string& args) { 88 response_delegate_.reset(new SendResponseDelegate); 89 function->set_test_delegate(response_delegate_.get()); 90 scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args)); 91 EXPECT_TRUE(parsed_args.get()) << 92 "Could not parse extension function arguments: " << args; 93 function->SetArgs(parsed_args.get()); 94 95 if (!function->GetExtension()) { 96 scoped_refptr<Extension> empty_extension( 97 utils::CreateEmptyExtension()); 98 function->set_extension(empty_extension.get()); 99 } 100 101 function->set_profile(browser()->profile()); 102 function->set_has_callback(true); 103 function->Run(); 104 } 105 106 std::string WaitForError(UIThreadExtensionFunction* function) { 107 RunMessageLoopUntilResponse(); 108 EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; 109 return function->GetError(); 110 } 111 112 base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) { 113 RunMessageLoopUntilResponse(); 114 EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " 115 << function->GetError(); 116 const base::Value* single_result = NULL; 117 if (function->GetResultList() != NULL && 118 function->GetResultList()->Get(0, &single_result)) { 119 return single_result->DeepCopy(); 120 } 121 return NULL; 122 } 123 124 private: 125 void RunMessageLoopUntilResponse() { 126 // If the RunImpl of |function| didn't already call SendResponse, run the 127 // message loop until they do. 128 if (!response_delegate_->HasResponse()) { 129 response_delegate_->set_should_post_quit(true); 130 content::RunMessageLoop(); 131 } 132 EXPECT_TRUE(response_delegate_->HasResponse()); 133 } 134 135 scoped_ptr<SendResponseDelegate> response_delegate_; 136}; 137 138class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow { 139 public: 140 enum ResultType { 141 ISSUE_ADVICE_SUCCESS, 142 MINT_TOKEN_SUCCESS, 143 MINT_TOKEN_FAILURE, 144 MINT_TOKEN_BAD_CREDENTIALS 145 }; 146 147 TestOAuth2MintTokenFlow(ResultType result, 148 OAuth2MintTokenFlow::Delegate* delegate) 149 : OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()), 150 result_(result), 151 delegate_(delegate) { 152 } 153 154 virtual void Start() OVERRIDE { 155 switch (result_) { 156 case ISSUE_ADVICE_SUCCESS: { 157 IssueAdviceInfo info; 158 delegate_->OnIssueAdviceSuccess(info); 159 break; 160 } 161 case MINT_TOKEN_SUCCESS: { 162 delegate_->OnMintTokenSuccess(kAccessToken, 3600); 163 break; 164 } 165 case MINT_TOKEN_FAILURE: { 166 GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); 167 delegate_->OnMintTokenFailure(error); 168 break; 169 } 170 case MINT_TOKEN_BAD_CREDENTIALS: { 171 GoogleServiceAuthError error( 172 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 173 delegate_->OnMintTokenFailure(error); 174 break; 175 } 176 } 177 } 178 179 private: 180 ResultType result_; 181 OAuth2MintTokenFlow::Delegate* delegate_; 182}; 183 184ProfileKeyedService* IdentityAPITestFactory(content::BrowserContext* profile) { 185 return new IdentityAPI(static_cast<Profile*>(profile)); 186} 187 188} // namespace 189 190class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction { 191 public: 192 MockGetAuthTokenFunction() : login_ui_result_(true), 193 install_ui_result_(false), 194 login_ui_shown_(false), 195 install_ui_shown_(false) { 196 } 197 198 void set_login_ui_result(bool result) { 199 login_ui_result_ = result; 200 } 201 202 void set_install_ui_result(bool result) { 203 install_ui_result_ = result; 204 } 205 206 bool login_ui_shown() const { 207 return login_ui_shown_; 208 } 209 210 bool install_ui_shown() const { 211 return install_ui_shown_; 212 } 213 214 virtual void ShowLoginPopup() OVERRIDE { 215 EXPECT_FALSE(login_ui_shown_); 216 login_ui_shown_ = true; 217 if (login_ui_result_) 218 SigninSuccess("fake_refresh_token"); 219 else 220 SigninFailed(); 221 } 222 223 virtual void ShowOAuthApprovalDialog( 224 const IssueAdviceInfo& issue_advice) OVERRIDE { 225 install_ui_shown_ = true; 226 // Call InstallUIProceed or InstallUIAbort based on the flag. 227 if (install_ui_result_) 228 InstallUIProceed(); 229 else 230 InstallUIAbort(true); 231 } 232 233 MOCK_CONST_METHOD0(HasLoginToken, bool()); 234 MOCK_METHOD1(CreateMintTokenFlow, 235 OAuth2MintTokenFlow* (OAuth2MintTokenFlow::Mode mode)); 236 237 private: 238 ~MockGetAuthTokenFunction() {} 239 bool login_ui_result_; 240 bool install_ui_result_; 241 bool login_ui_shown_; 242 bool install_ui_shown_; 243}; 244 245class MockQueuedMintRequest : public IdentityMintRequestQueue::Request { 246 public: 247 MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType)); 248}; 249 250class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest { 251 protected: 252 enum OAuth2Fields { 253 NONE = 0, 254 CLIENT_ID = 1, 255 SCOPES = 2 256 }; 257 258 virtual ~GetAuthTokenFunctionTest() {} 259 260 // Helper to create an extension with specific OAuth2Info fields set. 261 // |fields_to_set| should be computed by using fields of Oauth2Fields enum. 262 const Extension* CreateExtension(int fields_to_set) { 263 const Extension* ext = LoadExtension( 264 test_data_dir_.AppendASCII("platform_apps/oauth2")); 265 OAuth2Info& oauth2_info = const_cast<OAuth2Info&>( 266 OAuth2Info::GetOAuth2Info(ext)); 267 if ((fields_to_set & CLIENT_ID) != 0) 268 oauth2_info.client_id = "client1"; 269 if ((fields_to_set & SCOPES) != 0) { 270 oauth2_info.scopes.push_back("scope1"); 271 oauth2_info.scopes.push_back("scope2"); 272 } 273 return ext; 274 } 275 276 IdentityAPI* id_api() { 277 return IdentityAPI::GetFactoryInstance()->GetForProfile( 278 browser()->profile()); 279 } 280}; 281 282IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 283 NoClientId) { 284 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 285 func->set_extension(CreateExtension(SCOPES)); 286 std::string error = utils::RunFunctionAndReturnError( 287 func.get(), "[{}]", browser()); 288 EXPECT_EQ(std::string(errors::kInvalidClientId), error); 289 EXPECT_FALSE(func->login_ui_shown()); 290 EXPECT_FALSE(func->install_ui_shown()); 291} 292 293IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 294 NoScopes) { 295 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 296 func->set_extension(CreateExtension(CLIENT_ID)); 297 std::string error = utils::RunFunctionAndReturnError( 298 func.get(), "[{}]", browser()); 299 EXPECT_EQ(std::string(errors::kInvalidScopes), error); 300 EXPECT_FALSE(func->login_ui_shown()); 301 EXPECT_FALSE(func->install_ui_shown()); 302} 303 304IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 305 NonInteractiveNotSignedIn) { 306 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 307 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 308 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 309 std::string error = utils::RunFunctionAndReturnError( 310 func.get(), "[{}]", browser()); 311 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 312 EXPECT_FALSE(func->login_ui_shown()); 313 EXPECT_FALSE(func->install_ui_shown()); 314} 315 316IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 317 NonInteractiveMintFailure) { 318 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 319 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 320 EXPECT_CALL(*func.get(), HasLoginToken()) 321 .WillOnce(Return(true)); 322 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 323 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 324 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 325 std::string error = utils::RunFunctionAndReturnError( 326 func.get(), "[{}]", browser()); 327 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 328 EXPECT_FALSE(func->login_ui_shown()); 329 EXPECT_FALSE(func->install_ui_shown()); 330} 331 332IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 333 NonInteractiveMintAdviceSuccess) { 334 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 335 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 336 func->set_extension(extension); 337 EXPECT_CALL(*func.get(), HasLoginToken()) 338 .WillOnce(Return(true)); 339 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 340 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 341 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 342 std::string error = utils::RunFunctionAndReturnError( 343 func.get(), "[{}]", browser()); 344 EXPECT_EQ(std::string(errors::kNoGrant), error); 345 EXPECT_FALSE(func->login_ui_shown()); 346 EXPECT_FALSE(func->install_ui_shown()); 347 348 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 349 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 350 id_api()->GetCachedToken(extension->id(), 351 oauth2_info.scopes).status()); 352} 353 354IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 355 NonInteractiveMintBadCredentials) { 356 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 357 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 358 EXPECT_CALL(*func.get(), HasLoginToken()) 359 .WillOnce(Return(true)); 360 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 361 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 362 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 363 std::string error = utils::RunFunctionAndReturnError( 364 func.get(), "[{}]", browser()); 365 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 366 EXPECT_FALSE(func->login_ui_shown()); 367 EXPECT_FALSE(func->install_ui_shown()); 368} 369 370IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 371 NonInteractiveSuccess) { 372 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 373 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 374 func->set_extension(extension); 375 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 376 EXPECT_CALL(*func.get(), HasLoginToken()) 377 .WillOnce(Return(true)); 378 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 379 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 380 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 381 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 382 func.get(), "[{}]", browser())); 383 std::string access_token; 384 EXPECT_TRUE(value->GetAsString(&access_token)); 385 EXPECT_EQ(std::string(kAccessToken), access_token); 386 EXPECT_FALSE(func->login_ui_shown()); 387 EXPECT_FALSE(func->install_ui_shown()); 388 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 389 id_api()->GetCachedToken(extension->id(), 390 oauth2_info.scopes).status()); 391} 392 393IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 394 InteractiveLoginCanceled) { 395 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 396 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 397 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 398 func->set_login_ui_result(false); 399 std::string error = utils::RunFunctionAndReturnError( 400 func.get(), "[{\"interactive\": true}]", browser()); 401 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 402 EXPECT_TRUE(func->login_ui_shown()); 403 EXPECT_FALSE(func->install_ui_shown()); 404} 405 406IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 407 InteractiveMintBadCredentialsLoginCanceled) { 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_BAD_CREDENTIALS, func.get()); 414 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 415 func->set_login_ui_result(false); 416 std::string error = utils::RunFunctionAndReturnError( 417 func.get(), "[{\"interactive\": true}]", browser()); 418 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 419 EXPECT_TRUE(func->login_ui_shown()); 420 EXPECT_FALSE(func->install_ui_shown()); 421} 422 423IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 424 InteractiveLoginSuccessNoToken) { 425 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 426 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 427 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false)); 428 func->set_login_ui_result(false); 429 std::string error = utils::RunFunctionAndReturnError( 430 func.get(), "[{\"interactive\": true}]", browser()); 431 EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); 432 EXPECT_TRUE(func->login_ui_shown()); 433 EXPECT_FALSE(func->install_ui_shown()); 434} 435 436IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 437 InteractiveLoginSuccessMintFailure) { 438 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 439 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 440 EXPECT_CALL(*func.get(), HasLoginToken()) 441 .WillOnce(Return(false)); 442 func->set_login_ui_result(true); 443 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 444 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 445 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 446 std::string error = utils::RunFunctionAndReturnError( 447 func.get(), "[{\"interactive\": true}]", browser()); 448 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 449 EXPECT_TRUE(func->login_ui_shown()); 450 EXPECT_FALSE(func->install_ui_shown()); 451} 452 453IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 454 InteractiveLoginSuccessMintSuccess) { 455 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 456 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 457 EXPECT_CALL(*func.get(), HasLoginToken()) 458 .WillOnce(Return(false)); 459 func->set_login_ui_result(true); 460 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 461 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 462 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 463 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 464 func.get(), "[{\"interactive\": true}]", browser())); 465 std::string access_token; 466 EXPECT_TRUE(value->GetAsString(&access_token)); 467 EXPECT_EQ(std::string(kAccessToken), access_token); 468 EXPECT_TRUE(func->login_ui_shown()); 469 EXPECT_FALSE(func->install_ui_shown()); 470} 471 472IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 473 InteractiveLoginSuccessApprovalAborted) { 474 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 475 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 476 EXPECT_CALL(*func.get(), HasLoginToken()) 477 .WillOnce(Return(false)); 478 func->set_login_ui_result(true); 479 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 480 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 481 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 482 func->set_install_ui_result(false); 483 std::string error = utils::RunFunctionAndReturnError( 484 func.get(), "[{\"interactive\": true}]", browser()); 485 EXPECT_EQ(std::string(errors::kUserRejected), error); 486 EXPECT_TRUE(func->login_ui_shown()); 487 EXPECT_TRUE(func->install_ui_shown()); 488} 489 490IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 491 InteractiveLoginSuccessApprovalDoneMintFailure) { 492 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 493 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 494 EXPECT_CALL(*func.get(), HasLoginToken()) 495 .WillOnce(Return(false)); 496 func->set_login_ui_result(true); 497 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 498 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 499 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 500 TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get()); 501 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 502 .WillOnce(Return(flow1)) 503 .WillOnce(Return(flow2)); 504 505 func->set_install_ui_result(true); 506 std::string error = utils::RunFunctionAndReturnError( 507 func.get(), "[{\"interactive\": true}]", browser()); 508 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 509 EXPECT_TRUE(func->login_ui_shown()); 510 EXPECT_TRUE(func->install_ui_shown()); 511} 512 513IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 514 InteractiveLoginSuccessApprovalDoneMintSuccess) { 515 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 516 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 517 func->set_extension(extension); 518 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 519 EXPECT_CALL(*func.get(), HasLoginToken()) 520 .WillOnce(Return(false)); 521 func->set_login_ui_result(true); 522 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 523 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 524 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 525 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 526 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 527 .WillOnce(Return(flow1)) 528 .WillOnce(Return(flow2)); 529 530 func->set_install_ui_result(true); 531 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 532 func.get(), "[{\"interactive\": true}]", browser())); 533 std::string access_token; 534 EXPECT_TRUE(value->GetAsString(&access_token)); 535 EXPECT_EQ(std::string(kAccessToken), access_token); 536 EXPECT_TRUE(func->login_ui_shown()); 537 EXPECT_TRUE(func->install_ui_shown()); 538 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 539 id_api()->GetCachedToken(extension->id(), 540 oauth2_info.scopes).status()); 541} 542 543IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 544 InteractiveApprovalAborted) { 545 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 546 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 547 EXPECT_CALL(*func.get(), HasLoginToken()) 548 .WillOnce(Return(true)); 549 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 550 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 551 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 552 func->set_install_ui_result(false); 553 std::string error = utils::RunFunctionAndReturnError( 554 func.get(), "[{\"interactive\": true}]", browser()); 555 EXPECT_EQ(std::string(errors::kUserRejected), error); 556 EXPECT_FALSE(func->login_ui_shown()); 557 EXPECT_TRUE(func->install_ui_shown()); 558} 559 560IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 561 InteractiveApprovalDoneMintSuccess) { 562 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 563 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 564 EXPECT_CALL(*func.get(), HasLoginToken()) 565 .WillOnce(Return(true)); 566 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 567 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 568 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 569 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 570 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 571 .WillOnce(Return(flow1)) 572 .WillOnce(Return(flow2)); 573 574 func->set_install_ui_result(true); 575 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 576 func.get(), "[{\"interactive\": true}]", browser())); 577 std::string access_token; 578 EXPECT_TRUE(value->GetAsString(&access_token)); 579 EXPECT_EQ(std::string(kAccessToken), access_token); 580 EXPECT_FALSE(func->login_ui_shown()); 581 EXPECT_TRUE(func->install_ui_shown()); 582} 583 584IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 585 InteractiveApprovalDoneMintBadCredentials) { 586 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 587 func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); 588 EXPECT_CALL(*func.get(), HasLoginToken()) 589 .WillOnce(Return(true)); 590 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 591 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 592 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 593 TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get()); 594 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 595 .WillOnce(Return(flow1)) 596 .WillOnce(Return(flow2)); 597 598 func->set_install_ui_result(true); 599 std::string error = utils::RunFunctionAndReturnError( 600 func.get(), "[{\"interactive\": true}]", browser()); 601 EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false)); 602 EXPECT_FALSE(func->login_ui_shown()); 603 EXPECT_TRUE(func->install_ui_shown()); 604} 605 606IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) { 607 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 608 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 609 func->set_extension(extension); 610 611 // Create a fake request to block the queue. 612 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 613 std::set<std::string> scopes(oauth2_info.scopes.begin(), 614 oauth2_info.scopes.end()); 615 IdentityAPI* id_api = 616 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 617 browser()->profile()); 618 IdentityMintRequestQueue* queue = id_api->mint_queue(); 619 MockQueuedMintRequest queued_request; 620 IdentityMintRequestQueue::MintType type = 621 IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE; 622 623 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 624 queue->RequestStart(type, extension->id(), scopes, &queued_request); 625 626 // The real request will start processing, but wait in the queue behind 627 // the blocker. 628 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 629 RunFunctionAsync(func, "[{}]"); 630 // Verify that we have fetched the login token at this point. 631 testing::Mock::VerifyAndClearExpectations(func); 632 633 // The flow will be created after the first queued request clears. 634 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 635 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 636 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 637 638 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 639 640 scoped_ptr<base::Value> value(WaitForSingleResult(func)); 641 std::string access_token; 642 EXPECT_TRUE(value->GetAsString(&access_token)); 643 EXPECT_EQ(std::string(kAccessToken), access_token); 644 EXPECT_FALSE(func->login_ui_shown()); 645 EXPECT_FALSE(func->install_ui_shown()); 646} 647 648IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) { 649 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 650 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 651 func->set_extension(extension); 652 653 // Create a fake request to block the queue. 654 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 655 std::set<std::string> scopes(oauth2_info.scopes.begin(), 656 oauth2_info.scopes.end()); 657 IdentityAPI* id_api = 658 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 659 browser()->profile()); 660 IdentityMintRequestQueue* queue = id_api->mint_queue(); 661 MockQueuedMintRequest queued_request; 662 IdentityMintRequestQueue::MintType type = 663 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 664 665 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 666 queue->RequestStart(type, extension->id(), scopes, &queued_request); 667 668 // The real request will start processing, but wait in the queue behind 669 // the blocker. 670 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 671 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 672 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 673 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1)); 674 RunFunctionAsync(func, "[{\"interactive\": true}]"); 675 // Verify that we have fetched the login token and run the first flow. 676 testing::Mock::VerifyAndClearExpectations(func); 677 EXPECT_FALSE(func->install_ui_shown()); 678 679 // The UI will be displayed and the second flow will be created 680 // after the first queued request clears. 681 func->set_install_ui_result(true); 682 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 683 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 684 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow2)); 685 686 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 687 688 scoped_ptr<base::Value> value(WaitForSingleResult(func)); 689 std::string access_token; 690 EXPECT_TRUE(value->GetAsString(&access_token)); 691 EXPECT_EQ(std::string(kAccessToken), access_token); 692 EXPECT_FALSE(func->login_ui_shown()); 693 EXPECT_TRUE(func->install_ui_shown()); 694} 695 696IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 697 InteractiveQueuedNoninteractiveFails) { 698 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 699 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 700 func->set_extension(extension); 701 702 // Create a fake request to block the interactive queue. 703 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 704 std::set<std::string> scopes(oauth2_info.scopes.begin(), 705 oauth2_info.scopes.end()); 706 IdentityAPI* id_api = 707 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile( 708 browser()->profile()); 709 IdentityMintRequestQueue* queue = id_api->mint_queue(); 710 MockQueuedMintRequest queued_request; 711 IdentityMintRequestQueue::MintType type = 712 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 713 714 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 715 queue->RequestStart(type, extension->id(), scopes, &queued_request); 716 717 // Non-interactive requests fail without hitting GAIA, because a 718 // consent UI is known to be up. 719 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 720 std::string error = utils::RunFunctionAndReturnError( 721 func.get(), "[{}]", browser()); 722 EXPECT_EQ(std::string(errors::kNoGrant), error); 723 EXPECT_FALSE(func->login_ui_shown()); 724 EXPECT_FALSE(func->install_ui_shown()); 725 726 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 727} 728 729IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 730 NonInteractiveCacheHit) { 731 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 732 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 733 func->set_extension(extension); 734 735 // pre-populate the cache with a token 736 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 737 IdentityTokenCacheValue token(kAccessToken, 738 base::TimeDelta::FromSeconds(3600)); 739 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 740 741 // Get a token. Should not require a GAIA request. 742 EXPECT_CALL(*func.get(), HasLoginToken()) 743 .WillOnce(Return(true)); 744 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 745 func.get(), "[{}]", browser())); 746 std::string access_token; 747 EXPECT_TRUE(value->GetAsString(&access_token)); 748 EXPECT_EQ(std::string(kAccessToken), access_token); 749 EXPECT_FALSE(func->login_ui_shown()); 750 EXPECT_FALSE(func->install_ui_shown()); 751} 752 753IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 754 NonInteractiveIssueAdviceCacheHit) { 755 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 756 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 757 func->set_extension(extension); 758 759 // pre-populate the cache with advice 760 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 761 IssueAdviceInfo info; 762 IdentityTokenCacheValue token(info); 763 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 764 765 // Should return an error without a GAIA request. 766 EXPECT_CALL(*func.get(), HasLoginToken()) 767 .WillOnce(Return(true)); 768 std::string error = utils::RunFunctionAndReturnError( 769 func.get(), "[{}]", browser()); 770 EXPECT_EQ(std::string(errors::kNoGrant), error); 771 EXPECT_FALSE(func->login_ui_shown()); 772 EXPECT_FALSE(func->install_ui_shown()); 773} 774 775IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 776 InteractiveCacheHit) { 777 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 778 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 779 func->set_extension(extension); 780 781 // Create a fake request to block the queue. 782 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 783 std::set<std::string> scopes(oauth2_info.scopes.begin(), 784 oauth2_info.scopes.end()); 785 IdentityMintRequestQueue* queue = id_api()->mint_queue(); 786 MockQueuedMintRequest queued_request; 787 IdentityMintRequestQueue::MintType type = 788 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE; 789 790 EXPECT_CALL(queued_request, StartMintToken(type)).Times(1); 791 queue->RequestStart(type, extension->id(), scopes, &queued_request); 792 793 // The real request will start processing, but wait in the queue behind 794 // the blocker. 795 EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true)); 796 TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow( 797 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 798 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow)); 799 RunFunctionAsync(func, "[{\"interactive\": true}]"); 800 801 // Populate the cache with a token while the request is blocked. 802 IdentityTokenCacheValue token(kAccessToken, 803 base::TimeDelta::FromSeconds(3600)); 804 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 805 806 // When we wake up the request, it returns the cached token without 807 // displaying a UI, or hitting GAIA. 808 809 queue->RequestComplete(type, extension->id(), scopes, &queued_request); 810 811 scoped_ptr<base::Value> value(WaitForSingleResult(func)); 812 std::string access_token; 813 EXPECT_TRUE(value->GetAsString(&access_token)); 814 EXPECT_EQ(std::string(kAccessToken), access_token); 815 EXPECT_FALSE(func->login_ui_shown()); 816 EXPECT_FALSE(func->install_ui_shown()); 817} 818 819IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, 820 LoginInvalidatesTokenCache) { 821 scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction()); 822 scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); 823 func->set_extension(extension); 824 825 // pre-populate the cache with a token 826 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension); 827 IdentityTokenCacheValue token(kAccessToken, 828 base::TimeDelta::FromSeconds(3600)); 829 id_api()->SetCachedToken(extension->id(), oauth2_info.scopes, token); 830 831 // Because the user is not signed in, the token will be removed, 832 // and we'll hit GAIA for new tokens. 833 EXPECT_CALL(*func.get(), HasLoginToken()) 834 .WillOnce(Return(false)); 835 func->set_login_ui_result(true); 836 TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow( 837 TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get()); 838 TestOAuth2MintTokenFlow* flow2 = new TestOAuth2MintTokenFlow( 839 TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get()); 840 EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)) 841 .WillOnce(Return(flow1)) 842 .WillOnce(Return(flow2)); 843 844 func->set_install_ui_result(true); 845 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 846 func.get(), "[{\"interactive\": true}]", browser())); 847 std::string access_token; 848 EXPECT_TRUE(value->GetAsString(&access_token)); 849 EXPECT_EQ(std::string(kAccessToken), access_token); 850 EXPECT_TRUE(func->login_ui_shown()); 851 EXPECT_TRUE(func->install_ui_shown()); 852 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 853 id_api()->GetCachedToken(extension->id(), 854 oauth2_info.scopes).status()); 855} 856 857class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest { 858 protected: 859 bool InvalidateDefaultToken() { 860 scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func( 861 new IdentityRemoveCachedAuthTokenFunction); 862 func->set_extension(utils::CreateEmptyExtension(kExtensionId)); 863 return utils::RunFunction( 864 func, std::string("[{\"token\": \"") + kAccessToken + "\"}]", browser(), 865 extension_function_test_utils::NONE); 866 } 867 868 IdentityAPI* id_api() { 869 return IdentityAPI::GetFactoryInstance()->GetForProfile( 870 browser()->profile()); 871 } 872 873 void SetCachedToken(IdentityTokenCacheValue& token_data) { 874 id_api()->SetCachedToken(extensions::id_util::GenerateId(kExtensionId), 875 std::vector<std::string>(), token_data); 876 } 877 878 const IdentityTokenCacheValue& GetCachedToken() { 879 return id_api()->GetCachedToken( 880 extensions::id_util::GenerateId(kExtensionId), 881 std::vector<std::string>()); 882 } 883}; 884 885IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) { 886 EXPECT_TRUE(InvalidateDefaultToken()); 887 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 888 GetCachedToken().status()); 889} 890 891IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) { 892 IssueAdviceInfo info; 893 IdentityTokenCacheValue advice(info); 894 SetCachedToken(advice); 895 EXPECT_TRUE(InvalidateDefaultToken()); 896 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, 897 GetCachedToken().status()); 898} 899 900IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) { 901 IdentityTokenCacheValue token("non_matching_token", 902 base::TimeDelta::FromSeconds(3600)); 903 SetCachedToken(token); 904 EXPECT_TRUE(InvalidateDefaultToken()); 905 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, 906 GetCachedToken().status()); 907 EXPECT_EQ("non_matching_token", GetCachedToken().token()); 908} 909 910IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) { 911 IdentityTokenCacheValue token(kAccessToken, 912 base::TimeDelta::FromSeconds(3600)); 913 SetCachedToken(token); 914 EXPECT_TRUE(InvalidateDefaultToken()); 915 EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, 916 GetCachedToken().status()); 917} 918 919class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest { 920}; 921 922IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) { 923 content::WindowedNotificationObserver observer( 924 chrome::NOTIFICATION_BROWSER_WINDOW_READY, 925 content::NotificationService::AllSources()); 926 927 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 928 new IdentityLaunchWebAuthFlowFunction()); 929 930 RunFunctionAsync( 931 function, "[{\"interactive\": true, \"url\": \"data:text/html,auth\"}]"); 932 933 observer.Wait(); 934 Browser* web_auth_flow_browser = 935 content::Source<Browser>(observer.source()).ptr(); 936 web_auth_flow_browser->window()->Close(); 937 938 EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function)); 939} 940 941IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) { 942 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 943 new IdentityLaunchWebAuthFlowFunction()); 944 scoped_refptr<Extension> empty_extension( 945 utils::CreateEmptyExtension()); 946 function->set_extension(empty_extension.get()); 947 948 std::string error = utils::RunFunctionAndReturnError( 949 function, "[{\"interactive\": false, \"url\": \"data:text/html,auth\"}]", 950 browser()); 951 952 EXPECT_EQ(std::string(errors::kInteractionRequired), error); 953} 954 955IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) { 956 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 957 new IdentityLaunchWebAuthFlowFunction()); 958 scoped_refptr<Extension> empty_extension( 959 utils::CreateEmptyExtension()); 960 function->set_extension(empty_extension.get()); 961 962 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 963 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 964 function, 965 "[{\"interactive\": false," 966 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 967 browser())); 968 969 std::string url; 970 EXPECT_TRUE(value->GetAsString(&url)); 971 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 972 url); 973} 974 975IN_PROC_BROWSER_TEST_F( 976 LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) { 977 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 978 new IdentityLaunchWebAuthFlowFunction()); 979 scoped_refptr<Extension> empty_extension( 980 utils::CreateEmptyExtension()); 981 function->set_extension(empty_extension.get()); 982 983 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 984 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 985 function, 986 "[{\"interactive\": true," 987 "\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]", 988 browser())); 989 990 std::string url; 991 EXPECT_TRUE(value->GetAsString(&url)); 992 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 993 url); 994} 995 996IN_PROC_BROWSER_TEST_F( 997 LaunchWebAuthFlowFunctionTest, InteractiveSecondNavigationSuccess) { 998 scoped_refptr<IdentityLaunchWebAuthFlowFunction> function( 999 new IdentityLaunchWebAuthFlowFunction()); 1000 scoped_refptr<Extension> empty_extension( 1001 utils::CreateEmptyExtension()); 1002 function->set_extension(empty_extension.get()); 1003 1004 function->InitFinalRedirectURLPrefixesForTest("abcdefghij"); 1005 scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( 1006 function, 1007 "[{\"interactive\": true," 1008 "\"url\": \"data:text/html,<script>window.location.replace('" 1009 "https://abcdefghij.chromiumapp.org/callback#test')</script>\"}]", 1010 browser())); 1011 1012 std::string url; 1013 EXPECT_TRUE(value->GetAsString(&url)); 1014 EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"), 1015 url); 1016} 1017 1018} // namespace extensions 1019