1 // Copyright (c) 2012 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 "chrome/browser/signin/signin_tracker.h"
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/signin/signin_manager.h"
9 #include "chrome/browser/signin/signin_manager_factory.h"
10 #include "chrome/browser/signin/token_service.h"
11 #include "chrome/browser/signin/token_service_factory.h"
12 #include "chrome/browser/sync/profile_sync_service.h"
13 #include "chrome/browser/sync/profile_sync_service_factory.h"
14 #include "chrome/common/chrome_notification_types.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_source.h"
17 #include "google_apis/gaia/gaia_constants.h"
19 static const char* kSignedInServices
[] = {
20 GaiaConstants::kSyncService
,
21 GaiaConstants::kGaiaOAuth2LoginRefreshToken
23 static const int kNumSignedInServices
=
24 arraysize(kSignedInServices
);
26 // Helper to check if the given token service is relevant for sync.
27 SigninTracker::SigninTracker(Profile
* profile
, Observer
* observer
)
28 : state_(WAITING_FOR_GAIA_VALIDATION
),
31 credentials_valid_(false) {
35 SigninTracker::SigninTracker(Profile
* profile
,
41 credentials_valid_(false) {
45 SigninTracker::~SigninTracker() {
46 ProfileSyncService
* service
=
47 ProfileSyncServiceFactory::GetForProfile(profile_
);
49 service
->RemoveObserver(this);
52 void SigninTracker::Initialize() {
54 // Register for notifications from the SigninManager.
56 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
,
57 content::Source
<Profile
>(profile_
));
59 chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED
,
60 content::Source
<Profile
>(profile_
));
61 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile_
);
63 chrome::NOTIFICATION_TOKEN_AVAILABLE
,
64 content::Source
<TokenService
>(token_service
));
66 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED
,
67 content::Source
<TokenService
>(token_service
));
69 // Also listen for notifications from the various signed in services (only
71 ProfileSyncService
* service
=
72 ProfileSyncServiceFactory::GetForProfile(profile_
);
74 service
->AddObserver(this);
76 if (state_
== SERVICES_INITIALIZING
)
77 HandleServiceStateChange();
80 void SigninTracker::Observe(int type
,
81 const content::NotificationSource
& source
,
82 const content::NotificationDetails
& details
) {
83 // We should not get more than one of these notifications.
85 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL
:
86 DCHECK_EQ(state_
, WAITING_FOR_GAIA_VALIDATION
);
87 state_
= SERVICES_INITIALIZING
;
88 observer_
->GaiaCredentialsValid();
89 // If our services are already signed in, see if it's possible to
90 // transition to the SIGNIN_COMPLETE state.
91 if (AreServicesSignedIn(profile_
))
92 HandleServiceStateChange();
94 case chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED
: {
95 DCHECK_EQ(state_
, WAITING_FOR_GAIA_VALIDATION
);
96 const GoogleServiceAuthError
& error
=
97 *(content::Details
<const GoogleServiceAuthError
>(details
).ptr());
98 observer_
->SigninFailed(error
);
101 case chrome::NOTIFICATION_TOKEN_AVAILABLE
:
102 // A new token is available - check to see if we're all signed in now.
103 HandleServiceStateChange();
105 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED
:
106 if (state_
== SERVICES_INITIALIZING
) {
107 const TokenService::TokenRequestFailedDetails
& token_details
=
108 *(content::Details
<const TokenService::TokenRequestFailedDetails
>(
110 for (int i
= 0; i
< kNumSignedInServices
; ++i
) {
111 if (token_details
.service() == kSignedInServices
[i
]) {
112 // We got an error loading one of our tokens, so notify our
114 state_
= WAITING_FOR_GAIA_VALIDATION
;
115 observer_
->SigninFailed(token_details
.error());
125 // Called when the ProfileSyncService state changes.
126 void SigninTracker::OnStateChanged() {
127 HandleServiceStateChange();
130 void SigninTracker::HandleServiceStateChange() {
131 if (state_
!= SERVICES_INITIALIZING
) {
132 // Ignore service updates until after our GAIA credentials are validated.
136 GoogleServiceAuthError
error(GoogleServiceAuthError::NONE
);
137 state_
= GetSigninState(profile_
, &error
);
139 case WAITING_FOR_GAIA_VALIDATION
:
140 observer_
->SigninFailed(error
);
142 case SIGNIN_COMPLETE
:
143 observer_
->SigninSuccess();
146 // State has not changed, nothing to do.
147 DCHECK_EQ(state_
, SERVICES_INITIALIZING
);
153 bool SigninTracker::AreServiceTokensLoaded(Profile
* profile
) {
154 // See if we have all of the tokens required.
155 TokenService
* token_service
= TokenServiceFactory::GetForProfile(profile
);
156 for (int i
= 0; i
< kNumSignedInServices
; ++i
) {
157 if (!token_service
->HasTokenForService(kSignedInServices
[i
])) {
158 // Don't have a token for one of our signed-in services.
166 bool SigninTracker::AreServicesSignedIn(Profile
* profile
) {
167 if (!AreServiceTokensLoaded(profile
))
169 // Don't care about the sync state if sync is disabled by policy.
170 if (!profile
->IsSyncAccessible())
172 ProfileSyncService
* service
=
173 ProfileSyncServiceFactory::GetForProfile(profile
);
174 // Check the sync service state - we ignore CONNECTION_FAILED errors here
175 // because they are transient and do not signify a failure of the signin
177 return (service
->IsSyncEnabledAndLoggedIn() &&
178 service
->IsSyncTokenAvailable() &&
179 (service
->GetAuthError().state() == GoogleServiceAuthError::NONE
||
180 service
->GetAuthError().state() ==
181 GoogleServiceAuthError::CONNECTION_FAILED
) &&
182 !service
->HasUnrecoverableError());
186 SigninTracker::LoginState
SigninTracker::GetSigninState(
188 GoogleServiceAuthError
* error
) {
189 SigninManagerBase
* signin
= SigninManagerFactory::GetForProfile(profile
);
190 if (signin
->GetAuthenticatedUsername().empty()) {
191 // User is signed out, trigger a signin failure.
193 *error
= GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED
);
194 return WAITING_FOR_GAIA_VALIDATION
;
197 // Wait until all of our services are logged in. For now this just means sync.
198 // Long term, we should separate out service auth failures from the signin
199 // process, but for the current UI flow we'll validate service signin status
201 ProfileSyncService
* service
= profile
->IsSyncAccessible() ?
202 ProfileSyncServiceFactory::GetForProfile(profile
) : NULL
;
203 if (service
&& service
->waiting_for_auth()) {
204 // Still waiting for an auth token to come in so stay in the INITIALIZING
205 // state (we do this to avoid triggering an early signin error in the case
206 // where there's a previous auth error in the sync service that hasn't
207 // been cleared yet).
208 return SERVICES_INITIALIZING
;
211 // If we haven't loaded all our service tokens yet, just exit (we'll be called
212 // again when another token is loaded, or will transition to SigninFailed if
213 // the loading fails).
214 if (!AreServiceTokensLoaded(profile
))
215 return SERVICES_INITIALIZING
;
216 if (!AreServicesSignedIn(profile
)) {
218 *error
= signin
->signin_global_error()->GetLastAuthError();
219 return WAITING_FOR_GAIA_VALIDATION
;
222 if (!service
|| service
->sync_initialized())
223 return SIGNIN_COMPLETE
;
225 return SERVICES_INITIALIZING
;