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