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