Bug 1403081 - Optionally protect filling of saved logins with OS authentication (including biometrics). r=sgalich,settings-reviewers,fluent-reviewers,flod

Depends on D207219

Differential Revision: https://phabricator.services.mozilla.com/D201276
This commit is contained in:
Sidharth Sachdev 2024-05-05 15:59:35 +00:00
parent 15b24feecc
commit 78d3a3cf9d
6 changed files with 93 additions and 26 deletions

View File

@ -17,7 +17,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
LoginExport: "resource://gre/modules/LoginExport.sys.mjs",
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs",
UIState: "resource://services-sync/UIState.sys.mjs",
});
@ -36,12 +35,6 @@ XPCOMUtils.defineLazyPreferenceGetter(
"identity.fxaccounts.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"OS_AUTH_ENABLED",
"signon.management.page.os-auth.enabled",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"VULNERABLE_PASSWORDS_ENABLED",
@ -266,11 +259,15 @@ export class AboutLoginsParent extends JSWindowActorParent {
let messageText = { value: "NOT SUPPORTED" };
let captionText = { value: "" };
const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF
);
// This feature is only supported on Windows and macOS
// but we still call in to OSKeyStore on Linux to get
// the proper auth_details for Telemetry.
// See bug 1614874 for Linux support.
if (lazy.OS_AUTH_ENABLED && lazy.OSKeyStore.canReauth()) {
if (isOSAuthEnabled) {
messageId += "-" + AppConstants.platform;
[messageText, captionText] = await lazy.AboutLoginsL10n.formatMessages([
{
@ -284,7 +281,7 @@ export class AboutLoginsParent extends JSWindowActorParent {
let { isAuthorized, telemetryEvent } = await lazy.LoginHelper.requestReauth(
this.browsingContext.embedderElement,
lazy.OS_AUTH_ENABLED,
isOSAuthEnabled,
AboutLogins._authExpirationTime,
messageText.value,
captionText.value
@ -378,11 +375,15 @@ export class AboutLoginsParent extends JSWindowActorParent {
let messageText = { value: "NOT SUPPORTED" };
let captionText = { value: "" };
const isOSAuthEnabled = lazy.LoginHelper.getOSAuthEnabled(
lazy.LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF
);
// This feature is only supported on Windows and macOS
// but we still call in to OSKeyStore on Linux to get
// the proper auth_details for Telemetry.
// See bug 1614874 for Linux support.
if (lazy.OSKeyStore.canReauth()) {
if (isOSAuthEnabled) {
const messageId =
EXPORT_PASSWORD_OS_AUTH_DIALOG_MESSAGE_IDS[AppConstants.platform];
if (!messageId) {

View File

@ -551,6 +551,12 @@
/>
</hbox>
</vbox>
<vbox>
<hbox id="osReauthRow" align="center">
<checkbox id="osReauthCheckbox"
data-l10n-id="forms-os-reauth"/>
</hbox>
</vbox>
<vbox>
<hbox id="masterPasswordRow" align="center">
<checkbox id="useMasterPassword"

View File

@ -60,6 +60,10 @@ ChromeUtils.defineLazyGetter(this, "AlertsServiceDND", function () {
}
});
ChromeUtils.defineLazyGetter(lazy, "AboutLoginsL10n", () => {
return new Localization(["branding/brand.ftl", "browser/aboutLogins.ftl"]);
});
XPCOMUtils.defineLazyServiceGetter(
lazy,
"gParentalControlsService",
@ -67,13 +71,6 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIParentalControlsService"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"OS_AUTH_ENABLED",
"signon.management.page.os-auth.enabled",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gIsFirstPartyIsolated",
@ -1053,6 +1050,7 @@ var gPrivacyPane = {
this._initPasswordGenerationUI();
this._initRelayIntegrationUI();
this._initMasterPasswordUI();
this._initOSAuthentication();
this.initListenersForExtensionControllingPasswordManager();
@ -2863,8 +2861,7 @@ var gPrivacyPane = {
// OS reauthenticate functionality is not available on Linux yet (bug 1527745)
if (
!LoginHelper.isPrimaryPasswordSet() &&
OS_AUTH_ENABLED &&
OSKeyStore.canReauth()
LoginHelper.getOSAuthEnabled(LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF)
) {
// Uses primary-password-os-auth-dialog-message-win and
// primary-password-os-auth-dialog-message-macosx via concatenation:
@ -2961,6 +2958,54 @@ var gPrivacyPane = {
this._updateRelayIntegrationUI();
},
async _toggleOSAuth() {
let osReauthCheckbox = document.getElementById("osReauthCheckbox");
const messageText = await lazy.AboutLoginsL10n.formatValue(
"about-logins-os-auth-dialog-message"
);
const captionText = await lazy.AboutLoginsL10n.formatValue(
"about-logins-os-auth-dialog-caption"
);
let win =
osReauthCheckbox.ownerGlobal.docShell.chromeEventHandler.ownerGlobal;
// Calling OSKeyStore.ensureLoggedIn() instead of LoginHelper.verifyOSAuth()
// since we want to authenticate user each time this stting is changed.
let isAuthorized = (
await OSKeyStore.ensureLoggedIn(messageText, captionText, win, false)
).authenticated;
if (!isAuthorized) {
osReauthCheckbox.checked = !osReauthCheckbox.checked;
return;
}
// If osReauthCheckbox is checked enable osauth.
LoginHelper.setOSAuthEnabled(
LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF,
osReauthCheckbox.checked
);
},
_initOSAuthentication() {
let osReauthCheckbox = document.getElementById("osReauthCheckbox");
if (!OSKeyStore.canReauth()) {
osReauthCheckbox.hidden = true;
return;
}
osReauthCheckbox.setAttribute(
"checked",
LoginHelper.getOSAuthEnabled(LoginHelper.OS_AUTH_FOR_PASSWORDS_PREF)
);
setEventListener(
"osReauthCheckbox",
"command",
gPrivacyPane._toggleOSAuth.bind(gPrivacyPane)
);
},
/**
* Shows the sites where the user has saved passwords and the associated login
* information.

View File

@ -134,12 +134,20 @@ login-item-timeline-action-used = Used
## OS Authentication dialog
about-logins-os-auth-dialog-caption = { -brand-full-name }
## The macOS strings are preceded by the operating system with "Firefox is trying to "
## and includes subtitle of "Enter password for the user "xxx" to allow this." These
## notes are only valid for English. Please test in your respected locale.
# The macOS strings are preceded by the operating system with "Firefox is trying to ".
# This message can be seen when attempting to disable osauth in about:preferences.
about-logins-os-auth-dialog-message=
{ PLATFORM() ->
[macos] change the settings for passwords
*[other] { -brand-short-name } is trying to change the settings for passwords. Use your device sign in to allow this.
}
about-logins-os-auth-dialog-caption = { -brand-full-name }
# This message can be seen when attempting to edit a login in about:logins on Windows.
about-logins-edit-login-os-auth-dialog-message2-win = To edit your password, enter your Windows login credentials. This helps protect the security of your accounts.
# This message can be seen when attempting to edit a login in about:logins

View File

@ -1036,6 +1036,9 @@ forms-saved-passwords =
forms-primary-pw-use =
.label = Use a Primary Password
.accesskey = U
# This operation requires the user to authenticate with the operating system (device sign-in)
forms-os-reauth =
.label = Require device sign in to fill and manage passwords
forms-primary-pw-learn-more-link = Learn more
# This string uses the former name of the Primary Password feature
# ("Master Password" in English) so that the preferences can be found

View File

@ -373,7 +373,7 @@ class ImportRowProcessor {
return this.summary;
}
}
const OS_AUTH_FOR_PASSWORDS_PREF = "signon.management.page.os-auth.optout";
/**
* Contains functions shared by different Login Manager components.
*/
@ -401,6 +401,7 @@ export const LoginHelper = {
testOnlyUserHasInteractedWithDocument: null,
userInputRequiredToCapture: null,
captureInputChanges: null,
OS_AUTH_FOR_PASSWORDS_PREF,
init() {
// Watch for pref changes to update cached pref values.
@ -1728,18 +1729,21 @@ export const LoginHelper = {
}
// Use the OS auth dialog if there is no primary password
if (!token.hasPassword && OSReauthEnabled) {
let result = await lazy.OSKeyStore.ensureLoggedIn(
let isAuthorized = await this.verifyUserOSAuth(
OS_AUTH_FOR_PASSWORDS_PREF,
messageText,
captionText,
browser.ownerGlobal,
false
);
isAuthorized = result.authenticated;
let value = lazy.OSKeyStore.canReauth()
? "success"
: "success_unsupported_platform";
telemetryEvent = {
object: "os_auth",
method: "reauthenticate",
value: result.auth_details,
extra: result.auth_details_extra,
value: isAuthorized ? value : "fail",
};
return {
isAuthorized,