Commit c3f25da2 authored by Mohammed Abdon's avatar Mohammed Abdon Committed by Commit Bot

Introduce OnlineLoginHelper class

This CL introduces OnlineLoginHelper which will be used in authenticating Gaia and SAML users in loginscreen and SAML users in lockscreen.

Bug: 1102942
Change-Id: Ie0e41b30e4e6cfb8f027d729be24bcd5b8169151
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2466422Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Commit-Queue: Mohammed Abdon <mohammedabdon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825282}
parent 2a06d6e9
...@@ -2302,6 +2302,8 @@ static_library("ui") { ...@@ -2302,6 +2302,8 @@ static_library("ui") {
"webui/chromeos/login/network_screen_handler.h", "webui/chromeos/login/network_screen_handler.h",
"webui/chromeos/login/network_state_informer.cc", "webui/chromeos/login/network_state_informer.cc",
"webui/chromeos/login/network_state_informer.h", "webui/chromeos/login/network_state_informer.h",
"webui/chromeos/login/online_login_helper.cc",
"webui/chromeos/login/online_login_helper.h",
"webui/chromeos/login/oobe_display_chooser.cc", "webui/chromeos/login/oobe_display_chooser.cc",
"webui/chromeos/login/oobe_display_chooser.h", "webui/chromeos/login/oobe_display_chooser.h",
"webui/chromeos/login/oobe_ui.cc", "webui/chromeos/login/oobe_ui.cc",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h" #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h" #include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h" #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
#include "chrome/browser/ui/webui/chromeos/login/online_login_helper.h"
#include "chrome/browser/ui/webui/chromeos/login/saml_challenge_key_handler.h" #include "chrome/browser/ui/webui/chromeos/login/saml_challenge_key_handler.h"
#include "chromeos/components/security_token_pin/constants.h" #include "chromeos/components/security_token_pin/constants.h"
#include "components/user_manager/user_type.h" #include "components/user_manager/user_type.h"
...@@ -35,10 +36,8 @@ class NSSTempCertsCacheChromeOS; ...@@ -35,10 +36,8 @@ class NSSTempCertsCacheChromeOS;
namespace chromeos { namespace chromeos {
class CookieWaiter;
class SamlPasswordAttributes; class SamlPasswordAttributes;
class SigninScreenHandler; class SigninScreenHandler;
class UserContext;
class PublicSamlUrlFetcher; class PublicSamlUrlFetcher;
class GaiaScreen; class GaiaScreen;
...@@ -160,24 +159,22 @@ class GaiaScreenHandler : public BaseScreenHandler, ...@@ -160,24 +159,22 @@ class GaiaScreenHandler : public BaseScreenHandler,
// TODO (xiaoyinh): remove this dependency. // TODO (xiaoyinh): remove this dependency.
friend class SigninScreenHandler; friend class SigninScreenHandler;
struct GaiaContext; void LoadGaia(const login::GaiaContext& context);
void LoadGaia(const GaiaContext& context);
// Callback that loads GAIA after version and stat consent information has // Callback that loads GAIA after version and stat consent information has
// been retrieved. // been retrieved.
void LoadGaiaWithPartition(const GaiaContext& context, void LoadGaiaWithPartition(const login::GaiaContext& context,
const std::string& partition_name); const std::string& partition_name);
// Called after the GAPS cookie, if present, is added to the cookie store. // Called after the GAPS cookie, if present, is added to the cookie store.
void OnSetCookieForLoadGaiaWithPartition(const GaiaContext& context, void OnSetCookieForLoadGaiaWithPartition(const login::GaiaContext& context,
const std::string& partition_name, const std::string& partition_name,
net::CookieAccessResult result); net::CookieAccessResult result);
// Callback that loads GAIA after version and stat consent information has // Callback that loads GAIA after version and stat consent information has
// been retrieved. // been retrieved.
void LoadGaiaWithPartitionAndVersionAndConsent( void LoadGaiaWithPartitionAndVersionAndConsent(
const GaiaContext& context, const login::GaiaContext& context,
const std::string& partition_name, const std::string& partition_name,
const std::string* platform_version, const std::string* platform_version,
const bool* collect_stats_consent); const bool* collect_stats_consent);
...@@ -311,22 +308,6 @@ class GaiaScreenHandler : public BaseScreenHandler, ...@@ -311,22 +308,6 @@ class GaiaScreenHandler : public BaseScreenHandler,
// Records whether WebUI is currently in offline mode. // Records whether WebUI is currently in offline mode.
void SetOfflineLoginIsActive(bool is_active); void SetOfflineLoginIsActive(bool is_active);
// Builds the UserContext with the information from the given Gaia user
// sign-in. On failure, returns false and sets `error_message`.
bool BuildUserContextForGaiaSignIn(
user_manager::UserType user_type,
const AccountId& account_id,
bool using_saml,
const std::string& password,
const SamlPasswordAttributes& password_attributes,
UserContext* user_context,
std::string* error_message);
void ContinueAuthenticationWhenCookiesAvailable();
void OnGetCookiesForCompleteAuthentication(
const net::CookieAccessResultList& cookies,
const net::CookieAccessResultList& excluded_cookies);
void OnCookieWaitTimeout(); void OnCookieWaitTimeout();
bool is_security_token_pin_dialog_running() const { bool is_security_token_pin_dialog_running() const {
...@@ -447,8 +428,8 @@ class GaiaScreenHandler : public BaseScreenHandler, ...@@ -447,8 +428,8 @@ class GaiaScreenHandler : public BaseScreenHandler,
std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_; std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_;
std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_for_test_; std::unique_ptr<SamlChallengeKeyHandler> saml_challenge_key_handler_for_test_;
// Connection to the CookieManager that signals when the GAIA cookies change. std::unique_ptr<OnlineLoginHelper> online_login_helper_;
std::unique_ptr<CookieWaiter> oauth_code_waiter_;
std::unique_ptr<UserContext> pending_user_context_; std::unique_ptr<UserContext> pending_user_context_;
base::WeakPtrFactory<GaiaScreenHandler> weak_factory_{this}; base::WeakPtrFactory<GaiaScreenHandler> weak_factory_{this};
......
// Copyright 2020 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/chromeos/login/online_login_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/signin_partition_manager.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_webui.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chromeos/dbus/util/version_loader.h"
#include "chromeos/login/auth/challenge_response/cert_utils.h"
#include "chromeos/login/auth/cryptohome_key_constants.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_urls.h"
#include "ui/base/l10n/l10n_util.h"
namespace chromeos {
namespace login {
namespace {
const char kGAPSCookie[] = "GAPS";
const char kOAUTHCodeCookie[] = "oauth_code";
constexpr base::TimeDelta kCookieDelay = base::TimeDelta::FromSeconds(20);
} // namespace
GaiaContext::GaiaContext() {}
GaiaContext::GaiaContext(GaiaContext const&) = default;
bool ExtractSamlPasswordAttributesEnabled() {
return base::FeatureList::IsEnabled(::features::kInSessionPasswordChange);
}
base::OnceClosure GetStartSigninSession(::content::WebUI* web_ui,
LoadGaiaWithPartition callback) {
// Start a new session with SigninPartitionManager, generating a unique
// StoragePartition.
login::SigninPartitionManager* signin_partition_manager =
login::SigninPartitionManager::Factory::GetForBrowserContext(
Profile::FromWebUI(web_ui));
auto partition_call =
base::BindOnce(&login::SigninPartitionManager::StartSigninSession,
base::Unretained(signin_partition_manager),
web_ui->GetWebContents(), std::move(callback));
return partition_call;
}
void SetCookieForPartition(
const login::GaiaContext& context,
login::SigninPartitionManager* signin_partition_manager,
OnSetCookieForLoadGaiaWithPartition callback) {
content::StoragePartition* partition =
signin_partition_manager->GetCurrentStoragePartition();
if (!partition)
return;
// Note: The CanonicalCookie created here is not Secure. This is fine because
// it's being set into a different StoragePartition than the user's actual
// profile. The SetCanonicalCookie call will succeed regardless of the scheme
// of |gaia_url| since there are no scheme restrictions since the cookie is
// not Secure, and there is no preexisting Secure cookie in the profile that
// would preclude updating it insecurely. |gaia_url| is usually secure, and
// only insecure in local testing.
std::string gaps_cookie_value(kGAPSCookie);
gaps_cookie_value += "=" + context.gaps_cookie;
const GURL& gaia_url = GaiaUrls::GetInstance()->gaia_url();
std::unique_ptr<net::CanonicalCookie> cc(net::CanonicalCookie::Create(
gaia_url, gaps_cookie_value, base::Time::Now(),
base::nullopt /* server_time */));
const net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
partition->GetCookieManagerForBrowserProcess()->SetCanonicalCookie(
*cc.get(), gaia_url, options, std::move(callback));
}
user_manager::UserType GetUsertypeFromServicesString(
const ::login::StringList& services) {
bool is_child = false;
const bool support_usm =
base::FeatureList::IsEnabled(::features::kCrOSEnableUSMUserService);
using KnownFlags = base::flat_set<std::string>;
const KnownFlags known_flags =
support_usm ? KnownFlags({"uca", "usm"}) : KnownFlags({"uca"});
for (const std::string& item : services) {
if (known_flags.find(item) != known_flags.end()) {
is_child = true;
break;
}
}
return is_child ? user_manager::USER_TYPE_CHILD
: user_manager::USER_TYPE_REGULAR;
}
bool BuildUserContextForGaiaSignIn(
user_manager::UserType user_type,
const AccountId& account_id,
bool using_saml,
bool using_saml_api,
const std::string& password,
const SamlPasswordAttributes& password_attributes,
const LoginClientCertUsageObserver&
extension_provided_client_cert_usage_observer,
UserContext* user_context,
std::string* error_message) {
*user_context = UserContext(user_type, account_id);
if (using_saml &&
extension_provided_client_cert_usage_observer.ClientCertsWereUsed()) {
scoped_refptr<net::X509Certificate> saml_client_cert;
std::vector<ChallengeResponseKey::SignatureAlgorithm> signature_algorithms;
std::string extension_id;
if (!extension_provided_client_cert_usage_observer.GetOnlyUsedClientCert(
&saml_client_cert, &signature_algorithms, &extension_id)) {
*error_message = l10n_util::GetStringUTF8(
IDS_CHALLENGE_RESPONSE_AUTH_MULTIPLE_CLIENT_CERTS_ERROR);
return false;
}
ChallengeResponseKey challenge_response_key;
if (!ExtractChallengeResponseKeyFromCert(
*saml_client_cert, signature_algorithms, &challenge_response_key)) {
*error_message = l10n_util::GetStringUTF8(
IDS_CHALLENGE_RESPONSE_AUTH_INVALID_CLIENT_CERT_ERROR);
return false;
}
challenge_response_key.set_extension_id(extension_id);
user_context->GetMutableChallengeResponseKeys()->push_back(
challenge_response_key);
} else {
Key key(password);
key.SetLabel(kCryptohomeGaiaKeyLabel);
user_context->SetKey(key);
user_context->SetPasswordKey(Key(password));
}
user_context->SetAuthFlow(using_saml
? UserContext::AUTH_FLOW_GAIA_WITH_SAML
: UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
if (using_saml) {
user_context->SetIsUsingSamlPrincipalsApi(using_saml_api);
if (ExtractSamlPasswordAttributesEnabled()) {
user_context->SetSamlPasswordAttributes(password_attributes);
}
}
return true;
}
} // namespace login
OnlineLoginHelper::OnlineLoginHelper(
std::string signin_partition_name,
login::SigninPartitionManager* signin_partition_manager,
OnCookieTimeoutCallback on_cookie_timeout_callback,
CompleteLoginCallback complete_login_callback)
: signin_partition_name_(signin_partition_name),
signin_partition_manager_(signin_partition_manager),
on_cookie_timeout_callback_(std::move(on_cookie_timeout_callback)),
complete_login_callback_(std::move(complete_login_callback)) {}
OnlineLoginHelper::~OnlineLoginHelper() {}
void OnlineLoginHelper::SetUserContext(
std::unique_ptr<UserContext> pending_user_context) {
pending_user_context_ = std::move(pending_user_context);
}
void OnlineLoginHelper::RequestCookiesAndCompleteAuthentication() {
content::StoragePartition* partition =
signin_partition_manager_->GetCurrentStoragePartition();
if (!partition)
return;
// Validity check that partition did not change during login flow.
DCHECK_EQ(signin_partition_manager_->GetCurrentStoragePartitionName(),
signin_partition_name_);
network::mojom::CookieManager* cookie_manager =
partition->GetCookieManagerForBrowserProcess();
if (!oauth_code_listener_.is_bound()) {
// Set listener before requesting the cookies to avoid race conditions.
cookie_manager->AddCookieChangeListener(
GaiaUrls::GetInstance()->gaia_url(), login::kOAUTHCodeCookie,
oauth_code_listener_.BindNewPipeAndPassRemote());
cookie_waiting_timer_ = std::make_unique<base::OneShotTimer>();
cookie_waiting_timer_->Start(
FROM_HERE, login::kCookieDelay,
base::BindOnce(&OnlineLoginHelper::OnCookieWaitTimeout,
weak_factory_.GetWeakPtr()));
}
const net::CookieOptions cookie_options =
net::CookieOptions::MakeAllInclusive();
cookie_manager->GetCookieList(
GaiaUrls::GetInstance()->gaia_url(), cookie_options,
base::BindOnce(&OnlineLoginHelper::OnGetCookiesForCompleteAuthentication,
weak_factory_.GetWeakPtr()));
}
void OnlineLoginHelper::OnCookieChange(const net::CookieChangeInfo& change) {
RequestCookiesAndCompleteAuthentication();
}
void OnlineLoginHelper::OnCookieWaitTimeout() {
DCHECK(pending_user_context_);
pending_user_context_.reset();
oauth_code_listener_.reset();
cookie_waiting_timer_.reset();
std::move(on_cookie_timeout_callback_).Run();
}
void OnlineLoginHelper::OnGetCookiesForCompleteAuthentication(
const net::CookieAccessResultList& cookies,
const net::CookieAccessResultList& excluded_cookies) {
std::string auth_code, gaps_cookie;
for (const auto& cookie_with_access_result : cookies) {
const auto& cookie = cookie_with_access_result.cookie;
if (cookie.Name() == login::kOAUTHCodeCookie)
auth_code = cookie.Value();
else if (cookie.Name() == login::kGAPSCookie)
gaps_cookie = cookie.Value();
}
if (auth_code.empty()) {
// Will try again from onCookieChange.
return;
}
DCHECK(pending_user_context_);
UserContext user_context = *pending_user_context_;
pending_user_context_.reset();
oauth_code_listener_.reset();
cookie_waiting_timer_.reset();
user_context.SetAuthCode(auth_code);
if (!gaps_cookie.empty())
user_context.SetGAPSCookie(gaps_cookie);
std::move(complete_login_callback_).Run(user_context);
}
} // namespace chromeos
\ No newline at end of file
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ONLINE_LOGIN_HELPER_H_
#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ONLINE_LOGIN_HELPER_H_
#include <string>
#include "chrome/browser/chromeos/login/login_client_cert_usage_observer.h"
#include "chrome/browser/chromeos/login/signin_partition_manager.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/extensions/api/cookies/cookies_api.h"
#include "chromeos/login/auth/cryptohome_authenticator.h"
#include "components/login/base_screen_handler_utils.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_ui.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
namespace chromeos {
namespace login {
// A class that's used to specify the way how Gaia should be loaded.
struct GaiaContext {
GaiaContext();
GaiaContext(GaiaContext const&);
// Forces Gaia to reload.
bool force_reload = false;
// Whether Gaia should be loaded in offline mode.
bool use_offline = false;
// Email of the current user.
std::string email;
// GAIA ID of the current user.
std::string gaia_id;
// GAPS cookie.
std::string gaps_cookie;
};
using LoadGaiaWithPartition = base::OnceCallback<void(const std::string&)>;
using OnSetCookieForLoadGaiaWithPartition =
base::OnceCallback<void(::net::CookieAccessResult)>;
// Return whether the InSession Password Change feature is enabled.
bool ExtractSamlPasswordAttributesEnabled();
// Return Signin Session callback
base::OnceClosure GetStartSigninSession(::content::WebUI* web_ui,
LoadGaiaWithPartition callback);
// Callback that set GAPS cookie for the partition.
void SetCookieForPartition(
const login::GaiaContext& context,
login::SigninPartitionManager* signin_partition_manager,
OnSetCookieForLoadGaiaWithPartition callback);
// Return whether the user is regular user or child user.
user_manager::UserType GetUsertypeFromServicesString(
const ::login::StringList& services);
// Builds the UserContext with the information from the given Gaia user
// sign-in. On failure, returns false and sets |error_message|.
bool BuildUserContextForGaiaSignIn(
user_manager::UserType user_type,
const AccountId& account_id,
bool using_saml,
bool using_saml_api,
const std::string& password,
const SamlPasswordAttributes& password_attributes,
const LoginClientCertUsageObserver&
extension_provided_client_cert_usage_observer,
UserContext* user_context,
std::string* error_message);
} // namespace login
// This class will be used in authenticating Gaia and SAML users in loginscreen
// and SAML users in lockscreen.
class OnlineLoginHelper : public network::mojom::CookieChangeListener {
public:
using OnCookieTimeoutCallback = base::OnceCallback<void(void)>;
using CompleteLoginCallback = base::OnceCallback<void(const UserContext&)>;
explicit OnlineLoginHelper(
std::string signin_partition_name,
login::SigninPartitionManager* signin_partition_manager,
OnCookieTimeoutCallback on_cookie_timeout_callback,
CompleteLoginCallback complete_login_callback);
OnlineLoginHelper(const OnlineLoginHelper&) = delete;
OnlineLoginHelper& operator=(const OnlineLoginHelper&) = delete;
~OnlineLoginHelper() override;
void SetUserContext(std::unique_ptr<UserContext> pending_user_context);
void RequestCookiesAndCompleteAuthentication();
private:
// network::mojom::CookieChangeListener:
void OnCookieChange(const net::CookieChangeInfo& change) override;
void OnCookieWaitTimeout();
void OnGetCookiesForCompleteAuthentication(
const net::CookieAccessResultList& cookies,
const net::CookieAccessResultList& excluded_cookies);
std::string signin_partition_name_;
login::SigninPartitionManager* signin_partition_manager_;
// Connection to the CookieManager that signals when the GAIA cookies change.
mojo::Receiver<network::mojom::CookieChangeListener> oauth_code_listener_{
this};
std::unique_ptr<UserContext> pending_user_context_;
std::unique_ptr<base::OneShotTimer> cookie_waiting_timer_;
OnCookieTimeoutCallback on_cookie_timeout_callback_;
CompleteLoginCallback complete_login_callback_;
base::WeakPtrFactory<OnlineLoginHelper> weak_factory_{this};
};
} // namespace chromeos
#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ONLINE_LOGIN_HELPER_H_
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment