Home | History | Annotate | Download | only in integration
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/strings/stringprintf.h"
      6 #include "base/threading/platform_thread.h"
      7 #include "base/time/time.h"
      8 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
      9 #include "chrome/browser/sync/profile_sync_service.h"
     10 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
     11 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
     12 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
     13 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
     14 #include "chrome/browser/sync/test/integration/sync_test.h"
     15 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     16 #include "google_apis/gaia/google_service_auth_error.h"
     17 #include "net/http/http_status_code.h"
     18 #include "net/url_request/url_request_status.h"
     19 
     20 using bookmarks_helper::AddURL;
     21 using sync_integration_test_util::AwaitCommitActivityCompletion;
     22 
     23 const char kShortLivedOAuth2Token[] =
     24     "{"
     25     "  \"refresh_token\": \"short_lived_refresh_token\","
     26     "  \"access_token\": \"short_lived_access_token\","
     27     "  \"expires_in\": 5,"  // 5 seconds.
     28     "  \"token_type\": \"Bearer\""
     29     "}";
     30 
     31 const char kValidOAuth2Token[] = "{"
     32                                  "  \"refresh_token\": \"new_refresh_token\","
     33                                  "  \"access_token\": \"new_access_token\","
     34                                  "  \"expires_in\": 3600,"  // 1 hour.
     35                                  "  \"token_type\": \"Bearer\""
     36                                  "}";
     37 
     38 const char kInvalidGrantOAuth2Token[] = "{"
     39                                         "  \"error\": \"invalid_grant\""
     40                                         "}";
     41 
     42 const char kInvalidClientOAuth2Token[] = "{"
     43                                          "  \"error\": \"invalid_client\""
     44                                          "}";
     45 
     46 const char kEmptyOAuth2Token[] = "";
     47 
     48 const char kMalformedOAuth2Token[] = "{ \"foo\": ";
     49 
     50 class TestForAuthError : public SingleClientStatusChangeChecker {
     51  public:
     52   explicit TestForAuthError(ProfileSyncService* service);
     53   virtual ~TestForAuthError();
     54   virtual bool IsExitConditionSatisfied() OVERRIDE;
     55   virtual std::string GetDebugMessage() const OVERRIDE;
     56 };
     57 
     58 TestForAuthError::TestForAuthError(ProfileSyncService* service)
     59   : SingleClientStatusChangeChecker(service) {}
     60 
     61 TestForAuthError::~TestForAuthError() {}
     62 
     63 bool TestForAuthError::IsExitConditionSatisfied() {
     64   return !service()->HasUnsyncedItems() ||
     65       (service()->GetSyncTokenStatus().last_get_token_error.state() !=
     66        GoogleServiceAuthError::NONE);
     67 }
     68 
     69 std::string TestForAuthError::GetDebugMessage() const {
     70   return "Waiting for auth error";
     71 }
     72 
     73 class SyncAuthTest : public SyncTest {
     74  public:
     75   SyncAuthTest() : SyncTest(SINGLE_CLIENT), bookmark_index_(0) {}
     76   virtual ~SyncAuthTest() {}
     77 
     78   // Helper function that adds a bookmark and waits for either an auth error, or
     79   // for the bookmark to be committed.  Returns true if it detects an auth
     80   // error, false if the bookmark is committed successfully.
     81   bool AttemptToTriggerAuthError() {
     82     int bookmark_index = GetNextBookmarkIndex();
     83     std::string title = base::StringPrintf("Bookmark %d", bookmark_index);
     84     GURL url = GURL(base::StringPrintf("http://www.foo%d.com", bookmark_index));
     85     EXPECT_TRUE(AddURL(0, title, url) != NULL);
     86 
     87     // Run until the bookmark is committed or an auth error is encountered.
     88     TestForAuthError checker_(GetSyncService((0)));
     89     checker_.Wait();
     90 
     91     GoogleServiceAuthError oauth_error =
     92         GetSyncService((0))->GetSyncTokenStatus().last_get_token_error;
     93 
     94     return oauth_error.state() != GoogleServiceAuthError::NONE;
     95   }
     96 
     97   void DisableTokenFetchRetries() {
     98     // If ProfileSyncService observes a transient error like SERVICE_UNAVAILABLE
     99     // or CONNECTION_FAILED, this means the OAuth2TokenService has given up
    100     // trying to reach Gaia. In practice, OA2TS retries a fixed number of times,
    101     // but the count is transparent to PSS.
    102     // Override the max retry count in TokenService so that we instantly trigger
    103     // the case where ProfileSyncService must pick up where OAuth2TokenService
    104     // left off (in terms of retries).
    105     ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile(0))->
    106         set_max_authorization_token_fetch_retries_for_testing(0);
    107   }
    108 
    109 
    110  private:
    111   int GetNextBookmarkIndex() {
    112     return bookmark_index_++;
    113   }
    114 
    115   int bookmark_index_;
    116 
    117   DISALLOW_COPY_AND_ASSIGN(SyncAuthTest);
    118 };
    119 
    120 // Verify that sync works with a valid OAuth2 token.
    121 IN_PROC_BROWSER_TEST_F(SyncAuthTest, Sanity) {
    122   ASSERT_TRUE(SetupSync());
    123   GetFakeServer()->SetAuthenticated();
    124   DisableTokenFetchRetries();
    125   SetOAuth2TokenResponse(kValidOAuth2Token,
    126                          net::HTTP_OK,
    127                          net::URLRequestStatus::SUCCESS);
    128   ASSERT_FALSE(AttemptToTriggerAuthError());
    129 }
    130 
    131 // Verify that ProfileSyncService continues trying to fetch access tokens
    132 // when OAuth2TokenService has encountered more than a fixed number of
    133 // HTTP_INTERNAL_SERVER_ERROR (500) errors.
    134 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnInternalServerError500) {
    135   ASSERT_TRUE(SetupSync());
    136   ASSERT_FALSE(AttemptToTriggerAuthError());
    137   GetFakeServer()->SetUnauthenticated();
    138   DisableTokenFetchRetries();
    139   SetOAuth2TokenResponse(kValidOAuth2Token,
    140                          net::HTTP_INTERNAL_SERVER_ERROR,
    141                          net::URLRequestStatus::SUCCESS);
    142   ASSERT_TRUE(AttemptToTriggerAuthError());
    143   ASSERT_TRUE(
    144       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    145 }
    146 
    147 // Verify that ProfileSyncService continues trying to fetch access tokens
    148 // when OAuth2TokenService has encountered more than a fixed number of
    149 // HTTP_FORBIDDEN (403) errors.
    150 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnHttpForbidden403) {
    151   ASSERT_TRUE(SetupSync());
    152   ASSERT_FALSE(AttemptToTriggerAuthError());
    153   GetFakeServer()->SetUnauthenticated();
    154   DisableTokenFetchRetries();
    155   SetOAuth2TokenResponse(kEmptyOAuth2Token,
    156                          net::HTTP_FORBIDDEN,
    157                          net::URLRequestStatus::SUCCESS);
    158   ASSERT_TRUE(AttemptToTriggerAuthError());
    159   ASSERT_TRUE(
    160       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    161 }
    162 
    163 // Verify that ProfileSyncService continues trying to fetch access tokens
    164 // when OAuth2TokenService has encountered a URLRequestStatus of FAILED.
    165 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnRequestFailed) {
    166   ASSERT_TRUE(SetupSync());
    167   ASSERT_FALSE(AttemptToTriggerAuthError());
    168   GetFakeServer()->SetUnauthenticated();
    169   DisableTokenFetchRetries();
    170   SetOAuth2TokenResponse(kEmptyOAuth2Token,
    171                          net::HTTP_INTERNAL_SERVER_ERROR,
    172                          net::URLRequestStatus::FAILED);
    173   ASSERT_TRUE(AttemptToTriggerAuthError());
    174   ASSERT_TRUE(
    175       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    176 }
    177 
    178 // Verify that ProfileSyncService continues trying to fetch access tokens
    179 // when OAuth2TokenService receives a malformed token.
    180 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnMalformedToken) {
    181   ASSERT_TRUE(SetupSync());
    182   ASSERT_FALSE(AttemptToTriggerAuthError());
    183   GetFakeServer()->SetUnauthenticated();
    184   DisableTokenFetchRetries();
    185   SetOAuth2TokenResponse(kMalformedOAuth2Token,
    186                          net::HTTP_OK,
    187                          net::URLRequestStatus::SUCCESS);
    188   ASSERT_TRUE(AttemptToTriggerAuthError());
    189   ASSERT_TRUE(
    190       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    191 }
    192 
    193 // Verify that ProfileSyncService ends up with an INVALID_GAIA_CREDENTIALS auth
    194 // error when an invalid_grant error is returned by OAuth2TokenService with an
    195 // HTTP_BAD_REQUEST (400) response code.
    196 IN_PROC_BROWSER_TEST_F(SyncAuthTest, InvalidGrant) {
    197   ASSERT_TRUE(SetupSync());
    198   ASSERT_FALSE(AttemptToTriggerAuthError());
    199   GetFakeServer()->SetUnauthenticated();
    200   DisableTokenFetchRetries();
    201   SetOAuth2TokenResponse(kInvalidGrantOAuth2Token,
    202                          net::HTTP_BAD_REQUEST,
    203                          net::URLRequestStatus::SUCCESS);
    204   ASSERT_TRUE(AttemptToTriggerAuthError());
    205   ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
    206             GetSyncService((0))->GetAuthError().state());
    207 }
    208 
    209 // Verify that ProfileSyncService retries after SERVICE_ERROR auth error when
    210 // an invalid_client error is returned by OAuth2TokenService with an
    211 // HTTP_BAD_REQUEST (400) response code.
    212 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryInvalidClient) {
    213   ASSERT_TRUE(SetupSync());
    214   ASSERT_FALSE(AttemptToTriggerAuthError());
    215   GetFakeServer()->SetUnauthenticated();
    216   DisableTokenFetchRetries();
    217   SetOAuth2TokenResponse(kInvalidClientOAuth2Token,
    218                          net::HTTP_BAD_REQUEST,
    219                          net::URLRequestStatus::SUCCESS);
    220   ASSERT_TRUE(AttemptToTriggerAuthError());
    221   ASSERT_TRUE(GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    222 }
    223 
    224 // Verify that ProfileSyncService retries after REQUEST_CANCELED auth error
    225 // when OAuth2TokenService has encountered a URLRequestStatus of CANCELED.
    226 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryRequestCanceled) {
    227   ASSERT_TRUE(SetupSync());
    228   ASSERT_FALSE(AttemptToTriggerAuthError());
    229   GetFakeServer()->SetUnauthenticated();
    230   DisableTokenFetchRetries();
    231   SetOAuth2TokenResponse(kEmptyOAuth2Token,
    232                          net::HTTP_INTERNAL_SERVER_ERROR,
    233                          net::URLRequestStatus::CANCELED);
    234   ASSERT_TRUE(AttemptToTriggerAuthError());
    235   ASSERT_TRUE(GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    236 }
    237 
    238 // Verify that ProfileSyncService fails initial sync setup during backend
    239 // initialization and ends up with an INVALID_GAIA_CREDENTIALS auth error when
    240 // an invalid_grant error is returned by OAuth2TokenService with an
    241 // HTTP_BAD_REQUEST (400) response code.
    242 IN_PROC_BROWSER_TEST_F(SyncAuthTest, FailInitialSetupWithPersistentError) {
    243   ASSERT_TRUE(SetupClients());
    244   GetFakeServer()->SetUnauthenticated();
    245   DisableTokenFetchRetries();
    246   SetOAuth2TokenResponse(kInvalidGrantOAuth2Token,
    247                          net::HTTP_BAD_REQUEST,
    248                          net::URLRequestStatus::SUCCESS);
    249   ASSERT_FALSE(GetClient(0)->SetupSync());
    250   ASSERT_FALSE(GetSyncService((0))->sync_initialized());
    251   ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
    252             GetSyncService((0))->GetAuthError().state());
    253 }
    254 
    255 // Verify that ProfileSyncService fails initial sync setup during backend
    256 // initialization, but continues trying to fetch access tokens when
    257 // OAuth2TokenService receives an HTTP_INTERNAL_SERVER_ERROR (500) response
    258 // code.
    259 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryInitialSetupWithTransientError) {
    260   ASSERT_TRUE(SetupClients());
    261   GetFakeServer()->SetUnauthenticated();
    262   DisableTokenFetchRetries();
    263   SetOAuth2TokenResponse(kEmptyOAuth2Token,
    264                          net::HTTP_INTERNAL_SERVER_ERROR,
    265                          net::URLRequestStatus::SUCCESS);
    266   ASSERT_FALSE(GetClient(0)->SetupSync());
    267   ASSERT_FALSE(GetSyncService((0))->sync_initialized());
    268   ASSERT_TRUE(
    269       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    270 }
    271 
    272 // Verify that ProfileSyncService fetches a new token when an old token expires.
    273 IN_PROC_BROWSER_TEST_F(SyncAuthTest, TokenExpiry) {
    274   // Initial sync succeeds with a short lived OAuth2 Token.
    275   ASSERT_TRUE(SetupClients());
    276   GetFakeServer()->SetAuthenticated();
    277   DisableTokenFetchRetries();
    278   SetOAuth2TokenResponse(kShortLivedOAuth2Token,
    279                          net::HTTP_OK,
    280                          net::URLRequestStatus::SUCCESS);
    281   ASSERT_TRUE(GetClient(0)->SetupSync());
    282   std::string old_token = GetSyncService((0))->GetAccessTokenForTest();
    283 
    284   // Wait until the token has expired.
    285   base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
    286 
    287   // Trigger an auth error on the server so PSS requests OA2TS for a new token
    288   // during the next sync cycle.
    289   GetFakeServer()->SetUnauthenticated();
    290   SetOAuth2TokenResponse(kEmptyOAuth2Token,
    291                          net::HTTP_INTERNAL_SERVER_ERROR,
    292                          net::URLRequestStatus::SUCCESS);
    293   ASSERT_TRUE(AttemptToTriggerAuthError());
    294   ASSERT_TRUE(
    295       GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
    296 
    297   // Trigger an auth success state and set up a new valid OAuth2 token.
    298   GetFakeServer()->SetAuthenticated();
    299   SetOAuth2TokenResponse(kValidOAuth2Token,
    300                          net::HTTP_OK,
    301                          net::URLRequestStatus::SUCCESS);
    302 
    303   // Verify that the next sync cycle is successful, and uses the new auth token.
    304   ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
    305   std::string new_token = GetSyncService((0))->GetAccessTokenForTest();
    306   ASSERT_NE(old_token, new_token);
    307 }
    308