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 ~TestForAuthError() override
;
54 bool IsExitConditionSatisfied() override
;
55 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 ~SyncAuthTest() override
{}
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 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());
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());
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))->IsSyncActive());
251 ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
,
252 GetSyncService((0))->GetAuthError().state());
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
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))->IsSyncActive());
269 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
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
,
280 net::URLRequestStatus::SUCCESS
);
281 ASSERT_TRUE(GetClient(0)->SetupSync());
282 std::string old_token
= GetSyncService((0))->GetAccessTokenForTest();
284 // Wait until the token has expired.
285 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
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());
295 GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
297 // Trigger an auth success state and set up a new valid OAuth2 token.
298 GetFakeServer()->SetAuthenticated();
299 SetOAuth2TokenResponse(kValidOAuth2Token
,
301 net::URLRequestStatus::SUCCESS
);
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
);