From 624b3ed85589ec1e63ed319994dbdbb6585bc080 Mon Sep 17 00:00:00 2001 From: Cheok <30773478+jiafuei@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:35:29 +0800 Subject: [PATCH 1/4] feat: sync logout across browser tab sessions feat: constrain to origin, ignore repeated logout messages from current window feat: temp fix for brave browser instant logout feat: skip broadcast if logout was received from bc feat: use native methods for bc instead of server feat: scope channel name using verifierid and browser --- src/controllers/TorusController.ts | 33 ++++++++++++++++++++++++++++-- src/modules/controllers.ts | 10 ++++----- src/utils/const.ts | 2 ++ src/utils/helpers.ts | 24 ++++++++++++++++++---- src/utils/interfaces.ts | 5 +++++ 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/controllers/TorusController.ts b/src/controllers/TorusController.ts index 05179660..0a0f4a38 100644 --- a/src/controllers/TorusController.ts +++ b/src/controllers/TorusController.ts @@ -38,6 +38,7 @@ import { TX_EVENTS, UserInfo, } from "@toruslabs/base-controllers"; +import { BroadcastChannel } from "@toruslabs/broadcast-channel"; import eccrypto from "@toruslabs/eccrypto"; import { LOGIN_PROVIDER_TYPE } from "@toruslabs/openlogin"; import { @@ -96,8 +97,9 @@ import { TorusControllerState, TransactionChannelDataType, } from "@/utils/enums"; -import { getRandomWindowId, getRelaySigned, getUserLanguage, isMain, normalizeJson, parseJwt } from "@/utils/helpers"; -import { constructTokenData } from "@/utils/instructionDecoder"; +import { getLogoutBcChannelName, getRandomWindowId, getRelaySigned, getUserLanguage, isMain, normalizeJson, parseJwt } from "@/utils/helpers"; +import { constructTokenData } from "@/utils/instruction_decoder"; +import { LogoutMessage } from "@/utils/interfaces"; import TorusStorageLayer from "@/utils/tkey/storageLayer"; import { TOPUP } from "@/utils/topup"; @@ -1238,6 +1240,9 @@ export default class TorusController extends BaseController(channelName); + const start = new Date().getTime(); + const eventListener = (msg: LogoutMessage) => { + const nowTime = new Date().getTime(); + const msElapsedSinceAttach = nowTime - start; + const thisInstance = this.instanceId.slice(0, 8); + // Ignore the messages sent within 500ms of login, logic can be removed once TODO below is resolved + // TODO: Investigate Brave Browser receiving old logout messges from broadcast channel server + if (thisInstance === msg.instanceId || msElapsedSinceAttach <= 500) return; + bc.removeEventListener("message", eventListener); + bc.close() + .then(() => this.emit("logout", true)) + .catch((err) => log.error("broadcastchannel close error", err)); + }; + bc.addEventListener("message", eventListener); + } } diff --git a/src/modules/controllers.ts b/src/modules/controllers.ts index 72c51040..dddefc80 100644 --- a/src/modules/controllers.ts +++ b/src/modules/controllers.ts @@ -38,7 +38,7 @@ import { i18n } from "@/plugins/i18nPlugin"; import installStorePlugin from "@/plugins/persistPlugin"; import { WALLET_SUPPORTED_NETWORKS } from "@/utils/const"; import { CONTROLLER_MODULE_KEY, LOCAL_STORAGE_KEY, TorusControllerState } from "@/utils/enums"; -import { delay, isMain } from "@/utils/helpers"; +import { delay, isMain, logoutWithBC } from "@/utils/helpers"; import { NAVBAR_MESSAGES } from "@/utils/messages"; import store from "../store"; @@ -413,9 +413,8 @@ class ControllerModule extends VuexModule { } }); - this.torus.on("logout", () => { - // logoutWithBC(); - this.logout(); + this.torus.on("logout", (fromBC?: boolean) => { + this.logout(fromBC); }); this.setInstanceId(instanceId); @@ -486,9 +485,10 @@ class ControllerModule extends VuexModule { } @Action - async logout(): Promise { + async logout(fromBC?: boolean): Promise { if (isMain && this.selectedAddress) { this.openloginLogout(); + if (!fromBC) logoutWithBC(this.torus.origin, this.instanceId, this.torus.userInfo); } const initialState = { ...cloneDeep(DEFAULT_STATE) }; // this.updateTorusState(initialState); diff --git a/src/utils/const.ts b/src/utils/const.ts index e771fdd8..4d47e057 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -16,6 +16,8 @@ export const WALLET_SUPPORTED_NETWORKS = { }, }; +export const CHANNEL_LOGOUT = "LOGOUT_WINDOWS_CHANNEL"; + // testnet: { // blockExplorerUrl: "?cluster=testnet", // chainId: "0x2", diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 8a5c0d49..67d398bf 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -190,10 +190,26 @@ export const parseJwt = (token: string) => { return JSON.parse(jsonPayload); }; -export const logoutWithBC = async () => { - const bc = new BroadcastChannel("LOGOUT_WINDOWS_CHANNEL"); - await bc.postMessage("logout"); - bc.close(); +// Get a sufficiently unique channel name for +// the current window +export const getLogoutBcChannelName = (origin: string, userInfo: UserInfo) => { + const browser = bowser.getParser(window.navigator.userAgent); + const browserId = `${browser.getBrowserName()}_${browser.getBrowserVersion}`; + const platformId = `${browser.getOSName()}_${browser.getPlatformType()}`; + const userId = `${userInfo.verifier}_${userInfo.verifierId}`; + const id = `${userId}_${origin}_${browserId}_${platformId}`; + const hash = keccak(Buffer.from(id)).toString("hex"); + return hash; +}; + +export const logoutWithBC = async (origin: string, _instanceId: string, userInfo: UserInfo) => { + const channelName = getLogoutBcChannelName(origin, userInfo); + const bc = new BroadcastChannel(`${channelName}`); + const timestamp = new Date().getTime(); + const instanceId = _instanceId.slice(0, 8); + bc.postMessage({ instanceId, timestamp }) + .then(() => bc.close()) + .catch((err) => log.error(err)); }; export function getBrowserKey() { diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index bea31631..c904e1db 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -42,3 +42,8 @@ export interface FinalTxData { totalFiatCost: string; isGasless: boolean; } + +export interface LogoutMessage { + instanceId: string; + timestamp: number; +} From 4d79e9d0a143009aae5b101ecb9da4e1adff30f4 Mon Sep 17 00:00:00 2001 From: Cheok <30773478+jiafuei@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:41:51 +0800 Subject: [PATCH 2/4] feat: use timeout & cleanup --- src/controllers/TorusController.ts | 9 ++------- src/utils/helpers.ts | 5 +++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/controllers/TorusController.ts b/src/controllers/TorusController.ts index 0a0f4a38..52b91df3 100644 --- a/src/controllers/TorusController.ts +++ b/src/controllers/TorusController.ts @@ -1704,14 +1704,9 @@ export default class TorusController extends BaseController(channelName); - const start = new Date().getTime(); + const thisInstance = this.instanceId.slice(0, 8); const eventListener = (msg: LogoutMessage) => { - const nowTime = new Date().getTime(); - const msElapsedSinceAttach = nowTime - start; - const thisInstance = this.instanceId.slice(0, 8); - // Ignore the messages sent within 500ms of login, logic can be removed once TODO below is resolved - // TODO: Investigate Brave Browser receiving old logout messges from broadcast channel server - if (thisInstance === msg.instanceId || msElapsedSinceAttach <= 500) return; + if (thisInstance === msg.instanceId) return; bc.removeEventListener("message", eventListener); bc.close() .then(() => this.emit("logout", true)) diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 67d398bf..4eb603a4 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,4 +1,4 @@ -import { concatSig } from "@toruslabs/base-controllers"; +import { concatSig, UserInfo } from "@toruslabs/base-controllers"; import { BroadcastChannel } from "@toruslabs/broadcast-channel"; import { post } from "@toruslabs/http-helpers"; import bowser from "bowser"; @@ -10,6 +10,7 @@ import config from "@/config"; import { addToast } from "@/modules/app"; import { LOCALE_EN, LOGIN_CONFIG, STORAGE_TYPE } from "./enums"; +import { LogoutMessage } from "./interfaces"; export function getStorage(key: STORAGE_TYPE): Storage | undefined { if (config.isStorageAvailable[key]) return window[key]; @@ -204,7 +205,7 @@ export const getLogoutBcChannelName = (origin: string, userInfo: UserInfo) => { export const logoutWithBC = async (origin: string, _instanceId: string, userInfo: UserInfo) => { const channelName = getLogoutBcChannelName(origin, userInfo); - const bc = new BroadcastChannel(`${channelName}`); + const bc = new BroadcastChannel(`${channelName}`, { server: { timeout: 5 } }); const timestamp = new Date().getTime(); const instanceId = _instanceId.slice(0, 8); bc.postMessage({ instanceId, timestamp }) From 06eef3010e3c12b031cbc58097e33f73bfc3e1dd Mon Sep 17 00:00:00 2001 From: Cheok <30773478+jiafuei@users.noreply.github.com> Date: Tue, 12 Jul 2022 20:27:26 +0800 Subject: [PATCH 3/4] chore: use latest broadcast-channel package for timeout --- package-lock.json | 44 +++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- src/utils/helpers.ts | 2 ++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 58d96658..64b05efc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@solana/spl-token": "0.2.0", "@solana/web3.js": "^1.42.0", "@toruslabs/base-controllers": "^2.1.0", - "@toruslabs/broadcast-channel": "^5.0.2", + "@toruslabs/broadcast-channel": "^6.1.0", "@toruslabs/eccrypto": "^1.1.8", "@toruslabs/http-helpers": "^3.0.0", "@toruslabs/loglevel-sentry": "^4.0.0", @@ -3445,7 +3445,7 @@ "@babel/runtime": "7.x" } }, - "node_modules/@toruslabs/broadcast-channel": { + "node_modules/@toruslabs/base-controllers/node_modules/@toruslabs/broadcast-channel": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@toruslabs/broadcast-channel/-/broadcast-channel-5.0.2.tgz", "integrity": "sha512-B4kMZfcSZWzIi81gfb7deHFOa55RvCTUtIhCvTrGVobo8WZ8cCJC/4bLm1AbzgiPky8dbh8cb7mewuvnqaZpbA==", @@ -3461,6 +3461,22 @@ "unload": "^2.3.1" } }, + "node_modules/@toruslabs/broadcast-channel": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@toruslabs/broadcast-channel/-/broadcast-channel-6.1.0.tgz", + "integrity": "sha512-7aBVHA2RXI1RQaoMPTmb4jBVcQYp9/cxrMbQ90BEX1tDu11abS0MYjxR3ZfvyRQuU9RqRWeaG0leul5xouV6kA==", + "dependencies": { + "@babel/runtime": "^7.17.9", + "@toruslabs/eccrypto": "^1.1.8", + "@toruslabs/metadata-helpers": "^3.0.0", + "bowser": "^2.11.0", + "keccak": "^3.0.2", + "loglevel": "^1.8.0", + "oblivious-set": "1.1.1", + "socket.io-client": "^4.5.1", + "unload": "^2.3.1" + } + }, "node_modules/@toruslabs/eccrypto": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@toruslabs/eccrypto/-/eccrypto-1.1.8.tgz", @@ -23495,12 +23511,30 @@ "json-rpc-random-id": "^1.0.1", "lodash": "^4.17.21", "loglevel": "^1.8.0" + }, + "dependencies": { + "@toruslabs/broadcast-channel": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@toruslabs/broadcast-channel/-/broadcast-channel-5.0.2.tgz", + "integrity": "sha512-B4kMZfcSZWzIi81gfb7deHFOa55RvCTUtIhCvTrGVobo8WZ8cCJC/4bLm1AbzgiPky8dbh8cb7mewuvnqaZpbA==", + "requires": { + "@babel/runtime": "^7.17.9", + "@toruslabs/eccrypto": "^1.1.8", + "@toruslabs/metadata-helpers": "^3.0.0", + "bowser": "^2.11.0", + "keccak": "^3.0.2", + "loglevel": "^1.8.0", + "oblivious-set": "1.1.1", + "socket.io-client": "^4.5.1", + "unload": "^2.3.1" + } + } } }, "@toruslabs/broadcast-channel": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@toruslabs/broadcast-channel/-/broadcast-channel-5.0.2.tgz", - "integrity": "sha512-B4kMZfcSZWzIi81gfb7deHFOa55RvCTUtIhCvTrGVobo8WZ8cCJC/4bLm1AbzgiPky8dbh8cb7mewuvnqaZpbA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@toruslabs/broadcast-channel/-/broadcast-channel-6.1.0.tgz", + "integrity": "sha512-7aBVHA2RXI1RQaoMPTmb4jBVcQYp9/cxrMbQ90BEX1tDu11abS0MYjxR3ZfvyRQuU9RqRWeaG0leul5xouV6kA==", "requires": { "@babel/runtime": "^7.17.9", "@toruslabs/eccrypto": "^1.1.8", diff --git a/package.json b/package.json index 0b0a2963..064d570e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@solana/spl-token": "0.2.0", "@solana/web3.js": "^1.42.0", "@toruslabs/base-controllers": "^2.1.0", - "@toruslabs/broadcast-channel": "^5.0.2", + "@toruslabs/broadcast-channel": "^6.1.0", "@toruslabs/eccrypto": "^1.1.8", "@toruslabs/http-helpers": "^3.0.0", "@toruslabs/loglevel-sentry": "^4.0.0", diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 4eb603a4..e448bb37 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -205,6 +205,8 @@ export const getLogoutBcChannelName = (origin: string, userInfo: UserInfo) => { export const logoutWithBC = async (origin: string, _instanceId: string, userInfo: UserInfo) => { const channelName = getLogoutBcChannelName(origin, userInfo); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const bc = new BroadcastChannel(`${channelName}`, { server: { timeout: 5 } }); const timestamp = new Date().getTime(); const instanceId = _instanceId.slice(0, 8); From 8c35ca7fce14e21a7d5e01ca666f64f0ebd5f9d6 Mon Sep 17 00:00:00 2001 From: Cheok <30773478+jiafuei@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:11:25 +0800 Subject: [PATCH 4/4] feat: logout sync for embed feat: logout for embed --- src/controllers/TorusController.ts | 53 +++++++++++++++++------------- src/modules/controllers.ts | 7 +++- src/utils/helpers.ts | 5 ++- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/controllers/TorusController.ts b/src/controllers/TorusController.ts index 52b91df3..4be2c80f 100644 --- a/src/controllers/TorusController.ts +++ b/src/controllers/TorusController.ts @@ -218,6 +218,8 @@ export default class TorusController extends BaseController; _state: Partial }) { super({ config: _config, state: _state }); } @@ -776,7 +778,7 @@ export default class TorusController extends BaseController, res: JRPCResponse, _: JRPCEngineNextCallback, end: JRPCEngineEndCallback): void { this.handleLogout(); res.result = true; - end(); + setTimeout(() => end(), 100); // Make sure all async ops are executed. } public handleLogout(): void { @@ -1240,9 +1242,6 @@ export default class TorusController extends BaseController(channelName); + this.logoutBcAttached = true; + + const thisInstance = this.instanceId.slice(0, 8); + const eventListener = (msg: LogoutMessage) => { + if (thisInstance === msg.instanceId) return; + bc.removeEventListener("message", eventListener); + bc.close() + .then(() => { + this.logoutBcAttached = false; + this.emit("logout", true); + if (!isMain) this.notifyEmbedLogout(); + return null; + }) + .catch((err) => log.error("broadcastchannel close error", err)); + }; + bc.addEventListener("message", eventListener); + } + private async providerRequestAccounts(req: JRPCRequest) { const accounts = await this.requestAccounts(req); @@ -1700,18 +1721,4 @@ export default class TorusController extends BaseController(channelName); - const thisInstance = this.instanceId.slice(0, 8); - const eventListener = (msg: LogoutMessage) => { - if (thisInstance === msg.instanceId) return; - bc.removeEventListener("message", eventListener); - bc.close() - .then(() => this.emit("logout", true)) - .catch((err) => log.error("broadcastchannel close error", err)); - }; - bc.addEventListener("message", eventListener); - } } diff --git a/src/modules/controllers.ts b/src/modules/controllers.ts index dddefc80..e21d8937 100644 --- a/src/modules/controllers.ts +++ b/src/modules/controllers.ts @@ -416,6 +416,11 @@ class ControllerModule extends VuexModule { this.torus.on("logout", (fromBC?: boolean) => { this.logout(fromBC); }); + this.torus.on("LOGIN_RESPONSE", (message?: string, address?: string) => { + if (message === null && address) { + this.torus.attachLogoutBC(); + } + }); this.setInstanceId(instanceId); if (!isMain) { @@ -488,8 +493,8 @@ class ControllerModule extends VuexModule { async logout(fromBC?: boolean): Promise { if (isMain && this.selectedAddress) { this.openloginLogout(); - if (!fromBC) logoutWithBC(this.torus.origin, this.instanceId, this.torus.userInfo); } + if (!fromBC) await logoutWithBC(this.torus.origin, this.instanceId, this.torus.userInfo); const initialState = { ...cloneDeep(DEFAULT_STATE) }; // this.updateTorusState(initialState); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index e448bb37..62fc7be6 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -210,9 +210,8 @@ export const logoutWithBC = async (origin: string, _instanceId: string, userInfo const bc = new BroadcastChannel(`${channelName}`, { server: { timeout: 5 } }); const timestamp = new Date().getTime(); const instanceId = _instanceId.slice(0, 8); - bc.postMessage({ instanceId, timestamp }) - .then(() => bc.close()) - .catch((err) => log.error(err)); + await bc.postMessage({ instanceId, timestamp }); + await bc.close(); }; export function getBrowserKey() {