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