favalib 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/TwoFaLib.d.mts
CHANGED
|
@@ -72,8 +72,9 @@ declare class TwoFaLib extends TypedEventTarget<TwoFaLibEventMapEvents> {
|
|
|
72
72
|
/**
|
|
73
73
|
* Sets a server url, this will allow syncing with the server.
|
|
74
74
|
* @param serverUrl - The server url.
|
|
75
|
+
* @param force - Force setting the sync server url, even if no connection can be made
|
|
75
76
|
*/
|
|
76
|
-
setSyncServerUrl(serverUrl: string): Promise<void>;
|
|
77
|
+
setSyncServerUrl(serverUrl: string, force?: boolean): Promise<void>;
|
|
77
78
|
/**
|
|
78
79
|
* Sets the sync state, this will initiate the sync manager instance.
|
|
79
80
|
* @param syncState - The state of the sync.
|
package/build/TwoFaLib.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TypedEventTarget } from 'typescript-event-target';
|
|
2
2
|
import TwoFaLibMediator from './TwoFaLibMediator.mjs';
|
|
3
3
|
import { TwoFaLibEvent } from './TwoFaLibEvent.mjs';
|
|
4
|
-
import { InitializationError } from './TwoFALibError.mjs';
|
|
5
|
-
import SyncManager from './subclasses/SyncManager.mjs';
|
|
4
|
+
import { InitializationError, SyncError } from './TwoFALibError.mjs';
|
|
5
|
+
import SyncManager, { ConnectionStatus } from './subclasses/SyncManager.mjs';
|
|
6
6
|
import LibraryLoader from './subclasses/LibraryLoader.mjs';
|
|
7
7
|
import ExportImportManager from './subclasses/ExportImportManager.mjs';
|
|
8
8
|
import PersistentStorageManager from './subclasses/PersistentStorageManager.mjs';
|
|
@@ -139,18 +139,45 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
139
139
|
/**
|
|
140
140
|
* Sets a server url, this will allow syncing with the server.
|
|
141
141
|
* @param serverUrl - The server url.
|
|
142
|
+
* @param force - Force setting the sync server url, even if no connection can be made
|
|
142
143
|
*/
|
|
143
|
-
async setSyncServerUrl(serverUrl) {
|
|
144
|
+
async setSyncServerUrl(serverUrl, force = false) {
|
|
144
145
|
if (this.sync) {
|
|
146
|
+
// close connection so no data is send to the old syncServer
|
|
145
147
|
this.sync.closeServerConnection();
|
|
146
|
-
this.mediator.unRegisterComponent('syncManager');
|
|
147
148
|
}
|
|
148
149
|
const newSyncState = {
|
|
149
150
|
serverUrl,
|
|
150
151
|
devices: [],
|
|
151
152
|
commandSendQueue: [],
|
|
152
153
|
};
|
|
153
|
-
this.
|
|
154
|
+
const newSyncManager = new SyncManager(this.mediator, this.deviceType, this.publicKey, this.privateKey, newSyncState, this.deviceId);
|
|
155
|
+
const success = await new Promise((resolve) => {
|
|
156
|
+
this.addEventListener(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, (event) => {
|
|
157
|
+
if (event.detail.newStatus === ConnectionStatus.CONNECTED) {
|
|
158
|
+
resolve(true);
|
|
159
|
+
}
|
|
160
|
+
if (event.detail.newStatus === ConnectionStatus.FAILED) {
|
|
161
|
+
resolve(false);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
if (!success) {
|
|
166
|
+
if (force) {
|
|
167
|
+
this.log('warning', `Failed to connect to server at ${serverUrl}, force setting`);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
if (this.sync) {
|
|
171
|
+
// re-establish old connection
|
|
172
|
+
this.sync.initServerConnection();
|
|
173
|
+
}
|
|
174
|
+
newSyncManager.closeServerConnection();
|
|
175
|
+
throw new SyncError(`Failed to connect to server at ${serverUrl}, not setting`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// connection succeeded (or force=true), switch to the new syncManager
|
|
179
|
+
this.mediator.unRegisterComponent('syncManager');
|
|
180
|
+
this.mediator.registerComponent('syncManager', newSyncManager);
|
|
154
181
|
await this.forceSave();
|
|
155
182
|
}
|
|
156
183
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EmptyObject } from 'type-fest';
|
|
2
2
|
import type { TwoFaLibEvent } from '../TwoFaLibEvent.mjs';
|
|
3
3
|
import type { LockedRepresentationString } from './Vault.mjs';
|
|
4
|
+
import type { ConnectionStatus } from '../subclasses/SyncManager.mjs';
|
|
4
5
|
export interface ChangedEvent {
|
|
5
6
|
newLockedRepresentationString: LockedRepresentationString;
|
|
6
7
|
}
|
|
@@ -9,7 +10,7 @@ export interface TwoFaLibEventMap {
|
|
|
9
10
|
[TwoFaLibEvent.LoadedFromLockedRepresentation]: EmptyObject;
|
|
10
11
|
[TwoFaLibEvent.ConnectToExistingVaultFinished]: EmptyObject;
|
|
11
12
|
[TwoFaLibEvent.ConnectionToSyncServerStatusChanged]: {
|
|
12
|
-
|
|
13
|
+
newStatus: ConnectionStatus;
|
|
13
14
|
};
|
|
14
15
|
[TwoFaLibEvent.Log]: {
|
|
15
16
|
severity: 'info' | 'warning';
|
|
@@ -6,6 +6,12 @@ import { VaultSyncStateWithServerUrl } from '../interfaces/Vault.mjs';
|
|
|
6
6
|
import { SyncCommandFromServer } from 'favaserver/ServerMessage';
|
|
7
7
|
import { SyncCommandFromClient } from 'favaserver/ClientMessage';
|
|
8
8
|
import type { AddSyncDeviceData } from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
9
|
+
export declare enum ConnectionStatus {
|
|
10
|
+
CONNECTING = 0,
|
|
11
|
+
CONNECTED = 1,
|
|
12
|
+
NOT_CONNECTED = 2,
|
|
13
|
+
FAILED = 3
|
|
14
|
+
}
|
|
9
15
|
/**
|
|
10
16
|
* Manages synchronization of 2FA devices and communication with the server.
|
|
11
17
|
*/
|
|
@@ -23,6 +29,7 @@ declare class SyncManager {
|
|
|
23
29
|
private readyEventEmitted;
|
|
24
30
|
private commandSendQueue;
|
|
25
31
|
private reconnectTimeout?;
|
|
32
|
+
private terminateTimeout?;
|
|
26
33
|
private shouldReconnect;
|
|
27
34
|
/**
|
|
28
35
|
* Public getter for the command send queue.
|
|
@@ -7,6 +7,13 @@ import { InitializationError, SyncAddDeviceFlowConflictError, SyncError, SyncInW
|
|
|
7
7
|
import AddSyncDeviceCommand from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
8
8
|
const IN_TESTING = process.env.NODE_ENV === 'test';
|
|
9
9
|
const IN_DEV = process.env.NODE_ENV === 'development';
|
|
10
|
+
export var ConnectionStatus;
|
|
11
|
+
(function (ConnectionStatus) {
|
|
12
|
+
ConnectionStatus[ConnectionStatus["CONNECTING"] = 0] = "CONNECTING";
|
|
13
|
+
ConnectionStatus[ConnectionStatus["CONNECTED"] = 1] = "CONNECTED";
|
|
14
|
+
ConnectionStatus[ConnectionStatus["NOT_CONNECTED"] = 2] = "NOT_CONNECTED";
|
|
15
|
+
ConnectionStatus[ConnectionStatus["FAILED"] = 3] = "FAILED";
|
|
16
|
+
})(ConnectionStatus || (ConnectionStatus = {}));
|
|
10
17
|
const generateNonCryptographicRandomString = () => {
|
|
11
18
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
12
19
|
const length = Math.floor(Math.random() * 64) + 1;
|
|
@@ -53,6 +60,16 @@ class SyncManager {
|
|
|
53
60
|
this.commandSendQueue = commandSendQueue;
|
|
54
61
|
this.serverUrl = serverUrl;
|
|
55
62
|
this.initServerConnection();
|
|
63
|
+
// if not yet connected after 2 tries, emit ready event so we can continue
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
if (!this.readyEventEmitted && !this.webSocketConnected) {
|
|
66
|
+
this.log('warning', 'Failed to connect to sync backend');
|
|
67
|
+
this.dispatchLibEvent(TwoFaLibEvent.Ready);
|
|
68
|
+
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
69
|
+
newStatus: ConnectionStatus.FAILED,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}, this.reconnectInterval + 1000);
|
|
56
73
|
}
|
|
57
74
|
get libraryLoader() {
|
|
58
75
|
return this.mediator.getComponent('libraryLoader');
|
|
@@ -123,7 +140,7 @@ class SyncManager {
|
|
|
123
140
|
this.log('info', 'Connected to server.');
|
|
124
141
|
this.sendToServer('connect', { deviceId: syncManager.deviceId });
|
|
125
142
|
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
126
|
-
|
|
143
|
+
newStatus: ConnectionStatus.CONNECTED,
|
|
127
144
|
});
|
|
128
145
|
// send any commands that were done while offline
|
|
129
146
|
void this.processCommandSendQueue();
|
|
@@ -132,14 +149,23 @@ class SyncManager {
|
|
|
132
149
|
this.ws = ws;
|
|
133
150
|
}
|
|
134
151
|
handleWebSocketClose(event) {
|
|
135
|
-
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
136
|
-
connected: false,
|
|
137
|
-
});
|
|
138
152
|
if (this.shouldReconnect) {
|
|
153
|
+
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
154
|
+
newStatus: ConnectionStatus.CONNECTING,
|
|
155
|
+
});
|
|
139
156
|
// if we shouldn't reconnect, this closing is expected
|
|
140
157
|
this.log('warning', `WebSocket closed: ${event.code} ${event.reason}`);
|
|
141
158
|
this.attemptReconnect();
|
|
142
159
|
}
|
|
160
|
+
else {
|
|
161
|
+
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
162
|
+
newStatus: ConnectionStatus.NOT_CONNECTED,
|
|
163
|
+
});
|
|
164
|
+
// Connection closed, no need to force terminate
|
|
165
|
+
if (this.terminateTimeout) {
|
|
166
|
+
clearTimeout(this.terminateTimeout);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
143
169
|
}
|
|
144
170
|
handleServerMessage(message) {
|
|
145
171
|
switch (message.type) {
|
|
@@ -558,9 +584,9 @@ class SyncManager {
|
|
|
558
584
|
const ws = this.ws;
|
|
559
585
|
this.ws = undefined;
|
|
560
586
|
ws.close();
|
|
561
|
-
// force terminate the connection after
|
|
587
|
+
// force terminate the connection after 2 seconds
|
|
562
588
|
// ws.terminate is not defined in the test enviroment, which is the reason for the &&
|
|
563
|
-
setTimeout(() => ws.terminate && ws.terminate(),
|
|
589
|
+
this.terminateTimeout = setTimeout(() => ws.terminate && ws.terminate(), 2000);
|
|
564
590
|
}
|
|
565
591
|
}
|
|
566
592
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "favalib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -52,5 +52,8 @@
|
|
|
52
52
|
"vitest-websocket-mock": "^0.4.0",
|
|
53
53
|
"whatwg-url": "^14.0.0",
|
|
54
54
|
"ws": "^8.18.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20"
|
|
55
58
|
}
|
|
56
59
|
}
|