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