0

[High5] Allow offline login using PIN

When the user has only a pin factor, allow the user to use pin to login
offline.
Screenshot: https://screenshot.googleplex.com/7g9yggSd9WsZjms

Bug: b:370865046
Change-Id: I64f2560244bfc199a836901c24a8a965a4d7f852
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5936977
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Commit-Queue: Yunke Zhou <yunkez@google.com>
Reviewed-by: Hardik Goyal <hardikgoyal@chromium.org>
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1373331}
This commit is contained in:
Yunke Zhou
2024-10-24 15:19:06 +00:00
committed by Chromium LUCI CQ
parent b0c5660c96
commit 1fcc8c122d
9 changed files with 87 additions and 15 deletions

@ -1895,12 +1895,18 @@
<message name="IDS_OFFLINE_LOGIN_PASSWORD" desc="Password field text on the offline login screen. Note: should be the same as the one used on the Gaia sign-in page.">
Enter your password
</message>
<message name="IDS_OFFLINE_LOGIN_PIN" desc="PIN field placeholder text on the offline login screen.">
Enter your PIN
</message>
<message name="IDS_OFFLINE_LOGIN_INVALID_EMAIL" desc="Alert message about invalid email on the offline login screen. Note: should be the same as the one used on the Gaia sign-in page.">
Please enter a valid email address
</message>
<message name="IDS_OFFLINE_LOGIN_INVALID_PASSWORD" desc="Alert message about mismatching email and password on the device">
The email and password you entered don't match
</message>
<message name="IDS_OFFLINE_LOGIN_INVALID_PIN" desc="Alert message about mismatching email and PIN on the device">
The email and PIN you entered don't match
</message>
<message name="IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT" desc="Text shown on next button during offline signin flow dialog. Note: should be the same as the one used on the Gaia sign-in page.">
Next
</message>

@ -0,0 +1 @@
41ac6cb8edf9b724fdf5ee0bd10f41c6141ebbf5

@ -0,0 +1 @@
96b12f9ce663a2dc8fc8d73342b064e60a0ace85

@ -21,6 +21,7 @@
#include "chrome/browser/ui/webui/ash/login/offline_login_screen_handler.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/login/auth/public/auth_types.h"
#include "chromeos/ash/components/login/auth/public/key.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
@ -77,6 +78,7 @@ std::string OfflineLoginScreen::GetResultString(Result result) {
OfflineLoginScreen::OfflineLoginScreen(base::WeakPtr<OfflineLoginView> view,
const ScreenExitCallback& exit_callback)
: BaseScreen(OfflineLoginView::kScreenId, OobeScreenPriority::DEFAULT),
auth_factor_editor_(UserDataAuthClient::Get()),
view_(std::move(view)),
exit_callback_(exit_callback) {
network_state_informer_ = base::MakeRefCounted<NetworkStateInformer>();
@ -112,6 +114,7 @@ void OfflineLoginScreen::ShowImpl() {
void OfflineLoginScreen::HideImpl() {
scoped_observer_.reset();
idle_detector_.reset();
authenticate_by_pin_ = false;
if (view_)
view_->Hide();
}
@ -155,11 +158,13 @@ void OfflineLoginScreen::HandleCompleteAuth(const std::string& email,
UserContext user_context(*user);
user_context.SetKey(Key(password));
user_context.SetLocalPasswordInput(LocalPasswordInput{password});
// Save the user's plaintext password for possible authentication to a
// network. See https://crbug.com/386606 for details.
user_context.SetPasswordKey(Key(password));
user_context.SetIsUsingPin(false);
if (!authenticate_by_pin_) {
user_context.SetLocalPasswordInput(LocalPasswordInput{password});
// Save the user's plaintext password for possible authentication to a
// network. See https://crbug.com/386606 for details.
user_context.SetPasswordKey(Key(password));
}
user_context.SetIsUsingPin(authenticate_by_pin_);
CHECK(account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY)
<< "Incorrect Active Directory user type " << user_context.GetUserType();
user_context.SetIsUsingOAuth(false);
@ -205,12 +210,31 @@ void OfflineLoginScreen::HandleEmailSubmitted(const std::string& email) {
user_manager::UserManager::Get()->FindUser(account_id);
if (user && user->force_online_signin()) {
RecordEvent(OfflineLoginEvent::kOfflineLoginBlockedByInvalidToken);
view_->ShowPasswordPage();
view_->ShowOnlineRequiredDialog();
return;
}
auth_factor_editor_.GetAuthFactorsConfiguration(
std::make_unique<UserContext>(*user),
base::BindOnce(&OfflineLoginScreen::OnGetAuthFactorsConfiguration,
weak_ptr_factory_.GetWeakPtr()));
}
void OfflineLoginScreen::OnGetAuthFactorsConfiguration(
std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> error) {
if (error.has_value()) {
LOG(ERROR) << "Failed to get auth factors configuration, code "
<< error->get_cryptohome_error();
} else {
const auto& config = user_context->GetAuthFactorsConfiguration();
// Allow authentication by PIN if that is the user's only auth factor.
authenticate_by_pin_ =
!config.HasConfiguredFactor(cryptohome::AuthFactorType::kPassword) &&
config.HasConfiguredFactor(cryptohome::AuthFactorType::kPin);
}
RecordEvent(OfflineLoginEvent::kOfflineLoginEnabled);
view_->ShowPasswordPage();
view_->ShowPasswordPage(authenticate_by_pin_);
}
void OfflineLoginScreen::StartIdleDetection() {

@ -11,6 +11,7 @@
#include "base/scoped_observation.h"
#include "chrome/browser/ash/login/screens/base_screen.h"
#include "chrome/browser/ui/webui/ash/login/network_state_informer.h"
#include "chromeos/ash/components/login/auth/auth_factor_editor.h"
#include "chromeos/ash/experiences/idle_detector/idle_detector.h"
namespace ash {
@ -56,8 +57,20 @@ class OfflineLoginScreen
const std::string& password);
void HandleEmailSubmitted(const std::string& username);
void OnGetAuthFactorsConfiguration(std::unique_ptr<UserContext> user_context,
std::optional<AuthenticationError> error);
// The editor is used to call `ListAuthFactors` to fetch password & pin factor
// status. It does not change factor status.
// TODO: Update `Authenticator` to allow AuthSession to start earlier so we
// could get auth factor status from the AuthSession.
AuthFactorEditor auth_factor_editor_;
base::WeakPtr<OfflineLoginView> view_;
// Whether the user has only pin factor and should be authenticated by pin.
bool authenticate_by_pin_ = false;
// True when network is available.
bool is_network_available_ = false;

@ -238,9 +238,10 @@ found in the LICENSE file.
</div>
<cr-input id="passwordInput" value="{{password}}"
on-keydown="onKeyDown" disabled="[[disabled]]"
type="password" required error-message="[[i18nDynamic(
locale, 'offlineLoginInvalidPassword')]]"
placeholder="[[i18nDynamic(locale, 'offlineLoginPassword')]]">
type="password" required
error-message="[[passwordErrorText(locale, authenticateByPin)]]"
placeholder=
"[[passwordPlaceholderText(locale,authenticateByPin)]]">
</cr-input>
<div class="layout horizontal-reverse">
<gaia-button on-click="onForgotPasswordClicked" link

@ -101,6 +101,14 @@ export class OfflineLogin extends OfflineLoginBase {
value: '',
},
/**
* Whether the user should be authenticated by pin or password.
*/
authenticateByPin: {
type: Boolean,
value: false,
},
/**
* Proper e-mail with domain, displayed on password page.
*/
@ -127,6 +135,7 @@ export class OfflineLogin extends OfflineLoginBase {
private displayDomain: string;
private email: string;
private password: string;
private authenticateByPin: boolean;
private fullEmail: string;
private activeSection: string;
private animationInProgress: boolean;
@ -197,6 +206,7 @@ export class OfflineLogin extends OfflineLoginBase {
this.manager = '';
this.email = '';
this.fullEmail = '';
this.authenticateByPin = false;
this.shadowRoot!.querySelector<CrInputElement>('#emailInput')!.invalid =
false;
this.shadowRoot!.querySelector<CrInputElement>('#passwordInput')!.invalid =
@ -204,7 +214,8 @@ export class OfflineLogin extends OfflineLoginBase {
this.activeSection = LoginSection.EMAIL;
}
proceedToPasswordPage(): void {
proceedToPasswordPage(authenticateByPin: boolean): void {
this.authenticateByPin = authenticateByPin;
this.switchToPasswordCard(true /* animated */);
}
@ -343,6 +354,19 @@ export class OfflineLogin extends OfflineLoginBase {
}
this.onNextButtonClicked();
}
private passwordPlaceholderText(locale: string, authenticateByPin: boolean):
string {
const key = authenticateByPin ? 'offlineLoginPin' : 'offlineLoginPassword';
return this.i18nDynamic(locale, key);
}
private passwordErrorText(locale: string, authenticateByPin: boolean):
string {
const key = authenticateByPin ? 'offlineLoginInvalidPin' :
'offlineLoginInvalidPassword';
return this.i18nDynamic(locale, key);
}
}
declare global {

@ -21,9 +21,11 @@ void OfflineLoginScreenHandler::DeclareLocalizedValues(
::login::LocalizedValuesBuilder* builder) {
builder->Add("offlineLoginEmail", IDS_OFFLINE_LOGIN_EMAIL);
builder->Add("offlineLoginPassword", IDS_OFFLINE_LOGIN_PASSWORD);
builder->Add("offlineLoginPin", IDS_OFFLINE_LOGIN_PIN);
builder->Add("offlineLoginInvalidEmail", IDS_OFFLINE_LOGIN_INVALID_EMAIL);
builder->Add("offlineLoginInvalidPassword",
IDS_OFFLINE_LOGIN_INVALID_PASSWORD);
builder->Add("offlineLoginInvalidPin", IDS_OFFLINE_LOGIN_INVALID_PIN);
builder->Add("offlineLoginNextBtn", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
builder->Add("offlineLoginForgotPasswordBtn",
IDS_OFFLINE_LOGIN_FORGOT_PASSWORD_BUTTON_TEXT);
@ -47,8 +49,8 @@ void OfflineLoginScreenHandler::Reset() {
CallExternalAPI("reset");
}
void OfflineLoginScreenHandler::ShowPasswordPage() {
CallExternalAPI("proceedToPasswordPage");
void OfflineLoginScreenHandler::ShowPasswordPage(bool authenticate_by_pin) {
CallExternalAPI("proceedToPasswordPage", authenticate_by_pin);
}
void OfflineLoginScreenHandler::ShowOnlineRequiredDialog() {

@ -29,7 +29,7 @@ class OfflineLoginView {
virtual void Reset() = 0;
// Proceeds to the password input dialog.
virtual void ShowPasswordPage() = 0;
virtual void ShowPasswordPage(bool authenticate_by_pin) = 0;
// Shows error pop-up when the user cannot login offline.
virtual void ShowOnlineRequiredDialog() = 0;
@ -60,7 +60,7 @@ class OfflineLoginScreenHandler final : public BaseScreenHandler,
void Show(base::Value::Dict params) override;
void Hide() override;
void Reset() override;
void ShowPasswordPage() override;
void ShowPasswordPage(bool authenticate_by_pin) override;
void ShowOnlineRequiredDialog() override;
void ShowPasswordMismatchMessage() override;
base::WeakPtr<OfflineLoginView> AsWeakPtr() override;