From 0603bec9f69eb9823f83fabb40115e78c55741e7 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sat, 17 Jan 2026 11:08:24 +0200 Subject: [PATCH 1/3] Add support for Related Origin Requests with passkeys --- dist/manifest_chromium.json | 2 +- dist/manifest_firefox.json | 2 +- keepassxc-browser/background/keepass.js | 76 +++++++++++++++++++++---- keepassxc-browser/manifest.json | 2 +- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/dist/manifest_chromium.json b/dist/manifest_chromium.json index aa49167e..0a337eef 100755 --- a/dist/manifest_chromium.json +++ b/dist/manifest_chromium.json @@ -3,7 +3,7 @@ "name": "KeePassXC-Browser", "version": "1.9.11", "version_name": "1.9.11", - "minimum_chrome_version": "93", + "minimum_chrome_version": "124", "description": "__MSG_extensionDescription__", "author": "KeePassXC Team", "icons": { diff --git a/dist/manifest_firefox.json b/dist/manifest_firefox.json index 2153e089..dd87504e 100644 --- a/dist/manifest_firefox.json +++ b/dist/manifest_firefox.json @@ -185,7 +185,7 @@ "applications": { "gecko": { "id": "keepassxc-browser@keepassxc.org", - "strict_min_version": "96.0" + "strict_min_version": "100.0" } }, "storage": { diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 31b7da80..1c6ce9ce 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -10,20 +10,23 @@ keepass.featuresList = { passkeysDefaultGroup: false, requiredKeePassXCVersionFound: false, }; -keepass.keyPair = { publicKey: null, secretKey: null }; -keepass.serverPublicKey = ''; +keepass.cacheTimeout = 30 * 1000; // Milliseconds keepass.clientID = ''; +keepass.currentKeePassXC = ''; +keepass.databaseHash = ''; keepass.isConnected = false; keepass.isDatabaseClosed = true; -keepass.isKeePassXCAvailable = false; keepass.isEncryptionKeyUnrecognized = false; -keepass.currentKeePassXC = ''; -keepass.requiredKeePassXC = '2.6.0'; +keepass.isKeePassXCAvailable = false; +keepass.keyPair = { publicKey: null, secretKey: null }; keepass.latestVersionUrl = 'https://api.github.com/repos/keepassxreboot/keepassxc/releases/latest'; -keepass.cacheTimeout = 30 * 1000; // Milliseconds -keepass.databaseHash = ''; keepass.previousDatabaseHash = ''; keepass.reconnectLoop = null; +keepass.requiredKeePassXC = '2.6.0'; +keepass.serverPublicKey = ''; + +const DEFAULT_FETCH_TIMEOUT = 5000; // ms +const MAX_RELATED_ORIGIN_LABELS = 15; const kpActions = { SET_LOGIN: 'set-login', @@ -341,7 +344,13 @@ keepass.getDatabaseHash = async function(tab, args = []) { } try { - const request = keepassClient.buildRequest(kpAction, keepassClient.encrypt(messageData, nonce), nonce, keepass.clientID, triggerUnlock); + const request = keepassClient.buildRequest( + kpAction, + keepassClient.encrypt(messageData, nonce), + nonce, + keepass.clientID, + triggerUnlock, + ); const response = await keepassClient.sendNativeMessage(request, enableTimeout); if (response.message && response.nonce) { const res = keepassClient.decrypt(response.message, response.nonce); @@ -616,11 +625,14 @@ keepass.passkeysRegister = async function(tab, args = []) { const kpAction = kpActions.PASSKEYS_REGISTER; const nonce = keepassClient.getNonce(); const [ publicKey, origin ] = args; + const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey)); + const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id); const messageData = { action: kpAction, publicKey: JSON.parse(JSON.stringify(publicKey)), origin: origin, + relatedOrigins: relatedOrigins, groupName: page?.settings?.defaultPasskeyGroup, keys: keepass.getCryptoKeys() }; @@ -810,7 +822,9 @@ keepass.disableAutomaticReconnect = function() { keepass.reconnect = async function(tab = null, connectionTimeout = 1500) { keepassClient.connectToNative(); keepass.generateNewKeyPair(); - const keyChangeResult = await keepass.changePublicKeys(tab, !!connectionTimeout, connectionTimeout).catch(() => false); + const keyChangeResult = await keepass + .changePublicKeys(tab, !!connectionTimeout, connectionTimeout) + .catch(() => false); // Change public keys timeout if (!keyChangeResult) { @@ -866,7 +880,9 @@ keepass.setcurrentKeePassXCVersion = function(version) { keepass.keePassXCUpdateAvailable = async function() { const checkUpdate = Number(page.settings.checkUpdateKeePassXC); if (checkUpdate !== CHECK_UPDATE_NEVER) { - const lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date(1986, 11, 21); + const lastChecked = keepass.latestKeePassXC.lastChecked + ? new Date(keepass.latestKeePassXC.lastChecked) + : new Date(1986, 11, 21); const daysSinceLastCheck = Math.floor(((new Date()).getTime() - lastChecked.getTime()) / 86400000); if (daysSinceLastCheck >= checkUpdate) { await keepass.checkForNewKeePassXCVersion(); @@ -882,7 +898,7 @@ keepass.checkForNewKeePassXCVersion = async function() { let version = -1; try { - const response = await fetch(keepass.latestVersionUrl); + const response = await fetch(keepass.latestVersionUrl, { signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT) }); const jsonData = await response.json(); if (jsonData?.tag_name && jsonData?.prerelease === false) { version = jsonData.tag_name; @@ -894,6 +910,44 @@ keepass.checkForNewKeePassXCVersion = async function() { keepass.latestKeePassXC.lastChecked = new Date().valueOf(); }; +// Implements retrieval of Related Origin Requests for passkeys +// https://www.w3.org/TR/webauthn-3/#sctn-related-origins +keepass.getPasskeysRelatedOrigins = async function(rpId) { + if (!rpId) { + return []; + } + + try { + const response = await fetch(`https://${rpId}/.well-known/webauthn`, { + signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT), + }); + + // Basic reply validation, see: https://www.w3.org/TR/webauthn-3/#sctn-validating-relation-origin + const isJson = response?.headers?.get('content-type')?.includes('application/json'); + if (!isJson) { + logError('getRelatedOrigins error: Content-Type is not JSON'); + return []; + } + + const jsonData = await response.json(); + if (!Array.isArray(jsonData?.origins) + || jsonData?.origins?.length === 0 + || jsonData?.origins?.length > MAX_RELATED_ORIGIN_LABELS + || !jsonData?.origins?.every((origin) => typeof origin === 'string')) { + logError( + `getRelatedOrigins error: origins is not a list of strings, or it exceeds the maximum count of ${MAX_RELATED_ORIGIN_LABELS}`, + ); + return []; + } + + return jsonData.origins; + } catch (ex) { + logError(`getRelatedOrigins error: ${ex}`); + } + + return []; +} + keepass.clearErrorMessage = function(tab) { if (tab && page.tabs[tab.id]) { page.tabs[tab.id].errorMessage = undefined; diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index aa49167e..0a337eef 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -3,7 +3,7 @@ "name": "KeePassXC-Browser", "version": "1.9.11", "version_name": "1.9.11", - "minimum_chrome_version": "93", + "minimum_chrome_version": "124", "description": "__MSG_extensionDescription__", "author": "KeePassXC Team", "icons": { From 6c0700c5626b4bb59eb16043488d3ed90f1e9d5f Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sat, 17 Jan 2026 12:57:48 +0200 Subject: [PATCH 2/3] Increase maximum labels value to 60. Fix some eslint errors. --- keepassxc-browser/background/keepass.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 1c6ce9ce..e1021676 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -26,7 +26,7 @@ keepass.requiredKeePassXC = '2.6.0'; keepass.serverPublicKey = ''; const DEFAULT_FETCH_TIMEOUT = 5000; // ms -const MAX_RELATED_ORIGIN_LABELS = 15; +const MAX_RELATED_ORIGIN_LABELS = 60; const kpActions = { SET_LOGIN: 'set-login', @@ -946,7 +946,7 @@ keepass.getPasskeysRelatedOrigins = async function(rpId) { } return []; -} +}; keepass.clearErrorMessage = function(tab) { if (tab && page.tabs[tab.id]) { From 5e876f0e736a010ceca9a72035d0ab2dcb430656 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sun, 18 Jan 2026 09:55:08 +0200 Subject: [PATCH 3/3] Add related origins list to authentication --- keepassxc-browser/background/keepass.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index e1021676..72a5cad8 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -630,7 +630,7 @@ keepass.passkeysRegister = async function(tab, args = []) { const messageData = { action: kpAction, - publicKey: JSON.parse(JSON.stringify(publicKey)), + publicKey: passkeyPublicKey, origin: origin, relatedOrigins: relatedOrigins, groupName: page?.settings?.defaultPasskeyGroup, @@ -660,13 +660,15 @@ keepass.passkeysGet = async function(tab, args = []) { const kpAction = kpActions.PASSKEYS_GET; const nonce = keepassClient.getNonce(); - const publicKey = args[0]; - const origin = args[1]; + const [ publicKey, origin ] = args; + const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey)); + const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id); const messageData = { action: kpAction, - publicKey: JSON.parse(JSON.stringify(publicKey)), + publicKey: passkeyPublicKey, origin: origin, + relatedOrigins: relatedOrigins, keys: keepass.getCryptoKeys() };