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.
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"
20 using bookmarks_helper::AddURL;
21 using sync_integration_test_util::AwaitCommitActivityCompletion;
23 const char kShortLivedOAuth2Token[] =
25 " \"refresh_token\": \"short_lived_refresh_token\","
26 " \"access_token\": \"short_lived_access_token\","
27 " \"expires_in\": 5," // 5 seconds.
28 " \"token_type\": \"Bearer\""
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\""
38 const char kInvalidGrantOAuth2Token[] = "{"
39 " \"error\": \"invalid_grant\""
42 const char kInvalidClientOAuth2Token[] = "{"
43 " \"error\": \"invalid_client\""
46 const char kEmptyOAuth2Token[] = "";
48 const char kMalformedOAuth2Token[] = "{ \"foo\": ";
50 class TestForAuthError : public SingleClientStatusChangeChecker {
52 explicit TestForAuthError(ProfileSyncService* service);
53 virtual ~TestForAuthError();
54 virtual bool IsExitConditionSatisfied() OVERRIDE;
55 virtual std::string GetDebugMessage() const OVERRIDE;
58 TestForAuthError::TestForAuthError(ProfileSyncService* service)
59 : SingleClientStatusChangeChecker(service) {}
61 TestForAuthError::~TestForAuthError() {}
63 bool TestForAuthError::IsExitConditionSatisfied() {
64 return !service()->HasUnsyncedItems() ||
65 (service()->GetSyncTokenStatus().last_get_token_error.state() !=
66 GoogleServiceAuthError::NONE);
69 std::string TestForAuthError::GetDebugMessage() const {
70 return "Waiting for auth error";
73 class SyncAuthTest : public SyncTest {
75 SyncAuthTest() : SyncTest(SINGLE_CLIENT), bookmark_index_(0) {}
76 virtual ~SyncAuthTest() {}
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);
87 // Run until the bookmark is committed or an auth error is encountered.
88 TestForAuthError checker_(GetSyncService((0)));
91 GoogleServiceAuthError oauth_error =
92 GetSyncService((0))->GetSyncTokenStatus().last_get_token_error;
94 return oauth_error.state() != GoogleServiceAuthError::NONE;
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);
111 int GetNextBookmarkIndex() {
112 return bookmark_index_++;
117 DISALLOW_COPY_AND_ASSIGN(SyncAuthTest);
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,
127 net::URLRequestStatus::SUCCESS);
128 ASSERT_FALSE(AttemptToTriggerAuthError());
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());
144 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
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,
157 net::URLRequestStatus::SUCCESS);
158 ASSERT_TRUE(AttemptToTriggerAuthError());
160 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
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());
175 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
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,
187 net::URLRequestStatus::SUCCESS);
188 ASSERT_TRUE(AttemptToTriggerAuthError());
190 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
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());
209 // Verify that ProfileSyncService ends up with an 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, InvalidClient) {
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_EQ(GoogleServiceAuthError::SERVICE_ERROR,
222 GetSyncService((0))->GetAuthError().state());
225 // Verify that ProfileSyncService ends up with a REQUEST_CANCELED auth error
226 // when when OAuth2TokenService has encountered a URLRequestStatus of CANCELED.
227 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RequestCanceled) {
228 ASSERT_TRUE(SetupSync());
229 ASSERT_FALSE(AttemptToTriggerAuthError());
230 GetFakeServer()->SetUnauthenticated();
231 DisableTokenFetchRetries();
232 SetOAuth2TokenResponse(kEmptyOAuth2Token,
233 net::HTTP_INTERNAL_SERVER_ERROR,
234 net::URLRequestStatus::CANCELED);
235 ASSERT_TRUE(AttemptToTriggerAuthError());
236 ASSERT_EQ(GoogleServiceAuthError::REQUEST_CANCELED,
237 GetSyncService((0))->GetAuthError().state());
240 // Verify that ProfileSyncService fails initial sync setup during backend
241 // initialization and ends up with an INVALID_GAIA_CREDENTIALS auth error when
242 // an invalid_grant error is returned by OAuth2TokenService with an
243 // HTTP_BAD_REQUEST (400) response code.
244 IN_PROC_BROWSER_TEST_F(SyncAuthTest, FailInitialSetupWithPersistentError) {
245 ASSERT_TRUE(SetupClients());
246 GetFakeServer()->SetUnauthenticated();
247 DisableTokenFetchRetries();
248 SetOAuth2TokenResponse(kInvalidGrantOAuth2Token,
249 net::HTTP_BAD_REQUEST,
250 net::URLRequestStatus::SUCCESS);
251 ASSERT_FALSE(GetClient(0)->SetupSync());
252 ASSERT_FALSE(GetSyncService((0))->sync_initialized());
253 ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
254 GetSyncService((0))->GetAuthError().state());
257 // Verify that ProfileSyncService fails initial sync setup during backend
258 // initialization, but continues trying to fetch access tokens when
259 // OAuth2TokenService receives an HTTP_INTERNAL_SERVER_ERROR (500) response
261 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryInitialSetupWithTransientError) {
262 ASSERT_TRUE(SetupClients());
263 GetFakeServer()->SetUnauthenticated();
264 DisableTokenFetchRetries();
265 SetOAuth2TokenResponse(kEmptyOAuth2Token,
266 net::HTTP_INTERNAL_SERVER_ERROR,
267 net::URLRequestStatus::SUCCESS);
268 ASSERT_FALSE(GetClient(0)->SetupSync());
269 ASSERT_FALSE(GetSyncService((0))->sync_initialized());
271 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
274 // Verify that ProfileSyncService fetches a new token when an old token expires.
275 IN_PROC_BROWSER_TEST_F(SyncAuthTest, TokenExpiry) {
276 // Initial sync succeeds with a short lived OAuth2 Token.
277 ASSERT_TRUE(SetupClients());
278 GetFakeServer()->SetAuthenticated();
279 DisableTokenFetchRetries();
280 SetOAuth2TokenResponse(kShortLivedOAuth2Token,
282 net::URLRequestStatus::SUCCESS);
283 ASSERT_TRUE(GetClient(0)->SetupSync());
284 std::string old_token = GetSyncService((0))->GetAccessTokenForTest();
286 // Wait until the token has expired.
287 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
289 // Trigger an auth error on the server so PSS requests OA2TS for a new token
290 // during the next sync cycle.
291 GetFakeServer()->SetUnauthenticated();
292 SetOAuth2TokenResponse(kEmptyOAuth2Token,
293 net::HTTP_INTERNAL_SERVER_ERROR,
294 net::URLRequestStatus::SUCCESS);
295 ASSERT_TRUE(AttemptToTriggerAuthError());
297 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
299 // Trigger an auth success state and set up a new valid OAuth2 token.
300 GetFakeServer()->SetAuthenticated();
301 SetOAuth2TokenResponse(kValidOAuth2Token,
303 net::URLRequestStatus::SUCCESS);
305 // Verify that the next sync cycle is successful, and uses the new auth token.
306 ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
307 std::string new_token = GetSyncService((0))->GetAccessTokenForTest();
308 ASSERT_NE(old_token, new_token);