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