blob: 97497aa2e0f816ecea1dca9d5bb43c469f667887 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/signin/about_signin_internals_factory.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/local_auth.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/user_manager.h"
#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/about_signin_internals.h"
#include "components/signin/core/browser/signin_header_helper.h"
#include "components/signin/core/browser/signin_investigator.h"
#include "components/signin/core/browser/signin_metrics.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_ui.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/url_util.h"
#include "services/identity/public/cpp/accounts_mutator.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/identity/public/cpp/primary_account_mutator.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(OS_WIN)
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#endif // defined(OS_WIN)
namespace {
// Specific implementation of DiceTurnSyncOnHelper::Delegate for forced signin
// flows. Some confirmation prompts are skipped.
class ForcedSigninDiceTurnSyncOnHelperDelegate
: public DiceTurnSyncOnHelperDelegateImpl {
public:
explicit ForcedSigninDiceTurnSyncOnHelperDelegate(Browser* browser)
: DiceTurnSyncOnHelperDelegateImpl(browser) {}
private:
void ShowMergeSyncDataConfirmation(
const std::string& previous_email,
const std::string& new_email,
DiceTurnSyncOnHelper::SigninChoiceCallback callback) override {
NOTREACHED();
}
void ShowEnterpriseAccountConfirmation(
const std::string& email,
DiceTurnSyncOnHelper::SigninChoiceCallback callback) override {
std::move(callback).Run(
DiceTurnSyncOnHelper ::SigninChoice::SIGNIN_CHOICE_CONTINUE);
}
};
#if defined(OS_WIN)
// Returns a list of valid signin domains that were passed in
// |email_domains_parameter| as an argument to the gcpw signin dialog.
std::vector<std::string> GetEmailDomainsFromParameter(
const std::string& email_domains_parameter) {
return base::SplitString(base::ToLowerASCII(email_domains_parameter),
credential_provider::kEmailDomainsSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
}
// Validates that the |signin_gaia_id| that the user signed in with matches
// the |gaia_id_parameter| passed to the gcpw signin dialog. Also ensures
// that the |signin_email| is in a domain listed in |email_domains_parameter|.
// Returns kUiecSuccess on success.
// Returns the appropriate error code on failure.
credential_provider::UiExitCodes ValidateSigninEmail(
const std::string& gaia_id_parameter,
const std::string& email_domains_parameter,
const std::string& signin_email,
const std::string& signin_gaia_id) {
if (!gaia_id_parameter.empty() &&
!base::LowerCaseEqualsASCII(gaia_id_parameter, signin_gaia_id)) {
return credential_provider::kUiecEMailMissmatch;
}
if (email_domains_parameter.empty())
return credential_provider::kUiecSuccess;
std::vector<std::string> all_email_domains =
GetEmailDomainsFromParameter(email_domains_parameter);
std::string email_domain = gaia::ExtractDomainName(signin_email);
return std::find(all_email_domains.begin(), all_email_domains.end(),
email_domain) != all_email_domains.end()
? credential_provider::kUiecSuccess
: credential_provider::kUiecInvalidEmailDomain;
}
#endif
void LogHistogramValue(signin_metrics::AccessPointAction action) {
UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action,
signin_metrics::HISTOGRAM_MAX);
}
// Returns true if |profile| is a system profile or created from one.
bool IsSystemProfile(Profile* profile) {
return profile->GetOriginalProfile()->IsSystemProfile();
}
void RedirectToNtpOrAppsPage(content::WebContents* contents,
signin_metrics::AccessPoint access_point) {
// Do nothing if a navigation is pending, since this call can be triggered
// from DidStartLoading. This avoids deleting the pending entry while we are
// still navigating to it. See crbug/346632.
if (contents->GetController().GetPendingEntry())
return;
VLOG(1) << "RedirectToNtpOrAppsPage";
// Redirect to NTP/Apps page and display a confirmation bubble
GURL url(access_point ==
signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK
? chrome::kChromeUIAppsURL
: chrome::kChromeUINewTabURL);
content::OpenURLParams params(url, content::Referrer(),
WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
contents->OpenURL(params);
}
void RedirectToNtpOrAppsPageIfNecessary(
content::WebContents* contents,
signin_metrics::AccessPoint access_point) {
if (access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS)
RedirectToNtpOrAppsPage(contents, access_point);
}
void CloseModalSigninIfNeeded(InlineLoginHandlerImpl* handler) {
if (handler) {
Browser* browser = handler->GetDesktopBrowser();
if (browser)
browser->signin_view_controller()->CloseModalSignin();
}
}
void SetProfileLocked(const base::FilePath profile_path, bool locked) {
if (!profile_path.empty()) {
ProfileManager* profile_manager = g_browser_process->profile_manager();
if (profile_manager) {
ProfileAttributesEntry* entry;
if (profile_manager->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_path, &entry)) {
if (locked)
entry->LockForceSigninProfile(true);
else
entry->SetIsSigninRequired(false);
}
}
}
}
void UnlockProfileAndHideLoginUI(const base::FilePath profile_path,
InlineLoginHandlerImpl* handler) {
SetProfileLocked(profile_path, false);
if (handler)
handler->CloseDialogFromJavascript();
UserManager::Hide();
}
void LockProfileAndShowUserManager(const base::FilePath& profile_path) {
SetProfileLocked(profile_path, true);
UserManager::Show(profile_path,
profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
}
// Returns true if the showAccountManagement parameter in the given url is set
// to true.
bool ShouldShowAccountManagement(const GURL& url, bool is_mirror_enabled) {
if (!is_mirror_enabled)
return false;
std::string value;
if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyShowAccountManagement,
&value)) {
int enabled = 0;
if (base::StringToInt(value, &enabled) && enabled == 1)
return true;
}
return false;
}
// Callback for DiceTurnOnSyncHelper.
void OnSyncSetupComplete(Profile* profile,
base::WeakPtr<InlineLoginHandlerImpl> handler,
const std::string& username,
const std::string& password) {
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
bool has_primary_account = identity_manager->HasPrimaryAccount();
if (has_primary_account && !password.empty()) {
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(profile,
ServiceAccessType::EXPLICIT_ACCESS);
password_store->SaveGaiaPasswordHash(
username, base::UTF8ToUTF16(password),
password_manager::metrics_util::SyncPasswordHashChange::
SAVED_ON_CHROME_SIGNIN);
if (profiles::IsLockAvailable(profile))
LocalAuth::SetLocalAuthCredentials(profile, password);
}
if (handler) {
handler->SyncStarterCallback(has_primary_account);
} else if (signin_util::IsForceSigninEnabled() && !has_primary_account) {
BrowserList::CloseAllBrowsersWithProfile(
profile, base::Bind(&LockProfileAndShowUserManager),
// Cannot be called because skip_beforeunload is true.
BrowserList::CloseCallback(),
/*skip_beforeunload=*/true);
}
}
} // namespace
InlineSigninHelper::InlineSigninHelper(
base::WeakPtr<InlineLoginHandlerImpl> handler,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
Profile* profile,
Profile::CreateStatus create_status,
const GURL& current_url,
const std::string& email,
const std::string& gaia_id,
const std::string& password,
const std::string& auth_code,
const std::string& signin_scoped_device_id,
bool confirm_untrusted_signin,
bool is_force_sign_in_with_usermanager)
: gaia_auth_fetcher_(this, gaia::GaiaSource::kChrome, url_loader_factory),
handler_(handler),
profile_(profile),
create_status_(create_status),
current_url_(current_url),
email_(email),
gaia_id_(gaia_id),
password_(password),
auth_code_(auth_code),
confirm_untrusted_signin_(confirm_untrusted_signin),
is_force_sign_in_with_usermanager_(is_force_sign_in_with_usermanager) {
DCHECK(profile_);
DCHECK(!email_.empty());
DCHECK(!auth_code_.empty());
gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
auth_code_, signin_scoped_device_id);
}
InlineSigninHelper::~InlineSigninHelper() {}
void InlineSigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) {
if (is_force_sign_in_with_usermanager_) {
// If user sign in in UserManager with force sign in enabled, the browser
// window won't be opened until now.
UnlockProfileAndHideLoginUI(profile_->GetPath(), handler_.get());
profiles::OpenBrowserWindowForProfile(
base::Bind(&InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened,
base::Unretained(this), result),
true, false, true, profile_, create_status_);
} else {
OnClientOAuthSuccessAndBrowserOpened(result, profile_, create_status_);
}
}
void InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened(
const ClientOAuthResult& result,
Profile* profile,
Profile::CreateStatus status) {
Browser* browser = nullptr;
if (handler_)
browser = handler_->GetDesktopBrowser();
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(current_url_);
if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
// Constants are only available on Windows for the Google Credential
// Provider for Windows. Other platforms will just close the dialog here.
#if defined(OS_WIN)
std::string json_retval;
base::Value args(base::Value::Type::DICTIONARY);
args.SetKey(credential_provider::kKeyEmail, base::Value(email_));
args.SetKey(credential_provider::kKeyPassword, base::Value(password_));
args.SetKey(credential_provider::kKeyId, base::Value(gaia_id_));
args.SetKey(credential_provider::kKeyRefreshToken,
base::Value(result.refresh_token));
args.SetKey(credential_provider::kKeyAccessToken,
base::Value(result.access_token));
handler_->SendLSTFetchResultsMessage(args);
#else
if (handler_)
handler_->CloseDialogFromJavascript();
#endif // defined(OS_WIN)
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
return;
}
AboutSigninInternals* about_signin_internals =
AboutSigninInternalsFactory::GetForProfile(profile_);
about_signin_internals->OnRefreshTokenReceived("Successful");
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
// Seed the account with this combination of gaia id/display email.
AccountInfo account_info;
account_info.gaia = gaia_id_;
account_info.email = email_;
identity_manager->LegacySeedAccountInfo(account_info);
std::string primary_email = identity_manager->GetPrimaryAccountInfo().email;
if (gaia::AreEmailsSame(email_, primary_email) &&
(reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
reason == signin_metrics::Reason::REASON_UNLOCK) &&
!password_.empty() && profiles::IsLockAvailable(profile_)) {
LocalAuth::SetLocalAuthCredentials(profile_, password_);
}
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
if (!password_.empty()) {
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(profile_,
ServiceAccessType::EXPLICIT_ACCESS);
if (password_store && !primary_email.empty()) {
password_store->SaveGaiaPasswordHash(
primary_email, base::UTF8ToUTF16(password_),
password_manager::metrics_util::SyncPasswordHashChange::
SAVED_ON_CHROME_SIGNIN);
}
}
#endif
if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
reason == signin_metrics::Reason::REASON_UNLOCK ||
reason == signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) {
identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
gaia_id_, email_, result.refresh_token,
result.is_under_advanced_protection,
signin_metrics::SourceForRefreshTokenOperation::
kInlineLoginHandler_Signin);
if (signin::IsAutoCloseEnabledInEmbeddedURL(current_url_)) {
// Close the gaia sign in tab via a task to make sure we aren't in the
// middle of any webui handler code.
bool show_account_management = ShouldShowAccountManagement(
current_url_,
AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&InlineLoginHandlerImpl::CloseTab, handler_,
show_account_management));
}
if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
reason == signin_metrics::Reason::REASON_UNLOCK) {
identity_manager->GetPrimaryAccountMutator()
->LegacyMergeSigninCredentialIntoCookieJar();
}
LogSigninReason(reason);
} else {
if (confirm_untrusted_signin_) {
// Display a confirmation dialog to the user.
base::RecordAction(
base::UserMetricsAction("Signin_Show_UntrustedSigninPrompt"));
if (!browser)
browser = chrome::FindLastActiveWithProfile(profile_);
browser->window()->ShowOneClickSigninConfirmation(
base::UTF8ToUTF16(email_),
base::BindOnce(&InlineSigninHelper::UntrustedSigninConfirmed,
base::Unretained(this), result.refresh_token));
return;
}
CreateSyncStarter(browser, current_url_, result.refresh_token);
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
}
void InlineSigninHelper::UntrustedSigninConfirmed(
const std::string& refresh_token,
bool confirmed) {
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
if (confirmed) {
CreateSyncStarter(nullptr, current_url_, refresh_token);
return;
}
base::RecordAction(base::UserMetricsAction("Signin_Undo_Signin"));
if (handler_) {
handler_->SyncStarterCallback(false);
} else if (signin_util::IsForceSigninEnabled()) {
BrowserList::CloseAllBrowsersWithProfile(
profile_, base::Bind(&LockProfileAndShowUserManager),
// Cannot be called because skip_beforeunload is true.
BrowserList::CloseCallback(),
/*skip_beforeunload=*/true);
}
}
void InlineSigninHelper::CreateSyncStarter(Browser* browser,
const GURL& current_url,
const std::string& refresh_token) {
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
if (identity_manager->HasPrimaryAccount()) {
// Already signed in, nothing to do.
if (handler_)
handler_->SyncStarterCallback(true);
return;
}
if (!browser)
browser = chrome::OpenEmptyWindow(profile_);
std::string account_id =
identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
gaia_id_, email_, refresh_token,
/*is_under_advanced_protection=*/false,
signin_metrics::SourceForRefreshTokenOperation::
kInlineLoginHandler_Signin);
std::unique_ptr<DiceTurnSyncOnHelper::Delegate> delegate =
signin_util::IsForceSigninEnabled()
? std::make_unique<ForcedSigninDiceTurnSyncOnHelperDelegate>(browser)
: std::make_unique<DiceTurnSyncOnHelperDelegateImpl>(browser);
new DiceTurnSyncOnHelper(
profile_, signin::GetAccessPointForEmbeddedPromoURL(current_url),
signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO,
signin::GetSigninReasonForEmbeddedPromoURL(current_url), account_id,
DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT,
std::move(delegate),
base::BindOnce(&OnSyncSetupComplete, profile_, handler_, email_,
password_));
}
void InlineSigninHelper::OnClientOAuthFailure(
const GoogleServiceAuthError& error) {
if (handler_)
handler_->HandleLoginError(error.ToString(), base::string16());
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(current_url_);
if (reason != signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
AboutSigninInternals* about_signin_internals =
AboutSigninInternalsFactory::GetForProfile(profile_);
about_signin_internals->OnRefreshTokenReceived("Failure");
}
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
InlineLoginHandlerImpl::InlineLoginHandlerImpl()
: confirm_untrusted_signin_(false), weak_factory_(this) {}
InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
// This method is not called with webview sign in enabled.
void InlineLoginHandlerImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!web_contents() || !navigation_handle->HasCommitted() ||
navigation_handle->IsErrorPage()) {
return;
}
// Returns early if this is not a gaia webview navigation.
content::RenderFrameHost* gaia_frame =
signin::GetAuthFrame(web_contents(), "signin-frame");
if (navigation_handle->GetRenderFrameHost() != gaia_frame)
return;
// Loading any untrusted (e.g., HTTP) URLs in the privileged sign-in process
// will require confirmation before the sign in takes effect.
const GURL kGaiaExtOrigin(
GaiaUrls::GetInstance()->signin_completed_continue_url().GetOrigin());
if (!navigation_handle->GetURL().is_empty()) {
GURL origin(navigation_handle->GetURL().GetOrigin());
if (navigation_handle->GetURL().spec() != url::kAboutBlankURL &&
origin != kGaiaExtOrigin && !gaia::IsGaiaSignonRealm(origin)) {
confirm_untrusted_signin_ = true;
}
}
}
// static
void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) {
params.SetString("service", "chromiumsync");
// If this was called from the user manager to reauthenticate the profile,
// make sure the webui is aware.
Profile* profile = Profile::FromWebUI(web_ui());
if (IsSystemProfile(profile))
params.SetBoolean("dontResizeNonEmbeddedPages", true);
content::WebContents* contents = web_ui()->GetWebContents();
const GURL& current_url = contents->GetURL();
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(current_url);
const GURL& url = GaiaUrls::GetInstance()->embedded_signin_url();
params.SetBoolean("isNewGaiaFlow", true);
params.SetString("clientId",
GaiaUrls::GetInstance()->oauth2_chrome_client_id());
params.SetString("gaiaPath", url.path().substr(1));
#if defined(OS_WIN)
if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
std::string email_domains;
if (net::GetValueForKeyInQuery(
current_url, credential_provider::kEmailDomainsSigninPromoParameter,
&email_domains)) {
std::vector<std::string> all_email_domains =
GetEmailDomainsFromParameter(email_domains);
if (all_email_domains.size() == 1)
params.SetString("emailDomain", all_email_domains[0]);
}
// Prevent opening a new window if the embedded page fails to load.
// This will keep the user from being able to access a fully functional
// Chrome window in incognito mode.
params.SetBoolean("dontResizeNonEmbeddedPages", true);
GURL windows_url = GaiaUrls::GetInstance()->embedded_setup_windows_url();
// Redirect to specified gaia endpoint path for GCPW:
std::string windows_endpoint_path = windows_url.path().substr(1);
// Redirect to specified gaia endpoint path for GCPW:
std::string gcpw_endpoint_path;
if (net::GetValueForKeyInQuery(
current_url, credential_provider::kGcpwEndpointPathPromoParameter,
&gcpw_endpoint_path)) {
windows_endpoint_path = gcpw_endpoint_path;
}
params.SetString("gaiaPath", windows_endpoint_path);
}
#endif
std::string flow;
switch (reason) {
case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT:
flow = "addaccount";
break;
case signin_metrics::Reason::REASON_REAUTHENTICATION:
case signin_metrics::Reason::REASON_UNLOCK:
flow = "reauth";
break;
case signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT:
flow = "enterprisefsi";
break;
default:
flow = "signin";
break;
}
params.SetString("flow", flow);
content::WebContentsObserver::Observe(contents);
LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN);
}
void InlineLoginHandlerImpl::CompleteLogin(const std::string& email,
const std::string& password,
const std::string& gaia_id,
const std::string& auth_code,
bool skip_for_now,
bool trusted,
bool trusted_found,
bool choose_what_to_sync) {
content::WebContents* contents = web_ui()->GetWebContents();
const GURL& current_url = contents->GetURL();
if (skip_for_now) {
signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
SyncStarterCallback(false);
return;
}
// This value exists only for webview sign in.
if (trusted_found)
confirm_untrusted_signin_ = !trusted;
DCHECK(!email.empty());
DCHECK(!gaia_id.empty());
DCHECK(!auth_code.empty());
content::StoragePartition* partition =
content::BrowserContext::GetStoragePartitionForSite(
contents->GetBrowserContext(), signin::GetSigninPartitionURL());
// If this was called from the user manager to reauthenticate the profile,
// the current profile is the system profile. In this case, use the email to
// find the right profile to reauthenticate. Otherwise the profile can be
// taken from web_ui().
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(current_url);
Profile* profile = Profile::FromWebUI(web_ui());
if (reason != signin_metrics::Reason::REASON_FETCH_LST_ONLY &&
IsSystemProfile(profile)) {
ProfileManager* manager = g_browser_process->profile_manager();
base::FilePath path = profiles::GetPathOfProfileWithEmail(manager, email);
if (path.empty()) {
path = UserManager::GetSigninProfilePath();
}
if (!path.empty()) {
// If we are only reauthenticating a profile in the user manager (and not
// unlocking it), load the profile and finish the login.
if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION) {
FinishCompleteLoginParams params(
this, partition, current_url, base::FilePath(),
confirm_untrusted_signin_, email, gaia_id, password, auth_code,
choose_what_to_sync, false);
ProfileManager::CreateCallback callback =
base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params);
profiles::LoadProfileAsync(path, callback);
} else {
// Otherwise, switch to the profile and finish the login. Pass the
// profile path so it can be marked as unlocked. Don't pass a handler
// pointer since it will be destroyed before the callback runs.
bool is_force_signin_enabled = signin_util::IsForceSigninEnabled();
InlineLoginHandlerImpl* handler = nullptr;
if (is_force_signin_enabled)
handler = this;
FinishCompleteLoginParams params(
handler, partition, current_url, path, confirm_untrusted_signin_,
email, gaia_id, password, auth_code, choose_what_to_sync,
is_force_signin_enabled);
ProfileManager::CreateCallback callback =
base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params);
if (is_force_signin_enabled) {
// Browser window will be opened after ClientOAuthSuccess.
profiles::LoadProfileAsync(path, callback);
} else {
profiles::SwitchToProfile(path, true, callback,
ProfileMetrics::SWITCH_PROFILE_UNLOCK);
}
}
}
} else {
FinishCompleteLogin(FinishCompleteLoginParams(
this, partition, current_url, base::FilePath(),
confirm_untrusted_signin_, email, gaia_id, password,
auth_code, choose_what_to_sync, false),
profile, Profile::CREATE_STATUS_CREATED);
}
}
InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams(
InlineLoginHandlerImpl* handler,
content::StoragePartition* partition,
const GURL& url,
const base::FilePath& profile_path,
bool confirm_untrusted_signin,
const std::string& email,
const std::string& gaia_id,
const std::string& password,
const std::string& auth_code,
bool choose_what_to_sync,
bool is_force_sign_in_with_usermanager)
: handler(handler),
partition(partition),
url(url),
profile_path(profile_path),
confirm_untrusted_signin(confirm_untrusted_signin),
email(email),
gaia_id(gaia_id),
password(password),
auth_code(auth_code),
choose_what_to_sync(choose_what_to_sync),
is_force_sign_in_with_usermanager(is_force_sign_in_with_usermanager) {}
InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams(
const FinishCompleteLoginParams& other) = default;
InlineLoginHandlerImpl::FinishCompleteLoginParams::
~FinishCompleteLoginParams() {}
// static
void InlineLoginHandlerImpl::FinishCompleteLogin(
const FinishCompleteLoginParams& params,
Profile* profile,
Profile::CreateStatus status) {
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(params.url);
std::string default_email;
net::GetValueForKeyInQuery(params.url, "email", &default_email);
std::string validate_email;
net::GetValueForKeyInQuery(params.url, "validateEmail", &validate_email);
#if defined(OS_WIN)
if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
std::string validate_gaia_id;
net::GetValueForKeyInQuery(
params.url, credential_provider::kValidateGaiaIdSigninPromoParameter,
&validate_gaia_id);
std::string email_domains;
net::GetValueForKeyInQuery(
params.url, credential_provider::kEmailDomainsSigninPromoParameter,
&email_domains);
credential_provider::UiExitCodes exit_code = ValidateSigninEmail(
validate_gaia_id, email_domains, params.email, params.gaia_id);
if (exit_code != credential_provider::kUiecSuccess) {
if (params.handler) {
params.handler->HandleLoginError(base::NumberToString((int)exit_code),
base::UTF8ToUTF16(params.email));
}
return;
} else {
// Validation has already been done for GCPW, so clear the validate
// argument so it doesn't validate again. GCPW validation allows the
// signin email to not match the email given in the request url if the
// gaia id of the signin email matches the one given in the request url.
validate_email.clear();
}
}
#endif
// When doing a SAML sign in, this email check may result in a false
// positive. This happens when the user types one email address in the
// gaia sign in page, but signs in to a different account in the SAML sign in
// page.
if (validate_email == "1" && !default_email.empty()) {
if (!gaia::AreEmailsSame(params.email, default_email)) {
if (params.handler) {
params.handler->HandleLoginError(
l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
base::UTF8ToUTF16(default_email)),
base::UTF8ToUTF16(params.email));
}
return;
}
}
signin_metrics::AccessPoint access_point =
signin::GetAccessPointForEmbeddedPromoURL(params.url);
LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED);
bool switch_to_advanced =
params.choose_what_to_sync &&
(access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
LogHistogramValue(switch_to_advanced
? signin_metrics::HISTOGRAM_WITH_ADVANCED
: signin_metrics::HISTOGRAM_WITH_DEFAULTS);
CanOfferSigninType can_offer_for = CAN_OFFER_SIGNIN_FOR_ALL_ACCOUNTS;
switch (reason) {
case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT:
can_offer_for = CAN_OFFER_SIGNIN_FOR_SECONDARY_ACCOUNT;
break;
case signin_metrics::Reason::REASON_REAUTHENTICATION:
case signin_metrics::Reason::REASON_UNLOCK: {
std::string primary_username =
IdentityManagerFactory::GetForProfile(profile)
->GetPrimaryAccountInfo()
.email;
if (!gaia::AreEmailsSame(default_email, primary_username))
can_offer_for = CAN_OFFER_SIGNIN_FOR_SECONDARY_ACCOUNT;
break;
}
default:
// No need to change |can_offer_for|.
break;
}
std::string error_msg;
bool can_offer = reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY ||
CanOfferSignin(profile, can_offer_for, params.gaia_id,
params.email, &error_msg);
if (!can_offer) {
if (params.handler) {
params.handler->HandleLoginError(error_msg,
base::UTF8ToUTF16(params.email));
}
return;
}
AboutSigninInternals* about_signin_internals =
AboutSigninInternalsFactory::GetForProfile(profile);
if (about_signin_internals)
about_signin_internals->OnAuthenticationResultReceived("Successful");
std::string signin_scoped_device_id =
GetSigninScopedDeviceIdForProfile(profile);
base::WeakPtr<InlineLoginHandlerImpl> handler_weak_ptr;
if (params.handler)
handler_weak_ptr = params.handler->GetWeakPtr();
// InlineSigninHelper will delete itself.
new InlineSigninHelper(
handler_weak_ptr,
params.partition->GetURLLoaderFactoryForBrowserProcess(), profile, status,
params.url, params.email, params.gaia_id, params.password,
params.auth_code, signin_scoped_device_id,
params.confirm_untrusted_signin,
params.is_force_sign_in_with_usermanager);
// If opened from user manager to unlock a profile, make sure the user manager
// is closed and that the profile is marked as unlocked.
if (reason != signin_metrics::Reason::REASON_FETCH_LST_ONLY &&
!params.is_force_sign_in_with_usermanager) {
UnlockProfileAndHideLoginUI(params.profile_path, params.handler);
}
}
void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg,
const base::string16& email) {
content::WebContents* contents = web_ui()->GetWebContents();
const GURL& current_url = contents->GetURL();
signin_metrics::Reason reason =
signin::GetSigninReasonForEmbeddedPromoURL(current_url);
if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
base::Value error_value(base::Value::Type::DICTIONARY);
#if defined(OS_WIN)
// If the message is an integer error code, send it as part
// of the result.
int exit_code = 0;
if (base::StringToInt(error_msg, &exit_code)) {
error_value.SetKey(credential_provider::kKeyExitCode,
base::Value(exit_code));
}
#endif
SendLSTFetchResultsMessage(error_value);
return;
}
SyncStarterCallback(false);
Browser* browser = GetDesktopBrowser();
Profile* profile = Profile::FromWebUI(web_ui());
if (IsSystemProfile(profile))
profile = g_browser_process->profile_manager()->GetProfileByPath(
UserManager::GetSigninProfilePath());
CloseModalSigninIfNeeded(this);
if (!error_msg.empty()) {
LoginUIServiceFactory::GetForProfile(profile)->DisplayLoginResult(
browser, base::UTF8ToUTF16(error_msg), email);
}
}
void InlineLoginHandlerImpl::SendLSTFetchResultsMessage(
const base::Value& arg) {
if (IsJavascriptAllowed())
CallJavascriptFunction("inline.login.sendLSTFetchResults", arg);
}
Browser* InlineLoginHandlerImpl::GetDesktopBrowser() {
Browser* browser =
chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
if (!browser)
browser = chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui()));
return browser;
}
void InlineLoginHandlerImpl::SyncStarterCallback(bool sync_setup_success) {
content::WebContents* contents = web_ui()->GetWebContents();
if (contents->GetController().GetPendingEntry()) {
// Do nothing if a navigation is pending, since this call can be triggered
// from DidStartLoading. This avoids deleting the pending entry while we are
// still navigating to it. See crbug/346632.
return;
}
const GURL& current_url = contents->GetLastCommittedURL();
signin_metrics::AccessPoint access_point =
signin::GetAccessPointForEmbeddedPromoURL(current_url);
bool auto_close = signin::IsAutoCloseEnabledInEmbeddedURL(current_url);
if (!sync_setup_success) {
RedirectToNtpOrAppsPage(contents, access_point);
} else if (auto_close) {
bool show_account_management = ShouldShowAccountManagement(
current_url, AccountConsistencyModeManager::IsMirrorEnabledForProfile(
Profile::FromWebUI(web_ui())));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&InlineLoginHandlerImpl::CloseTab,
weak_factory_.GetWeakPtr(), show_account_management));
} else {
RedirectToNtpOrAppsPageIfNecessary(contents, access_point);
}
}
void InlineLoginHandlerImpl::CloseTab(bool show_account_management) {
content::WebContents* tab = web_ui()->GetWebContents();
Browser* browser = chrome::FindBrowserWithWebContents(tab);
if (browser) {
TabStripModel* tab_strip_model = browser->tab_strip_model();
if (tab_strip_model) {
int index = tab_strip_model->GetIndexOfWebContents(tab);
if (index != TabStripModel::kNoTab) {
tab_strip_model->ExecuteContextMenuCommand(
index, TabStripModel::CommandCloseTab);
}
}
if (show_account_management) {
browser->window()->ShowAvatarBubbleFromAvatarButton(
BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT,
signin::ManageAccountsParams(),
signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
false);
}
}
}