Skip to content

Commit 744e3c1

Browse files
committed
Add support for WebSocket connection method
1 parent b46c1a1 commit 744e3c1

6 files changed

Lines changed: 98 additions & 5 deletions

File tree

keepassxc-browser/_locales/en/messages.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,22 @@
874874
"message": "Allow filling HTTP Basic Auth credentials",
875875
"description": "Allow filling HTTP Basic Auth credentials checkbox text."
876876
},
877+
"optionsConnectionMethod": {
878+
"message": "Connection method",
879+
"description": "Connection method selection text."
880+
},
881+
"optionsConnectionMethodHelpText": {
882+
"message": "If native messaging is blocked in your browser, you can switch to a direct WebSocket sonnection. The feature must be enabled from KeePassXC side to work. Browser restart is required.",
883+
"description": "Connection method help text."
884+
},
885+
"optionsConnectionMethodNativeMessaging": {
886+
"message": "Native messaging",
887+
"description": "Native messaging option for Connection method."
888+
},
889+
"optionsConnectionMethodWebSocket": {
890+
"message": "WebSocket",
891+
"description": "WebSocket option for Connection method."
892+
},
877893
"optionsDebugLogging": {
878894
"message": "Debug logging",
879895
"description": "Debug logging checkbox text."

keepassxc-browser/background/client.js

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ keepassClient.keySize = 24;
55
keepassClient.messageTimeout = 500; // Milliseconds
66
keepassClient.nativeHostName = 'org.keepassxc.keepassxc_browser';
77
keepassClient.nativePort = null;
8+
keepassClient.webSocket = null;
89

910
const kpErrors = {
1011
UNKNOWN_ERROR: 0,
1112
DATABASE_NOT_OPENED: 1,
12-
DATABASE_HASH_NOT_RECEIVED: 2,
13+
DATABASE_HASH_NOT_RECEIVED: 2,
1314
CLIENT_PUBLIC_KEY_NOT_RECEIVED: 3,
1415
CANNOT_DECRYPT_MESSAGE: 4,
1516
TIMEOUT_OR_NOT_CONNECTED: 5,
@@ -157,7 +158,10 @@ class Message {
157158
//--------------------------------------------------------------------------
158159

159160
keepassClient.sendNativeMessage = async function(request, enableTimeout = false, timeoutValue) {
160-
if (!keepassClient.nativePort) {
161+
if (page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET && !keepassClient.webSocket) {
162+
logError('No WebSocket defined.');
163+
return;
164+
} else if (page?.settings?.connectionMethod === ConnectionMethod.NATIVE_MESSAGING && !keepassClient.nativePort) {
161165
logError('No native messaging port defined.');
162166
return;
163167
}
@@ -167,7 +171,11 @@ keepassClient.sendNativeMessage = async function(request, enableTimeout = false,
167171
messageBuffer.addMessage(message);
168172
});
169173

170-
keepassClient.nativePort.postMessage(request);
174+
if (page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET) {
175+
keepassClient.webSocket.send(JSON.stringify(request));
176+
} else {
177+
keepassClient.nativePort.postMessage(request);
178+
}
171179

172180
const response = await message.promise;
173181

@@ -400,7 +408,7 @@ function onDisconnected() {
400408
page.clearAllLogins();
401409
keepass.updatePopup('cross');
402410
keepass.updateDatabaseHashToContent();
403-
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)}`);
411+
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError?.message)}`);
404412
}
405413

406414
keepassClient.onNativeMessage = function(response) {
@@ -413,3 +421,42 @@ keepassClient.onNativeMessage = function(response) {
413421
// Generic response handling
414422
keepassClient.handleNativeMessage(response);
415423
};
424+
425+
//--------------------------------------------------------------------------
426+
// WebSocket related
427+
//--------------------------------------------------------------------------
428+
429+
keepassClient.connectToWebSocket = async function() {
430+
return new Promise((resolve, reject) => {
431+
if (keepassClient.webSocket) {
432+
keepassClient.webSocket.close();
433+
}
434+
435+
console.log(`${EXTENSION_NAME}: Connecting to WebSocket`);
436+
437+
try {
438+
keepassClient.webSocket = new WebSocket('ws://localhost:7580');
439+
keepassClient.webSocket.addEventListener('close', (event) => {
440+
logError('Close WebSocket:', event);
441+
onDisconnected();
442+
reject();
443+
});
444+
keepassClient.webSocket.addEventListener('error', (event) => {
445+
logError('WebSocket error:', event);
446+
onDisconnected();
447+
reject();
448+
});
449+
keepassClient.webSocket.addEventListener('message', (event) => {
450+
keepassClient.onNativeMessage(JSON.parse(event?.data));
451+
});
452+
keepassClient.webSocket.addEventListener('open', (event) => {
453+
console.log(`${EXTENSION_NAME}: WebSocket connected`);
454+
keepass.isConnected = true;
455+
resolve();
456+
});
457+
} catch (e) {
458+
keepassClient.webSocket = null;
459+
onDisconnected();
460+
}
461+
});
462+
};

keepassxc-browser/background/keepass.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,12 @@ keepass.disableAutomaticReconnect = function() {
805805
};
806806

807807
keepass.reconnect = async function(tab = null, connectionTimeout = 1500) {
808-
keepassClient.connectToNative();
808+
if (page?.settings?.connectionMethod === ConnectionMethod.WEBSOCKET) {
809+
await keepassClient.connectToWebSocket();
810+
} else {
811+
keepassClient.connectToNative();
812+
}
813+
809814
keepass.generateNewKeyPair();
810815
const keyChangeResult = await keepass.changePublicKeys(tab, !!connectionTimeout, connectionTimeout).catch(() => false);
811816

keepassxc-browser/background/page.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
'use strict';
22

3+
const ConnectionMethod = {
4+
NATIVE_MESSAGING: 'nativemessaging',
5+
WEBSOCKET: 'websocket',
6+
};
7+
38
const defaultSettings = {
49
afterFillSorting: SORT_BY_MATCHING_CREDENTIALS_SETTING,
510
afterFillSortingTotp: SORT_BY_RELEVANT_ENTRY,
@@ -15,6 +20,7 @@ const defaultSettings = {
1520
checkUpdateKeePassXC: CHECK_UPDATE_NEVER,
1621
clearCredentialsTimeout: 10,
1722
colorTheme: 'system',
23+
connectionMethod: ConnectionMethod.NATIVE_MESSAGING,
1824
credentialSorting: SORT_BY_GROUP_AND_TITLE,
1925
debugLogging: false,
2026
defaultGroup: '',

keepassxc-browser/options/options.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,18 @@ <h2 class="pb-3 mt-0" data-i18n="optionsGeneralSettingsTab"></h2>
531531
<div class="form-text" data-i18n="optionsClearCredentialsTimeoutHelpText"></div>
532532
</div>
533533

534+
<!-- Connection method -->
535+
<div class="form-group col-sm-3 pb-2 py-2">
536+
<label for="connectionMethod" class="form-label" data-i18n="optionsConnectionMethod"></label>
537+
<select class="form-select form-select-sm col-md-3 col-lg-2" id="connectionMethod" data-i18n="[title]optionsConnectionMethodSelection">
538+
<option value="nativemessaging" data-i18n="optionsConnectionMethodNativeMessaging"></option>
539+
<option value="websocket" data-i18n="optionsConnectionMethodWebSocket"></option>
540+
</select>
541+
</div>
542+
<div>
543+
<span class="form-text text-muted" data-i18n="optionsConnectionMethodHelpText"></span>
544+
</div>
545+
534546
<!-- Debug logging -->
535547
<div class="form-group mt-2 pb-1">
536548
<div class="form-check form-switch">

keepassxc-browser/options/options.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ options.initGeneralSettings = async function() {
226226
});
227227
});
228228

229+
// Connection method
230+
$('#tab-general-settings select#connectionMethod').value = options.settings['connectionMethod'];
231+
$('#tab-general-settings select#connectionMethod').addEventListener('change', async function(e) {
232+
options.settings['connectionMethod'] = e.currentTarget.value;
233+
await options.saveSettings();
234+
});
235+
229236
// Default group
230237
$('#defaultGroupButton').addEventListener('click', async function() {
231238
const value = $('#defaultGroup').value;

0 commit comments

Comments
 (0)