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