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