From 1786ff8a2dd85549f8996735d76e21703f413c9a Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Fri, 17 Jan 2025 17:42:12 +0100 Subject: [PATCH 1/5] fix: dApp account disabled --- .../src/clients/beacon-client/BeaconClient.ts | 2 +- .../transports/clients/CommunicationClient.ts | 2 +- .../beacon-dapp/src/dapp-client/DAppClient.ts | 74 +++++++++++++------ .../P2PCommunicationClient.ts | 25 ++++++- 4 files changed, 74 insertions(+), 29 deletions(-) diff --git a/packages/beacon-core/src/clients/beacon-client/BeaconClient.ts b/packages/beacon-core/src/clients/beacon-client/BeaconClient.ts index 8e79be01a..b8c5791b3 100644 --- a/packages/beacon-core/src/clients/beacon-client/BeaconClient.ts +++ b/packages/beacon-core/src/clients/beacon-client/BeaconClient.ts @@ -85,7 +85,7 @@ export abstract class BeaconClient { /** * This method initializes the SDK by setting some values in the storage and generating a keypair. */ - private async initSDK(): Promise { + protected async initSDK(): Promise { this.storage.set(StorageKey.BEACON_SDK_VERSION, SDK_VERSION).catch(console.error) this.loadOrCreateBeaconSecret().catch(console.error) diff --git a/packages/beacon-core/src/transports/clients/CommunicationClient.ts b/packages/beacon-core/src/transports/clients/CommunicationClient.ts index 740db2ad4..2bfe8a122 100644 --- a/packages/beacon-core/src/transports/clients/CommunicationClient.ts +++ b/packages/beacon-core/src/transports/clients/CommunicationClient.ts @@ -9,7 +9,7 @@ import { ClientEvents } from './ClientEvents' * */ export abstract class CommunicationClient { - constructor(protected readonly keyPair?: KeyPair) {} + constructor(protected keyPair?: KeyPair) {} public eventHandlers: Map = new Map() diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index e470502f5..6e6f8b106 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -101,7 +101,8 @@ import { signMessage, CONTRACT_PREFIX, prefixPublicKey, - isValidAddress + isValidAddress, + getKeypairFromSeed } from '@airgap/beacon-utils' import { messageEvents } from '../beacon-message-events' import { BlockExplorer } from '../utils/block-explorer' @@ -264,7 +265,9 @@ export class DAppClient extends Client { this.storage.subscribeToStorageChanged(async (event) => { if (event.eventType === 'storageCleared') { this.setActiveAccount(undefined) - } else if (event.eventType === 'entryModified') { + return + } + if (event.eventType === 'entryModified') { if (event.key === this.storage.getPrefixedKey(StorageKey.ACTIVE_ACCOUNT)) { const accountIdentifier = event.newValue if (!accountIdentifier || accountIdentifier === 'undefined') { @@ -273,8 +276,17 @@ export class DAppClient extends Client { const account = await this.getAccount(accountIdentifier) this.setActiveAccount(account) } - } else if (event.key === this.storage.getPrefixedKey(StorageKey.ENABLE_METRICS)) { + return + } + if (event.key === this.storage.getPrefixedKey(StorageKey.ENABLE_METRICS)) { this.enableMetrics = !!(await this.storage.get(StorageKey.ENABLE_METRICS)) + return + } + if (event.key === this.storage.getPrefixedKey(StorageKey.BEACON_SDK_SECRET_SEED)) { + this._keyPair = new ExposedPromise() + this._beaconId = new ExposedPromise() + await this.initSDK() + return } } }) @@ -292,8 +304,9 @@ export class DAppClient extends Client { } }) .catch(async (storageError) => { - await this.setActiveAccount(undefined) + // await this.setActiveAccount(undefined) logger.error(storageError) + await this.resetInvalidState(false) return undefined }) @@ -555,7 +568,11 @@ export class DAppClient extends Client { } public async initInternalTransports(): Promise { - const keyPair = await this.keyPair + const seed = await this.storage.get(StorageKey.BEACON_SDK_SECRET_SEED) + if (!seed) { + throw new Error('Secret seed not found') + } + const keyPair = await getKeypairFromSeed(seed) if (this.postMessageTransport || this.p2pTransport || this.walletConnectTransport) { return @@ -785,33 +802,42 @@ export class DAppClient extends Client { console.error(error) }) + const abortHandler = async () => { + logger.log('init', 'ABORTED') + this.sendMetrics( + 'performance-metrics/save', + await this.buildPayload('connect', 'abort') + ) + await Promise.all([ + postMessageTransport.disconnect(), + // p2pTransport.disconnect(), do not abort connection manually + walletConnectTransport.disconnect() + ]) + this.postMessageTransport = this.walletConnectTransport = this.p2pTransport = undefined + this._activeAccount.isResolved() && this.clearActiveAccount() + this._initPromise = undefined + } + this.events .emit(BeaconEvent.PAIR_INIT, { p2pPeerInfo: () => { - p2pTransport.connect().then().catch(console.error) + p2pTransport + .connect() + .then() + .catch((err) => { + console.warn('yesss...') + console.error(err) + if (err.message === 'The account is deactivated.') { + this.hideUI(['alert']) + abortHandler() + } + }) return p2pTransport.getPairingRequestInfo() }, postmessagePeerInfo: () => postMessageTransport.getPairingRequestInfo(), walletConnectPeerInfo: () => walletConnectTransport.getPairingRequestInfo(), networkType: this.network.type, - abortedHandler: async () => { - logger.log('init', 'ABORTED') - this.sendMetrics( - 'performance-metrics/save', - await this.buildPayload('connect', 'abort') - ) - await Promise.all([ - postMessageTransport.disconnect(), - // p2pTransport.disconnect(), do not abort connection manually - walletConnectTransport.disconnect() - ]) - this.postMessageTransport = - this.walletConnectTransport = - this.p2pTransport = - undefined - this._activeAccount.isResolved() && this.clearActiveAccount() - this._initPromise = undefined - }, + abortedHandler: abortHandler.bind(this), disclaimerText: this.disclaimerText, analytics: this.analytics, featuredWallets: this.featuredWallets diff --git a/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts b/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts index eec92668a..77a9005e9 100644 --- a/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts +++ b/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts @@ -8,7 +8,8 @@ import { encryptCryptoboxPayload, decryptCryptoboxPayload, secretbox_NONCEBYTES, - secretbox_MACBYTES + secretbox_MACBYTES, + getKeypairFromSeed } from '@airgap/beacon-utils' import { MatrixClient } from '../matrix-client/MatrixClient' import { @@ -387,8 +388,15 @@ export class P2PCommunicationClient extends CommunicationClient { password: `ed:${toHex(rawSignature)}:${await this.getPublicKey()}`, deviceId: toHex(this.keyPair!.publicKey) }) - } catch (error) { - logger.error('start', 'Could not log in, retrying') + } catch (error: any) { + logger.error('start', 'Could not log in, retrying', error) + + if (error.errcode === 'M_USER_DEACTIVATED') { + await this.generateNewKeyPair() + await this.reset() + throw new Error('The account is deactivated.') + } + await this.reset() // If we can't log in, let's reset if (!this.selectedRegion) { throw new Error('No region selected.') @@ -426,6 +434,7 @@ export class P2PCommunicationClient extends CommunicationClient { await this.storage.delete(StorageKey.MATRIX_PEER_ROOM_IDS).catch((error) => logger.log(error)) await this.storage.delete(StorageKey.MATRIX_PRESERVED_STATE).catch((error) => logger.log(error)) await this.storage.delete(StorageKey.MATRIX_SELECTED_NODE).catch((error) => logger.log(error)) + // Instead of resetting everything, maybe we should make sure a new instance is created? this.relayServer = undefined this.client = new ExposedPromise() @@ -779,6 +788,16 @@ export class P2PCommunicationClient extends CommunicationClient { ) } + private async generateNewKeyPair() { + const newSeed = await generateGUID() + + console.warn(`The current user ID has been deactivated. Generating new ID: ${newSeed}`) + + this.storage.set(StorageKey.BEACON_SDK_SECRET_SEED, newSeed) + + this.keyPair = await getKeypairFromSeed(newSeed) + } + private async getRelevantRoom(recipient: string): Promise { const roomIds = await this.storage.get(StorageKey.MATRIX_PEER_ROOM_IDS) let roomId = roomIds[recipient] From c048897ff0ce9023f784c0707acde540c63310c1 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 20 Jan 2025 16:30:18 +0100 Subject: [PATCH 2/5] fix: user deactivated on wallet side --- .../beacon-wallet/src/client/WalletClient.ts | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/beacon-wallet/src/client/WalletClient.ts b/packages/beacon-wallet/src/client/WalletClient.ts index b34e9622c..d2299735b 100644 --- a/packages/beacon-wallet/src/client/WalletClient.ts +++ b/packages/beacon-wallet/src/client/WalletClient.ts @@ -237,22 +237,32 @@ export class WalletClient extends Client { */ public async _connect(): Promise { const transport: WalletP2PTransport = (await this.transport) as WalletP2PTransport - if (transport.connectionStatus === TransportStatus.NOT_CONNECTED) { + if (transport.connectionStatus !== TransportStatus.NOT_CONNECTED) { + return + } + + try { await transport.connect() - transport - .addListener(async (message: unknown, connectionInfo: ConnectionContext) => { - if (typeof message === 'string') { - const deserializedMessage = (await new Serializer().deserialize( - message - )) as BeaconRequestMessage - this.handleResponse(deserializedMessage, connectionInfo) - } - }) - .catch((error) => logger.log('_connect', error)) - this._isConnected.resolve(true) - } else { - // NO-OP + } catch (err: any) { + logger.warn('_connect', err.message) + if (err.message === 'The account is deactivated.') { + await transport.disconnect() + await this._connect() + return + } } + + transport + .addListener(async (message: unknown, connectionInfo: ConnectionContext) => { + if (typeof message === 'string') { + const deserializedMessage = (await new Serializer().deserialize( + message + )) as BeaconRequestMessage + this.handleResponse(deserializedMessage, connectionInfo) + } + }) + .catch((error) => logger.log('_connect', error)) + this._isConnected.resolve(true) } /** From 4ee57f1d1f5a76f2c3e2af3ebd7b65d57150f997 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 20 Jan 2025 16:32:19 +0100 Subject: [PATCH 3/5] chore: remove commented out code --- packages/beacon-dapp/src/dapp-client/DAppClient.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 6e6f8b106..1bef3397b 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -304,7 +304,6 @@ export class DAppClient extends Client { } }) .catch(async (storageError) => { - // await this.setActiveAccount(undefined) logger.error(storageError) await this.resetInvalidState(false) return undefined From 598d5c000b5d2236e8c075ef2b25a19ab233d9ee Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Wed, 22 Jan 2025 16:29:22 +0100 Subject: [PATCH 4/5] feat: add UI notification --- .../beacon-dapp/src/dapp-client/DAppClient.ts | 1 + packages/beacon-dapp/src/events.ts | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 1bef3397b..513394ab1 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -306,6 +306,7 @@ export class DAppClient extends Client { .catch(async (storageError) => { logger.error(storageError) await this.resetInvalidState(false) + this.events.emit(BeaconEvent.INVALID_ACCOUNT_DEACTIVATED) return undefined }) diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index a3dafdab2..f8ef74604 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -95,6 +95,7 @@ export enum BeaconEvent { SHOW_PREPARE = 'SHOW_PREPARE', HIDE_UI = 'HIDE_UI', INVALID_ACTIVE_ACCOUNT_STATE = 'INVALID_ACTIVE_ACCOUNT_STATE', + INVALID_ACCOUNT_DEACTIVATED = 'INVALID_ACCOUNT_DEACTIVATED', PAIR_INIT = 'PAIR_INIT', PAIR_SUCCESS = 'PAIR_SUCCESS', CHANNEL_CLOSED = 'CHANNEL_CLOSED', @@ -195,7 +196,8 @@ export interface BeaconEventType { [BeaconEvent.NO_PERMISSIONS]: undefined [BeaconEvent.ACTIVE_ACCOUNT_SET]: AccountInfo [BeaconEvent.ACTIVE_TRANSPORT_SET]: Transport - [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: undefined + [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: undefined + [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: undefined [BeaconEvent.SHOW_PREPARE]: { walletInfo?: WalletInfo } [BeaconEvent.HIDE_UI]: ('alert' | 'toast')[] | undefined [BeaconEvent.PAIR_INIT]: { @@ -314,7 +316,7 @@ const showNoPermissionAlert = async (): Promise => { } /** - * Show a + * Show an "Invalid state" alert */ const showInvalidActiveAccountState = async (): Promise => { await openAlert({ @@ -324,6 +326,16 @@ const showInvalidActiveAccountState = async (): Promise => { }) } +/** + * Show an "account deactivated" error alert + */ +const showInvalidAccountDeactivated = async (): Promise => { + await openAlert({ + title: 'Error', + body: `Your session has expired. Please pair with your wallet again.` + }) +} + /** * Show an error toast * @@ -756,6 +768,7 @@ export const defaultEventCallbacks: { [BeaconEvent.ACTIVE_ACCOUNT_SET]: emptyHandler(), [BeaconEvent.ACTIVE_TRANSPORT_SET]: emptyHandler(), [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: showInvalidActiveAccountState, + [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: showInvalidAccountDeactivated, [BeaconEvent.SHOW_PREPARE]: showPrepare, [BeaconEvent.HIDE_UI]: hideUI, [BeaconEvent.PAIR_INIT]: showPairAlert, @@ -816,6 +829,9 @@ export class BeaconEventHandler { [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: [ defaultEventCallbacks.INVALID_ACTIVE_ACCOUNT_STATE ], + [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: [ + defaultEventCallbacks.INVALID_ACCOUNT_DEACTIVATED + ], [BeaconEvent.SHOW_PREPARE]: [defaultEventCallbacks.SHOW_PREPARE], [BeaconEvent.HIDE_UI]: [defaultEventCallbacks.HIDE_UI], [BeaconEvent.PAIR_INIT]: [defaultEventCallbacks.PAIR_INIT], From 1b97b9bb8d3511bdc1bd6829172b06230e8fd1f6 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Thu, 23 Jan 2025 13:59:53 +0100 Subject: [PATCH 5/5] fix: walletclient add number of attemps --- packages/beacon-dapp/src/dapp-client/DAppClient.ts | 3 +-- packages/beacon-wallet/src/client/WalletClient.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 513394ab1..55cb68089 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -825,8 +825,7 @@ export class DAppClient extends Client { .connect() .then() .catch((err) => { - console.warn('yesss...') - console.error(err) + logger.error(err) if (err.message === 'The account is deactivated.') { this.hideUI(['alert']) abortHandler() diff --git a/packages/beacon-wallet/src/client/WalletClient.ts b/packages/beacon-wallet/src/client/WalletClient.ts index d2299735b..d33dab817 100644 --- a/packages/beacon-wallet/src/client/WalletClient.ts +++ b/packages/beacon-wallet/src/client/WalletClient.ts @@ -235,9 +235,9 @@ export class WalletClient extends Client { /** * The method will attempt to initiate a connection using the active transport. */ - public async _connect(): Promise { + public async _connect(attempts: number = 3): Promise { const transport: WalletP2PTransport = (await this.transport) as WalletP2PTransport - if (transport.connectionStatus !== TransportStatus.NOT_CONNECTED) { + if (attempts == 0 || transport.connectionStatus !== TransportStatus.NOT_CONNECTED) { return } @@ -247,7 +247,7 @@ export class WalletClient extends Client { logger.warn('_connect', err.message) if (err.message === 'The account is deactivated.') { await transport.disconnect() - await this._connect() + await this._connect(--attempts) return } }