Skip to content

Bug 2064395 - Lock the FELT session on browser close - #1308

Open
jporter-dev wants to merge 23 commits into
mozilla:enterprise-mainfrom
jporter-dev:persist-session-on-browser-close
Open

Bug 2064395 - Lock the FELT session on browser close#1308
jporter-dev wants to merge 23 commits into
mozilla:enterprise-mainfrom
jporter-dev:persist-session-on-browser-close

Conversation

@jporter-dev

@jporter-dev jporter-dev commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Description

Bugzilla: Bug-2064395

Adds session locking for FELT SSO sessions to require OS auth to resume. Instead of always signing out when the browser closes, FELT can now lock the session, controlled by policy: the refresh token is encrypted via OSKeyStore and persisted, then resumed on next launch after the user passes an OS auth prompt. If locking is disabled or unavailable, we fall back to the existing sign-out-on-close behavior.

When a session is "locked" the user will be prompted with the OS-level authentication prompt (Windows Hello, TouchID, password prompt) instead of SSO when proceeding to sign in from FELT with their email address.

  • Adds enterprise.locking.on_close pref, plus a SignOut enterprise policy to control it.
  • FeltLocking module handles encrypt/store, update, and unlock. The token is only ever stored encrypted in the keystore, and is dropped whenever locking is disabled or the user signs out.

Screenshots

felt-locking-unlock-button
FELT login with unlock button
felt-locking-macos-touchid-2
Unlock prompt on MacOS with TouchID
felt-locking-macos-password-2
Unlock prompt on MacOS without TouchID
felt-locking-macos-keychain-permission-prompt
Keychain Permission Request on MacOS
felt-locking-ubuntu-2
Unlock prompt on Ubuntu
felt-locking-windows-pin
Unlock prompt on Windows with pin
felt-locking-windows-password
Unlock prompt on Windows with password
felt-locking-signout-prompt-without-tabs
Signout prompt with locking
felt-locking-signout-prompt-with-tabs
Signout prompt with locking and tabs

Testing

  • Added tests
  • Manual testing performed

Steps to verify changes:

  1. Set enterprise.session.locking.enabled and enterprise.session.locking.on_close to true.
  2. Sign in then close the browser.
  3. Relaunch, you should get an OS auth prompt and resume the session without redoing SSO.

Expected result:

Session resumes after OS auth. With locking off, the browser signs out on close as before.

@jporter-dev jporter-dev self-assigned this Aug 19, 2026
@jporter-dev jporter-dev added enhancement New feature or request branch:main PR that should be merged on enterprise-main branch labels Aug 19, 2026
@jporter-dev
jporter-dev force-pushed the persist-session-on-browser-close branch from fffb2e3 to 45978cd Compare August 19, 2026 19:29
@jporter-dev jporter-dev changed the title Bug 2064395 - Lock the FELT session on browser close to persist it behind OS auth Bug 2064395 - Lock the FELT session on browser close behind OS auth Aug 20, 2026
@jporter-dev
jporter-dev force-pushed the persist-session-on-browser-close branch 5 times, most recently from db9b5da to 9cd67f2 Compare August 22, 2026 00:17
@jporter-dev
jporter-dev marked this pull request as ready for review August 24, 2026 13:20
@jporter-dev
jporter-dev requested review from a team and fiji-flo August 24, 2026 13:21
@lissyx

lissyx commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@jporter-dev When does the prompt you shared in screenshots gets displayed? What is the user input flow to it?

* @returns {Promise<void>}
*/
async function storeToken(email, token) {
const encryptedRefreshToken = await lazy.OSKeyStore.encrypt(token);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume OSKeyStore are the calls triggering the ask to unlock key store that are presented in screenshot. Can we make sure there's an explicit message shown in felt when we know we will ask for the key store credentials ? I believe in term of UX security it's not a good practice that we accustom people to accept "random" request to unlock system key store on our behalf, without a good explanation before.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think many applications show a "Unlock with TouchID" button or indicator. May be worth exploring the UX on this a bit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From only looking at the code, it's not clear what precisely is the flow here. Will I input my email, hiyt "login" and be presented with the OS-level prompt asking me for my password?

For an uneducated folk, how can this be distinguished from trying to steal their information ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an "Unlock" button to FELT when it's meant to unlock instead of go to SSO signin. Pressing "Unlock" will prompt with the OS auth.

felt-locking-unlock-button

@jporter-dev

Copy link
Copy Markdown
Contributor Author

@jporter-dev When does the prompt you shared in screenshots gets displayed? What is the user input flow to it?

When a user attempts to sign-in using an email that has a stored token. I'll add more detail and a recording of the flow to the PR.

Comment thread browser/app/profile/firefox.js Outdated
Comment thread browser/components/enterprisepolicies/Policies.sys.mjs
Comment thread browser/components/enterprisepolicies/Policies.sys.mjs Outdated
Comment thread testing/enterprise/test_felt_browser_lock_on_close.py Outdated
Comment thread browser/components/enterprise/EnterpriseHandler.sys.mjs Outdated
Comment thread toolkit/components/enterprise/modules/EnterpriseHandler.sys.mjs Outdated
Comment thread browser/components/enterprise/EnterpriseHandler.sys.mjs Outdated
Comment thread browser/components/enterprise/EnterpriseHandler.sys.mjs
Comment thread toolkit/components/felt/FeltStorage.sys.mjs Outdated
Comment thread toolkit/components/felt/FeltStorage.sys.mjs Outdated
Comment thread toolkit/components/felt/FeltStorage.sys.mjs Outdated
Comment thread toolkit/components/felt/content/FeltLocking.sys.mjs Outdated
Comment thread toolkit/components/felt/content/FeltLocking.sys.mjs
Comment thread toolkit/components/felt/content/FeltLocking.sys.mjs Outdated
@jporter-dev
jporter-dev force-pushed the persist-session-on-browser-close branch from fd18f42 to b9a0d2c Compare September 3, 2026 15:04
@jporter-dev

jporter-dev commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

@lissyx thanks for all of the feedback! I think I addressed everything, and made some other changes prompted by the posture rework and the signout on close handling.

I ended up reworking this quite a bit, I think it's a bit simpler now - instead of a custom "lockAndClose" event, the lock intent is set based on the pref and is passed with the exit. This allows the browser to go through the regular shutdown flow (same way we do for "signoutAndClose"), and keeps it compatible with our existing prompt and close logic and makes the "lockOnRestart" and "lockOnCrash" paths easier to add.

I also updated the FELT UX when unlocking to make it more explicit that it'll be unlocking, updated the OS auth messaging to be OS-dependent, and updated unlocking to support posture collection.

@jporter-dev
jporter-dev requested a review from lissyx September 3, 2026 15:26
@jporter-dev
jporter-dev force-pushed the persist-session-on-browser-close branch from b9a0d2c to 5f93a61 Compare September 3, 2026 15:55
Comment thread browser/components/enterprise/EnterpriseHandler.sys.mjs Outdated
Comment on lines +4167 to +4202
"SignOut": {
"type": "object",
"x-category": "Authentication",
"x-compatibility": {
"firefox": {
"version_added": false
},
"firefox_esr": {
"version_added": false
},
"firefox_enterprise": {
"version_added": "156"
}
},
"x-restart-required": false,
"description": "Control whether the managed session is signed out or locked when the browser closes.",
"examples": [
{
"BrowserClose": {
"Action": "lock"
}
}
],
"properties": {
"BrowserClose": {
"type": "object",
"properties": {
"Action": {
"type": "string",
"enum": ["signout", "lock"]
}
},
"required": ["Action"]
}
}
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make either a port of this for Thunderbird if it is a policy that should be handled there, or update allowed differences of https://searchfox.org/comm-central/source/mail/components/enterprisepolicies/tests/browser/browser_policies_differences.js

Or at least ping me when you land so I can adapt it

Also update/adapt https://docs.google.com/spreadsheets/d/1h-UmrLEBVmLVEl9eIVQlNkrP-NkgGXfxpzN5tJWCWv4/edit?pli=1&gid=1904079787#gid=1904079787 ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least file a bug blocking thunderbird-client-poc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the TB version of this: https://bugzilla.mozilla.org/show_bug.cgi?id=2060195

Comment on lines +3715 to +3734
SignOut: {
onBeforeAddons(manager, param) {
if (param.BrowserClose) {
lazy.PoliciesUtils.setAndLockPref(
"enterprise.locking.browser_close",
param.BrowserClose.Action === "lock"
);
}
},
onRemove(manager, oldParams) {
if (oldParams.BrowserClose) {
lazy.PoliciesUtils.unsetAndUnlockPref(
"enterprise.locking.browser_close"
);
// unsetAndUnlockPref restores the build default but never re-locks;
// re-lock to match the locked default the enterprise build ships.
Services.prefs.lockPref("enterprise.locking.browser_close");
}
},
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment regarding Thunderbird

Comment thread browser/components/BrowserComponents.manifest
Comment thread toolkit/components/felt/content/FeltLocking.sys.mjs Outdated
Comment thread toolkit/components/felt/content/FeltProcessParent.sys.mjs Outdated
Comment thread toolkit/components/felt/content/FeltProcessParent.sys.mjs Outdated
Comment on lines +865 to +881
/**
* Collect device posture for the given user's profile, for a session about to
* launch. Exposed for the unlock flow (FeltLocking.tryUnlock), which runs in
* this process and submits the posture with its resuming refresh, so posture
* collection stays owned here rather than duplicated in FeltLocking.
*
* @param {string} userId
* @returns {Promise<{posture: DevicePosture, measuredAt: number}>}
*/
async collectLaunchPosture(userId) {
const { path: profileDir } = await lazy.resolveManagedProfile({
id: userId,
});
const measuredAt = Date.now();
const posture = await lazy.DevicePosture.collect({ profileDir });
return { posture, measuredAt };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more like the block here, for collecting fresh posture: https://searchfox.org/enterprise-main/source/toolkit/components/felt/content/FeltProcessParent.sys.mjs#1104-1117

I moved this logic directly into FeltLocking instead of defining this function here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:main PR that should be merged on enterprise-main branch enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants