1 // Copyright 2014 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 "components/signin/core/browser/signin_manager.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "components/signin/core/browser/profile_oauth2_token_service.h"
17 #include "components/signin/core/browser/signin_account_id_helper.h"
18 #include "components/signin/core/browser/signin_client.h"
19 #include "components/signin/core/browser/signin_internals_util.h"
20 #include "components/signin/core/browser/signin_manager_cookie_helper.h"
21 #include "components/signin/core/browser/signin_metrics.h"
22 #include "components/signin/core/common/signin_pref_names.h"
23 #include "google_apis/gaia/gaia_auth_util.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "google_apis/gaia/gaia_urls.h"
26 #include "net/base/escape.h"
27 #include "third_party/icu/source/i18n/unicode/regex.h"
29 using namespace signin_internals_util;
33 const char kChromiumSyncService[] = "service=chromiumsync";
37 // Under the covers, we use a dummy chrome-extension ID to serve the purposes
38 // outlined in the .h file comment for this string.
39 const char SigninManager::kChromeSigninEffectiveSite[] =
40 "chrome-extension://acfccoigjajmmgbhpfbjnpckhjjegnih";
43 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
44 GURL effective(kChromeSigninEffectiveSite);
45 if (url.SchemeIs(effective.scheme().c_str()) &&
46 url.host() == effective.host()) {
50 GURL service_login(GaiaUrls::GetInstance()->service_login_url());
51 if (url.GetOrigin() != service_login.GetOrigin())
54 // Any login UI URLs with signin=chromiumsync should be considered a web
55 // URL (relies on GAIA keeping the "service=chromiumsync" query string
56 // fragment present even when embedding inside a "continue" parameter).
57 return net::UnescapeURLComponent(url.query(),
58 net::UnescapeRule::URL_SPECIAL_CHARS)
59 .find(kChromiumSyncService) != std::string::npos;
62 SigninManager::SigninManager(SigninClient* client,
63 ProfileOAuth2TokenService* token_service)
64 : SigninManagerBase(client),
65 prohibit_signout_(false),
66 type_(SIGNIN_TYPE_NONE),
68 token_service_(token_service),
69 weak_pointer_factory_(this) {}
71 void SigninManager::AddMergeSessionObserver(
72 MergeSessionHelper::Observer* observer) {
73 if (merge_session_helper_)
74 merge_session_helper_->AddObserver(observer);
77 void SigninManager::RemoveMergeSessionObserver(
78 MergeSessionHelper::Observer* observer) {
79 if (merge_session_helper_)
80 merge_session_helper_->RemoveObserver(observer);
83 SigninManager::~SigninManager() {}
85 void SigninManager::InitTokenService() {
86 if (token_service_ && IsAuthenticated())
87 token_service_->LoadCredentials(GetAuthenticatedAccountId());
90 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
92 case SIGNIN_TYPE_NONE:
94 case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
95 return "Signin with refresh token";
102 bool SigninManager::PrepareForSignin(SigninType type,
103 const std::string& username,
104 const std::string& password) {
105 DCHECK(possibly_invalid_username_.empty() ||
106 possibly_invalid_username_ == username);
107 DCHECK(!username.empty());
109 if (!IsAllowedUsername(username)) {
110 // Account is not allowed by admin policy.
112 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
116 // This attempt is either 1) the user trying to establish initial sync, or
117 // 2) trying to refresh credentials for an existing username. If it is 2, we
118 // need to try again, but take care to leave state around tracking that the
119 // user has successfully signed in once before with this username, so that on
120 // restart we don't think sync setup has never completed.
121 ClearTransientSigninData();
123 possibly_invalid_username_.assign(username);
124 password_.assign(password);
125 NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
129 void SigninManager::StartSignInWithRefreshToken(
130 const std::string& refresh_token,
131 const std::string& username,
132 const std::string& password,
133 const OAuthTokenFetchedCallback& callback) {
134 DCHECK(!IsAuthenticated() ||
135 gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
137 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password))
140 // Store our callback and token.
141 temp_refresh_token_ = refresh_token;
142 possibly_invalid_username_ = username;
144 NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
146 if (!callback.is_null() && !temp_refresh_token_.empty()) {
147 callback.Run(temp_refresh_token_);
149 // No oauth token or callback, so just complete our pending signin.
150 CompletePendingSignin();
154 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
155 DCHECK_NE(this, &source);
156 possibly_invalid_username_ = source.possibly_invalid_username_;
157 temp_refresh_token_ = source.temp_refresh_token_;
158 password_ = source.password_;
161 void SigninManager::ClearTransientSigninData() {
162 DCHECK(IsInitialized());
164 possibly_invalid_username_.clear();
166 type_ = SIGNIN_TYPE_NONE;
167 temp_refresh_token_.clear();
170 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
171 ClearTransientSigninData();
173 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
176 void SigninManager::SignOut(
177 signin_metrics::ProfileSignout signout_source_metric) {
178 DCHECK(IsInitialized());
180 signin_metrics::LogSignout(signout_source_metric);
181 if (!IsAuthenticated()) {
182 if (AuthInProgress()) {
183 // If the user is in the process of signing in, then treat a call to
184 // SignOut as a cancellation request.
185 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
186 HandleAuthError(error);
188 // Clean up our transient data and exit if we aren't signed in.
189 // This avoids a perf regression from clearing out the TokenDB if
190 // SignOut() is invoked on startup to clean up any incomplete previous
192 ClearTransientSigninData();
197 if (prohibit_signout_) {
198 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
202 ClearTransientSigninData();
204 const std::string account_id = GetAuthenticatedAccountId();
205 const std::string username = GetAuthenticatedUsername();
206 const base::Time signin_time =
207 base::Time::FromInternalValue(
208 client_->GetPrefs()->GetInt64(prefs::kSignedInTime));
209 ClearAuthenticatedUsername();
210 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
211 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
212 client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
213 client_->OnSignedOut();
215 // Erase (now) stale information from AboutSigninInternals.
216 NotifyDiagnosticsObservers(USERNAME, "");
218 // Determine the duration the user was logged in and log that to UMA.
219 if (!signin_time.is_null()) {
220 base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
221 UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
222 signed_in_duration.InMinutes());
225 // Revoke all tokens before sending signed_out notification, because there
226 // may be components that don't listen for token service events when the
227 // profile is not connected to an account.
228 LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
229 << "IsSigninAllowed: " << IsSigninAllowed();
230 token_service_->RevokeAllCredentials();
232 FOR_EACH_OBSERVER(Observer,
234 GoogleSignedOut(account_id, username));
237 void SigninManager::Initialize(PrefService* local_state) {
238 SigninManagerBase::Initialize(local_state);
240 // local_state can be null during unit tests.
242 local_state_pref_registrar_.Init(local_state);
243 local_state_pref_registrar_.Add(
244 prefs::kGoogleServicesUsernamePattern,
245 base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
246 weak_pointer_factory_.GetWeakPtr()));
248 signin_allowed_.Init(prefs::kSigninAllowed,
250 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
251 base::Unretained(this)));
254 client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
255 if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
256 // User is signed in, but the username is invalid - the administrator must
257 // have changed the policy since the last signin, so sign out the user.
258 SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
262 account_id_helper_.reset(
263 new SigninAccountIdHelper(client_, token_service_, this));
266 void SigninManager::Shutdown() {
267 if (merge_session_helper_)
268 merge_session_helper_->CancelAll();
270 local_state_pref_registrar_.RemoveAll();
271 account_id_helper_.reset();
272 SigninManagerBase::Shutdown();
275 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
276 if (IsAuthenticated() &&
277 !IsAllowedUsername(GetAuthenticatedUsername())) {
278 // Signed in user is invalid according to the current policy so sign
280 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
284 bool SigninManager::IsSigninAllowed() const {
285 return signin_allowed_.GetValue();
288 void SigninManager::OnSigninAllowedPrefChanged() {
289 if (!IsSigninAllowed())
290 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
294 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
295 const std::string& policy) {
299 // Patterns like "*@foo.com" are not accepted by our regex engine (since they
300 // are not valid regular expressions - they should instead be ".*@foo.com").
301 // For convenience, detect these patterns and insert a "." character at the
303 base::string16 pattern = base::UTF8ToUTF16(policy);
304 if (pattern[0] == L'*')
305 pattern.insert(pattern.begin(), L'.');
307 // See if the username matches the policy-provided pattern.
308 UErrorCode status = U_ZERO_ERROR;
309 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
310 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
311 if (!U_SUCCESS(status)) {
312 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
313 // If an invalid pattern is provided, then prohibit *all* logins (better to
314 // break signin than to quietly allow users to sign in).
317 base::string16 username16 = base::UTF8ToUTF16(username);
318 icu::UnicodeString icu_input(username16.data(), username16.length());
319 matcher.reset(icu_input);
320 status = U_ZERO_ERROR;
321 UBool match = matcher.matches(status);
322 DCHECK(U_SUCCESS(status));
323 return !!match; // !! == convert from UBool to bool.
326 bool SigninManager::IsAllowedUsername(const std::string& username) const {
327 const PrefService* local_state = local_state_pref_registrar_.prefs();
329 return true; // In a unit test with no local state - all names are allowed.
331 std::string pattern =
332 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
333 return IsUsernameAllowedByPolicy(username, pattern);
336 bool SigninManager::AuthInProgress() const {
337 return !possibly_invalid_username_.empty();
340 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
341 return possibly_invalid_username_;
344 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
345 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
348 void SigninManager::CompletePendingSignin() {
349 DCHECK(!possibly_invalid_username_.empty());
350 OnSignedIn(possibly_invalid_username_);
352 if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) {
353 merge_session_helper_.reset(new MergeSessionHelper(
354 token_service_, GaiaConstants::kChromeSource,
355 client_->GetURLRequestContext(), NULL));
358 DCHECK(!temp_refresh_token_.empty());
359 DCHECK(IsAuthenticated());
360 std::string account_id = GetAuthenticatedAccountId();
361 token_service_->UpdateCredentials(account_id, temp_refresh_token_);
362 temp_refresh_token_.clear();
364 if (client_->ShouldMergeSigninCredentialsIntoCookieJar())
365 merge_session_helper_->LogIn(account_id);
368 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
369 OnSignedIn(username);
372 void SigninManager::OnSignedIn(const std::string& username) {
373 client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
374 base::Time::Now().ToInternalValue());
375 SetAuthenticatedUsername(username);
376 possibly_invalid_username_.clear();
381 GoogleSigninSucceeded(GetAuthenticatedAccountId(),
382 GetAuthenticatedUsername(),
385 client_->GoogleSigninSucceeded(GetAuthenticatedAccountId(),
386 GetAuthenticatedUsername(),
389 signin_metrics::LogSigninProfile(client_->IsFirstRun(),
390 client_->GetInstallDate());
392 password_.clear(); // Don't need it anymore.
393 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
396 void SigninManager::ProhibitSignout(bool prohibit_signout) {
397 prohibit_signout_ = prohibit_signout;
400 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }