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/controllers/TorusController.ts b/src/controllers/TorusController.ts index 05179660..4be2c80f 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"; @@ -216,6 +218,8 @@ export default class TorusController extends BaseController; _state: Partial }) { super({ config: _config, state: _state }); } @@ -774,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 { @@ -1384,6 +1388,7 @@ 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); diff --git a/src/modules/controllers.ts b/src/modules/controllers.ts index 72c51040..e21d8937 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,13 @@ class ControllerModule extends VuexModule { } }); - this.torus.on("logout", () => { - // logoutWithBC(); - this.logout(); + 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); @@ -486,10 +490,11 @@ class ControllerModule extends VuexModule { } @Action - async logout(): Promise { + async logout(fromBC?: boolean): Promise { if (isMain && this.selectedAddress) { this.openloginLogout(); } + 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/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..62fc7be6 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]; @@ -190,10 +191,27 @@ 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); + // 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); + await bc.postMessage({ instanceId, timestamp }); + await bc.close(); }; 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; +}