0

[High 5] Implement Active Session Auth Controller.

Handling cryptohome based pin and password.

Bug: b:352238958, b:348326316
Change-Id: I5745e958037ebea607c22a28be65ba1bd3494f75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5712829
Reviewed-by: Hardik Goyal <hardikgoyal@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Commit-Queue: Hardik Goyal <hardikgoyal@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1332904}
This commit is contained in:
Elie Maamari
2024-07-25 13:19:39 +00:00
committed by Chromium LUCI CQ
parent 32fb53e6a4
commit a3ab723d86
12 changed files with 510 additions and 0 deletions

@ -348,6 +348,8 @@ component("ash") {
"assistant/assistant_web_ui_controller.h",
"assistant/assistant_web_view_delegate_impl.cc",
"assistant/assistant_web_view_delegate_impl.h",
"auth/active_session_auth_controller_impl.cc",
"auth/active_session_auth_controller_impl.h",
"auth/views/active_session_auth_view.cc",
"auth/views/active_session_auth_view.h",
"auth/views/auth_common.h",

@ -5236,6 +5236,12 @@ No devices connected.
<message name="IDS_ASH_IN_SESSION_AUTH_ACCESSIBLE_TITLE" desc="Accessibility text read by chromevox to indicate which website / app we are authenticating for.">
Verify your identity: <ph name="ORIGIN_NAME">$1<ex>example.com</ex></ph> would like to confirm it's you
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_SETTINGS_PROMPT" desc="Text shown in the settings auth dialog.">
ChromeOS Settings would like to confirm it's you
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_PASSWORD_MANAGER_PROMPT" desc="Text shown in the password manager auth dialog.">
ChromeOS Password Manager would like to confirm it's you
</message>
<message name="IDS_ASH_IN_SESSION_AUTH_PASSWORD_PLACEHOLDER" desc="Text to display as placeholder in the password input field.">
Password
</message>

@ -0,0 +1 @@
5cd8069cfaad9b12fac178fdb52b8bc046a987bf

@ -0,0 +1 @@
5bac6407f632a3445c262bd4d1c36138e3e30a1e

3
ash/auth/DEPS Normal file

@ -0,0 +1,3 @@
include_rules = [
"+components/user_manager/user_manager.h",
]

@ -0,0 +1,296 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/auth/active_session_auth_controller_impl.h"
#include <memory>
#include <string>
#include "ash/auth/views/active_session_auth_view.h"
#include "ash/auth/views/auth_common.h"
#include "ash/auth/views/auth_view_utils.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chromeos/ash/components/cryptohome/constants.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "chromeos/ash/components/login/auth/public/auth_session_intent.h"
#include "chromeos/ash/components/login/auth/public/session_auth_factors.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_types.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
namespace {
std::unique_ptr<views::Widget> CreateAuthDialogWidget(
std::unique_ptr<views::View> contents_view) {
views::Widget::InitParams params(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
params.delegate = new views::WidgetDelegate();
params.show_state = ui::SHOW_STATE_NORMAL;
CHECK_EQ(Shell::Get()->session_controller()->GetSessionState(),
session_manager::SessionState::ACTIVE);
params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
kShellWindowId_SystemModalContainer);
params.autosize = true;
params.name = "AuthDialogWidget";
params.delegate->SetInitiallyFocusedView(contents_view.get());
params.delegate->SetModalType(ui::MODAL_TYPE_NONE);
params.delegate->SetOwnedByWidget(true);
std::unique_ptr<views::Widget> widget = std::make_unique<views::Widget>();
widget->Init(std::move(params));
widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
widget->SetContentsView(std::move(contents_view));
return widget;
}
} // namespace
ActiveSessionAuthControllerImpl::ActiveSessionAuthControllerImpl() = default;
ActiveSessionAuthControllerImpl::~ActiveSessionAuthControllerImpl() = default;
bool ActiveSessionAuthControllerImpl::ShowAuthDialog(
Reason reason,
const AccountId& account_id,
AuthCompletionCallback on_auth_complete) {
if (IsShown()) {
LOG(ERROR) << "ActiveSessionAuthController widget is already exists.";
return false;
}
title_ = l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_TITLE);
description_ = l10n_util::GetStringUTF16(
reason == Reason::kSettings
? IDS_ASH_IN_SESSION_AUTH_SETTINGS_PROMPT
: IDS_ASH_IN_SESSION_AUTH_PASSWORD_MANAGER_PROMPT);
on_auth_complete_ = std::move(on_auth_complete);
auth_factor_editor_ =
std::make_unique<AuthFactorEditor>(UserDataAuthClient::Get());
auth_performer_ = std::make_unique<AuthPerformer>(UserDataAuthClient::Get());
account_id_ = account_id;
user_manager::User* active_user =
user_manager::UserManager::Get()->GetActiveUser();
auto user_context = std::make_unique<UserContext>(*active_user);
auth_factor_editor_->GetAuthFactorsConfiguration(
std::move(user_context),
base::BindOnce(&ActiveSessionAuthControllerImpl::OnAuthFactorsListed,
weak_ptr_factory_.GetWeakPtr()));
return true;
}
bool ActiveSessionAuthControllerImpl::IsShown() const {
return widget_ != nullptr;
}
void ActiveSessionAuthControllerImpl::OnAuthFactorsListed(
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error) {
if (authentication_error.has_value()) {
LOG(ERROR) << "Failed to start auth session, code "
<< authentication_error->get_cryptohome_code();
Close();
return;
}
const auto& config = user_context->GetAuthFactorsConfiguration();
AuthFactorSet available_factors;
if (config.FindFactorByType(cryptohome::AuthFactorType::kPassword)) {
available_factors.Put(AuthInputType::kPassword);
}
if (config.FindFactorByType(cryptohome::AuthFactorType::kPin)) {
available_factors.Put(AuthInputType::kPin);
}
auto contents_view = std::make_unique<ActiveSessionAuthView>(
account_id_, title_, description_, available_factors);
contents_view_ = contents_view.get();
contents_view_->AddObserver(this);
widget_ = CreateAuthDialogWidget(std::move(contents_view));
contents_view_observer_.Observe(contents_view_);
MoveToTheCenter();
widget_->Show();
}
void ActiveSessionAuthControllerImpl::Close() {
contents_view_observer_.Reset();
CHECK(contents_view_);
contents_view_->RemoveObserver(this);
contents_view_ = nullptr;
if (auth_performer_) {
auth_performer_->InvalidateCurrentAttempts();
auth_performer_.reset();
}
auth_factor_editor_.reset();
title_.clear();
description_.clear();
widget_.reset();
if (on_auth_complete_) {
std::move(on_auth_complete_)
.Run(false, ash::AuthProofToken{}, base::TimeDelta{});
}
}
void ActiveSessionAuthControllerImpl::OnViewPreferredSizeChanged(
views::View* observed_view) {
MoveToTheCenter();
}
void ActiveSessionAuthControllerImpl::MoveToTheCenter() {
widget_->CenterWindow(widget_->GetContentsView()->GetPreferredSize());
}
void ActiveSessionAuthControllerImpl::OnPasswordSubmit(
const std::u16string& password) {
user_manager::User* active_user =
user_manager::UserManager::Get()->GetActiveUser();
auto user_context = std::make_unique<UserContext>(*active_user);
CHECK_EQ(active_user->GetAccountId(), account_id_);
const bool ephemeral =
user_manager::UserManager::Get()->IsUserCryptohomeDataEphemeral(
account_id_);
auth_performer_->StartAuthSession(
std::move(user_context), ephemeral, AuthSessionIntent::kVerifyOnly,
base::BindOnce(&ActiveSessionAuthControllerImpl::OnPasswordSessionStarted,
weak_ptr_factory_.GetWeakPtr(), password));
}
void ActiveSessionAuthControllerImpl::OnPasswordSessionStarted(
const std::u16string& password,
bool exists,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error) {
if (authentication_error.has_value()) {
LOG(ERROR) << "Failed to start auth session, code "
<< authentication_error->get_cryptohome_code();
// TODO: Is close is the right thing to do?
Close();
return;
}
const auto* password_factor =
user_context->GetAuthFactorsData().FindAnyPasswordFactor();
CHECK(password_factor);
const cryptohome::KeyLabel key_label = password_factor->ref().label();
auth_performer_->AuthenticateWithPassword(
key_label.value(), base::UTF16ToUTF8(password), std::move(user_context),
base::BindOnce(&ActiveSessionAuthControllerImpl::OnAuthComplete,
weak_ptr_factory_.GetWeakPtr(), AuthInputType::kPassword));
}
void ActiveSessionAuthControllerImpl::OnPinSubmit(const std::u16string& pin) {
user_manager::User* active_user =
user_manager::UserManager::Get()->GetActiveUser();
auto user_context = std::make_unique<UserContext>(*active_user);
CHECK_EQ(active_user->GetAccountId(), account_id_);
const bool ephemeral =
user_manager::UserManager::Get()->IsUserCryptohomeDataEphemeral(
account_id_);
auth_performer_->StartAuthSession(
std::move(user_context), ephemeral, AuthSessionIntent::kVerifyOnly,
base::BindOnce(&ActiveSessionAuthControllerImpl::OnPinSessionStarted,
weak_ptr_factory_.GetWeakPtr(), pin));
}
void ActiveSessionAuthControllerImpl::OnPinSessionStarted(
const std::u16string& pin,
bool exists,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error) {
if (authentication_error.has_value()) {
LOG(ERROR) << "Failed to start auth session, code "
<< authentication_error->get_cryptohome_code();
// TODO: Is close is the right thing to do?
Close();
return;
}
// TODO: Is this correct?!
user_manager::KnownUser known_user(Shell::Get()->local_state());
const std::string salt =
*known_user.FindStringPath(account_id_, prefs::kQuickUnlockPinSalt);
auth_performer_->AuthenticateWithPin(
base::UTF16ToUTF8(pin), salt, std::move(user_context),
base::BindOnce(&ActiveSessionAuthControllerImpl::OnAuthComplete,
weak_ptr_factory_.GetWeakPtr(), AuthInputType::kPin));
}
void ActiveSessionAuthControllerImpl::OnAuthComplete(
AuthInputType input_type,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error) {
if (authentication_error.has_value()) {
contents_view_->SetErrorTitle(l10n_util::GetStringUTF16(
input_type == AuthInputType::kPassword
? IDS_ASH_IN_SESSION_AUTH_PASSWORD_INCORRECT
: IDS_ASH_IN_SESSION_AUTH_PIN_INCORRECT));
} else {
ExchangeForToken(
std::move(user_context),
base::BindOnce(&ActiveSessionAuthControllerImpl::NotifySuccess,
weak_ptr_factory_.GetWeakPtr()));
}
}
void ActiveSessionAuthControllerImpl::ExchangeForToken(
std::unique_ptr<UserContext> user_context,
InSessionAuthTokenProvider::OnAuthTokenGenerated callback) {
AuthProofToken token =
AuthSessionStorage::Get()->Store(std::move(user_context));
std::move(callback).Run(token, cryptohome::kAuthsessionInitialLifetime);
}
void ActiveSessionAuthControllerImpl::NotifySuccess(const AuthProofToken& token,
base::TimeDelta timeout) {
CHECK(on_auth_complete_);
std::move(on_auth_complete_).Run(true, token, timeout);
Close();
}
void ActiveSessionAuthControllerImpl::OnClose() {
Close();
}
} // namespace ash

@ -0,0 +1,113 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_IMPL_H_
#define ASH_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_IMPL_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "ash/auth/views/active_session_auth_view.h"
#include "ash/auth/views/auth_common.h"
#include "ash/public/cpp/auth/active_session_auth_controller.h"
#include "ash/public/cpp/in_session_auth_token_provider.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/login/auth/auth_factor_editor.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "chromeos/ash/components/osauth/public/common_types.h"
#include "components/account_id/account_id.h"
#include "ui/aura/window.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h"
namespace ash {
// ActiveSessionAuthControllerImpl is responsible for :
// - Initialize the ActiveSessionAuthView and control this view
// - Create and manage a widget to show the ActiveSessionAuthView
// - Listening the ActiveSessionAuthView observers and call auth performer if
// authentication is requested on the UI
// - Call the callback with the authentication result
class ASH_EXPORT ActiveSessionAuthControllerImpl
: public ActiveSessionAuthController,
public ActiveSessionAuthView::Observer,
public views::ViewObserver,
public InSessionAuthTokenProvider {
public:
ActiveSessionAuthControllerImpl();
ActiveSessionAuthControllerImpl(const ActiveSessionAuthControllerImpl&) =
delete;
ActiveSessionAuthControllerImpl& operator=(
const ActiveSessionAuthControllerImpl&) = delete;
~ActiveSessionAuthControllerImpl() override;
// ActiveSessionAuthController:
bool ShowAuthDialog(Reason reason,
const AccountId& account_id,
AuthCompletionCallback on_auth_complete) override;
bool IsShown() const override;
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* observed_view) override;
// ActiveSessionAuthView::Observer:
void OnPasswordSubmit(const std::u16string& password) override;
void OnPinSubmit(const std::u16string& pin) override;
void OnClose() override;
// InSessionAuthTokenProvider:
void ExchangeForToken(
std::unique_ptr<UserContext> user_context,
InSessionAuthTokenProvider::OnAuthTokenGenerated callback) override;
// Actions:
void MoveToTheCenter();
void Close();
private:
// Internal methods for authentication.
void OnAuthFactorsListed(
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error);
void OnPasswordSessionStarted(
const std::u16string& password,
bool exists,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error);
void OnAuthComplete(AuthInputType input_type,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error);
void OnPinSessionStarted(
const std::u16string& pin,
bool exists,
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> authentication_error);
void NotifySuccess(const AuthProofToken& token, base::TimeDelta timeout);
std::unique_ptr<views::Widget> widget_;
base::ScopedObservation<views::View, ViewObserver> contents_view_observer_{
this};
raw_ptr<ActiveSessionAuthView> contents_view_ = nullptr;
AccountId account_id_;
std::u16string title_;
std::u16string description_;
AuthCompletionCallback on_auth_complete_;
std::unique_ptr<AuthFactorEditor> auth_factor_editor_;
std::unique_ptr<AuthPerformer> auth_performer_;
base::WeakPtrFactory<ActiveSessionAuthControllerImpl> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_IMPL_H_

@ -107,6 +107,8 @@ component("cpp") {
"assistant/controller/assistant_ui_controller.h",
"audio_config_service.cc",
"audio_config_service.h",
"auth/active_session_auth_controller.cc",
"auth/active_session_auth_controller.h",
"back_gesture_contextual_nudge_controller.h",
"back_gesture_contextual_nudge_delegate.h",
"bluetooth_config_service.cc",

@ -0,0 +1,28 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/auth/active_session_auth_controller.h"
#include "base/check_op.h"
namespace ash {
namespace {
ActiveSessionAuthController* g_instance = nullptr;
}
ActiveSessionAuthController::ActiveSessionAuthController() {
CHECK_EQ(nullptr, g_instance);
g_instance = this;
}
ActiveSessionAuthController::~ActiveSessionAuthController() {
CHECK_EQ(this, g_instance);
g_instance = nullptr;
}
ActiveSessionAuthController* ActiveSessionAuthController::Get() {
return g_instance;
}
} // namespace ash

@ -0,0 +1,50 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_H_
#define ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_H_
#include <memory>
#include "ash/public/cpp/ash_public_export.h"
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "chromeos/ash/components/osauth/public/common_types.h"
namespace ash {
// ActiveSessionAuthController serves active session authentication requests.
// It takes care of showing and hiding the UI and the authentication process.
class ASH_PUBLIC_EXPORT ActiveSessionAuthController {
public:
using AuthCompletionCallback =
base::OnceCallback<void(bool success,
const ash::AuthProofToken& token,
base::TimeDelta timeout)>;
enum Reason {
kPasswordManager,
kSettings,
};
static ActiveSessionAuthController* Get();
virtual ~ActiveSessionAuthController();
// Shows a standalone authentication widget.
// |callback| is invoked when the widget is closed e.g with the back button
// or the correct code is entered.
// Returns whether opening the widget was successful. Will fail if another
// widget is already opened.
virtual bool ShowAuthDialog(Reason reason,
const AccountId& account_id,
AuthCompletionCallback on_auth_complete) = 0;
virtual bool IsShown() const = 0;
protected:
ActiveSessionAuthController();
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_AUTH_ACTIVE_SESSION_AUTH_CONTROLLER_H_

@ -40,6 +40,7 @@
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_feature_usage_metrics.h"
#include "ash/assistant/assistant_controller_impl.h"
#include "ash/auth/active_session_auth_controller_impl.h"
#include "ash/birch/birch_model.h"
#include "ash/booting/booting_animation_controller.h"
#include "ash/calendar/calendar_controller.h"
@ -693,6 +694,8 @@ Shell::Shell(std::unique_ptr<ShellDelegate> shell_delegate)
parent_access_controller_(std::make_unique<ParentAccessControllerImpl>()),
local_authentication_request_controller_(
std::make_unique<LocalAuthenticationRequestControllerImpl>()),
active_session_auth_controller_(
std::make_unique<ActiveSessionAuthControllerImpl>()),
session_controller_(std::make_unique<SessionControllerImpl>()),
feature_discover_reporter_(
std::make_unique<FeatureDiscoveryDurationReporterImpl>(

@ -16,6 +16,7 @@
#include "ash/in_session_auth/in_session_auth_dialog_controller_impl.h"
#include "ash/metrics/login_unlock_throughput_recorder.h"
#include "ash/metrics/unlock_throughput_recorder.h"
#include "ash/public/cpp/auth/active_session_auth_controller.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/system_sounds_delegate.h"
@ -644,6 +645,9 @@ class ASH_EXPORT Shell : public SessionObserver,
local_authentication_request_controller() {
return local_authentication_request_controller_.get();
}
ActiveSessionAuthController* active_session_auth_controller() {
return active_session_auth_controller_.get();
}
LoginScreenController* login_screen_controller() {
return login_screen_controller_.get();
}
@ -1104,6 +1108,7 @@ class ASH_EXPORT Shell : public SessionObserver,
std::unique_ptr<ParentAccessController> parent_access_controller_;
std::unique_ptr<LocalAuthenticationRequestController>
local_authentication_request_controller_;
std::unique_ptr<ActiveSessionAuthController> active_session_auth_controller_;
std::unique_ptr<PciePeripheralNotificationController>
pcie_peripheral_notification_controller_;
std::unique_ptr<PickerController> picker_controller_;