From 819e0b6ed5915b9e3e5a03617aba7080370e5529 Mon Sep 17 00:00:00 2001 From: Hunain Bin Sajid Date: Fri, 29 Mar 2024 05:47:58 +0500 Subject: [PATCH 1/2] fix: signin persistance across controllers --- package-lock.json | 45 +++++++++- package.json | 1 + src/components/signinCard.tsx | 4 +- src/components/signinList.tsx | 29 ++++--- src/config/types.ts | 14 ++- src/pages/background/index.ts | 66 ++++++-------- src/pages/background/resource/signin/index.ts | 85 +++++++++++++++++++ src/pages/background/services/signify.ts | 6 ++ src/pages/background/signins-utils.ts | 43 ---------- 9 files changed, 188 insertions(+), 105 deletions(-) create mode 100644 src/pages/background/resource/signin/index.ts delete mode 100644 src/pages/background/signins-utils.ts diff --git a/package-lock.json b/package-lock.json index 169f7b91..b8cb2571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "APACHE 2.0", "dependencies": { "lodash": "^4.17.21", + "nanoid": "^5.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.6.2", @@ -4485,19 +4486,20 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.7", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz", + "integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -5002,6 +5004,24 @@ "version": "4.2.0", "license": "MIT" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -5764,6 +5784,23 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/styled-components/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/styled-components/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/package.json b/package.json index d2df7cf7..c732d778 100755 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "type": "module", "dependencies": { "lodash": "^4.17.21", + "nanoid": "^5.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.6.2", diff --git a/src/components/signinCard.tsx b/src/components/signinCard.tsx index dcb143aa..6389b5a9 100644 --- a/src/components/signinCard.tsx +++ b/src/components/signinCard.tsx @@ -46,7 +46,7 @@ export function SigninCard({ ? formatMessage({ id: "signin.identifierAlias" }) : formatMessage({ id: "credential.title" })} - {signin?.identifier?.name} + {signin?.identifier?.name ?? signin?.credential?.schema?.title}
@@ -63,7 +63,7 @@ export function SigninCard({ {formatMessage({ id: "signin.autoSignin" })} } /> diff --git a/src/components/signinList.tsx b/src/components/signinList.tsx index 5cf8005a..706bf8ff 100644 --- a/src/components/signinList.tsx +++ b/src/components/signinList.tsx @@ -4,9 +4,12 @@ import { SigninCard } from "@components/signinCard"; import { Loader, Flex, Box, Text } from "@components/ui"; import { IMessage, ISignin } from "@config/types"; -interface IResourceSignin { - index: number; - signin?: ISignin; +interface IDeleteSignin { + id: string; +} + +interface IUpdateSignin { + signin: ISignin; } export function SigninList(): JSX.Element { @@ -23,14 +26,14 @@ export function SigninList(): JSX.Element { setIsLoading(false); }; - const deleteSignin = async (index: number) => { + const deleteSignin = async (id: string) => { const { data } = await chrome.runtime.sendMessage< - IMessage + IMessage >({ type: "delete-resource", subtype: "signins", data: { - index, + id, }, }); if (data?.isDeleted) { @@ -52,15 +55,13 @@ export function SigninList(): JSX.Element { } }; - const updateAutoSignin = async (index: number, signin: ISignin) => { - console.log("signin", signin, index); + const updateAutoSignin = async (signin: ISignin) => { const { data } = await chrome.runtime.sendMessage< - IMessage + IMessage >({ type: "update-resource", subtype: "auto-signin", data: { - index, signin, }, }); @@ -94,12 +95,12 @@ export function SigninList(): JSX.Element { ) : null} - {signins.map((signin, index) => ( - + {signins.map((signin) => ( + deleteSignin(index)} - handleAutoSignin={() => updateAutoSignin(index, signin)} + handleDelete={() => deleteSignin(signin.id)} + handleAutoSignin={() => updateAutoSignin(signin)} /> ))} diff --git a/src/config/types.ts b/src/config/types.ts index d7772c4d..3f5c27fc 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -1,3 +1,7 @@ +export interface ObjectOfArrays { + [key: string]: T[]; +} + export interface IMessage { type: string; subtype?: string; @@ -31,18 +35,22 @@ export interface IVendorData { } export interface ISignin { + id: string; domain: string; identifier?: { name?: string; + prefix?: string; }; credential?: { issueeName?: string; + sad: { d: string }; schema?: { title: string; }; }; - updatedAt: string; - autoSignin: boolean; + createdAt: number; + updatedAt: number; + autoSignin?: boolean; } export interface IIdentifier { @@ -52,7 +60,7 @@ export interface IIdentifier { export interface ICredential { issueeName: string; - sad: { a: { i: string } }; + sad: { a: { i: string }, d: string }; schema: { title: string; credentialType: string; diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index 8589d015..01b69b17 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -5,6 +5,7 @@ import { } from "@pages/background/services/config"; import { userService } from "@pages/background/services/user"; import { signifyService } from "@pages/background/services/signify"; +import * as signinResource from "@pages/background/resource/signin"; import { IMessage, IIdentifier, ICredential } from "@config/types"; import { senderIsPopup } from "@pages/background/utils"; import { @@ -12,11 +13,6 @@ import { getCurrentUrl, setActionIcon, } from "@pages/background/utils"; -import { - updateDomainAutoSigninByIndex, - getSigninsByDomain, - deleteSigninByIndex, -} from "@pages/background/signins-utils"; console.log("Background script loaded"); @@ -116,8 +112,7 @@ chrome.runtime.onMessage.addListener(function ( message.subtype === "auto-signin-signature" ) { // Validate that message comes from a page that has a signin - const origin = removeSlash(sender.url); - const signins = await getSigninsByDomain(origin); + const signins = await signinResource.getSigninsByDomain(sender.url!); const autoSignin = signins?.find((signin) => signin.autoSignin); if (!signins?.length || !autoSignin) { sendResponse({ @@ -179,7 +174,7 @@ chrome.runtime.onMessage.addListener(function ( message.type === "fetch-resource" && message.subtype === "tab-signin" ) { - const signins = await getSigninsByDomain(removeSlash(sender.url)); + const signins = await signinResource.getSigninsByDomain(sender.url); const autoSigninObj = signins?.find((signin) => signin.autoSignin); sendResponse({ data: { signins: signins ?? [], autoSigninObj } }); } @@ -277,47 +272,44 @@ chrome.runtime.onMessage.addListener(function ( } if (message.type === "create-resource" && message.subtype === "signin") { - const signins = (await browserStorageService.getValue( - "signins" - )) as any[]; + const signins = await signinResource.getSignins(); const currentUrl = await getCurrentUrl(); const { identifier, credential } = message.data; let signinExists = false; if (identifier && identifier.prefix) { - signinExists = signins?.find( - (signin) => - signin.domain === currentUrl?.origin && - signin?.identifier?.prefix === identifier.prefix + signinExists = Boolean( + signins?.find( + (signin) => + signin.domain === currentUrl?.origin && + signin?.identifier?.prefix === identifier.prefix + ) ); } if (credential && credential.sad.d) { - signinExists = signins?.find( - (signin) => - signin.domain === currentUrl?.origin && - signin?.credential?.sad?.d === credential.sad.d + signinExists = Boolean( + signins?.find( + (signin) => + signin.domain === currentUrl?.origin && + signin?.credential?.sad?.d === credential.sad.d + ) ); } if (signinExists) { sendResponse({ data: { signins: signins } }); } else { - const signinObj = { + const signinObj = signinResource.newSigninObject({ identifier, credential, - createdAt: new Date().getTime(), - updatedAt: new Date().getTime(), domain: currentUrl!.origin, - }; + }); if (signins && signins?.length) { - await browserStorageService.setValue("signins", [ - ...signins, - signinObj, - ]); + await signinResource.updateSignins([...signins, signinObj]); } else { - await browserStorageService.setValue("signins", [signinObj]); + await signinResource.updateSignins([signinObj]); } - const storageSignins = await browserStorageService.getValue("signins"); + const storageSignins = await signinResource.getSignins(); sendResponse({ data: { signins: storageSignins } }); } } @@ -344,10 +336,10 @@ chrome.runtime.onMessage.addListener(function ( } if (message.type === "fetch-resource" && message.subtype === "signins") { - const signins = await browserStorageService.getValue("signins"); + const signins = await signinResource.getSignins(); sendResponse({ data: { - signins: signins ?? [], + signins, }, }); } @@ -356,10 +348,7 @@ chrome.runtime.onMessage.addListener(function ( message.type === "update-resource" && message.subtype === "auto-signin" ) { - const resp = await updateDomainAutoSigninByIndex( - message?.data?.index, - message?.data?.signin - ); + const resp = await signinResource.updateAutoSigninByDomain(message?.data?.signin); sendResponse({ data: { ...resp, @@ -368,7 +357,7 @@ chrome.runtime.onMessage.addListener(function ( } if (message.type === "delete-resource" && message.subtype === "signins") { - const resp = await deleteSigninByIndex(message?.data?.index); + const resp = await signinResource.deleteSigninById(message?.data?.id); sendResponse({ data: { ...resp, @@ -415,8 +404,7 @@ chrome.runtime.onMessageExternal.addListener(function ( message.subtype === "auto-signin-signature" ) { // Validate that message comes from a page that has a signin - const origin = removeSlash(sender.url); - const signins = await getSigninsByDomain(origin); + const signins = await signinResource.getSigninsByDomain(sender.url); console.log("signins", signins); const autoSignin = signins?.find((signin) => signin.autoSignin); if (!signins?.length || !autoSignin) { @@ -431,7 +419,7 @@ chrome.runtime.onMessageExternal.addListener(function ( autoSignin?.identifier ? autoSignin?.identifier?.name : autoSignin?.credential?.issueeName, - origin + removeSlash(sender.url) ); let jsonHeaders: { [key: string]: string } = {}; for (const pair of signedHeaders.entries()) { diff --git a/src/pages/background/resource/signin/index.ts b/src/pages/background/resource/signin/index.ts new file mode 100644 index 00000000..640fa9fb --- /dev/null +++ b/src/pages/background/resource/signin/index.ts @@ -0,0 +1,85 @@ +import { nanoid } from "nanoid"; +import { browserStorageService } from "@pages/background/services/browser-storage"; +import { signifyService } from "@pages/background/services/signify"; +import { removeSlash } from "@pages/background/utils"; +import { + ObjectOfArrays, + ISignin, + ICredential, + IIdentifier, +} from "@config/types"; + +type IObjectSignins = ObjectOfArrays; + +const getSigninsObject = async (): Promise => { + const signinsObj = (await browserStorageService.getValue( + "signins" + )) as IObjectSignins; + return signinsObj ?? {}; +}; + +export const getSignins = async (): Promise => { + const signinsObj = await getSigninsObject(); + return signinsObj[signifyService.getAgentID()] ?? []; +}; + +export const updateSignins = async (signins: ISignin[]) => { + const signinsObj = await getSigninsObject(); + signinsObj[signifyService.getAgentID()] = signins; + await browserStorageService.setValue("signins", signinsObj); +}; + +export const getSigninsByDomain = async (url?: string) => { + const domain = removeSlash(url); + const signins = await getSignins(); + return signins?.filter((signin) => signin.domain === domain); +}; + +export const updateAutoSigninByDomain = async (signin: ISignin) => { + let signins = await getSignins(); + if (signins?.length) { + const newSignins = signins.map((_ele) => { + if (_ele.domain === signin.domain) { + if (signin.id === _ele.id) + return { ..._ele, autoSignin: !signin?.autoSignin }; + return { ..._ele, autoSignin: false }; + } + return _ele; + }); + await updateSignins(newSignins); + signins = await getSignins(); + } + return { signins }; +}; + +export const deleteSigninById = async (id: string) => { + let signins = await getSignins(); + let deleted = false; + if (signins?.length) { + const newSignins = signins.filter((_ele) => id !== _ele.id); + await updateSignins(newSignins); + deleted = newSignins.length !== signins?.length; + } + signins = await getSignins(); + return { isDeleted: deleted, signins }; +}; + +export const newSigninObject = ({ + identifier, + credential, + domain, +}: { + identifier?: IIdentifier; + credential?: ICredential; + domain: string; +}): ISignin => { + const signinObj: ISignin = { + id: nanoid(), + credential, + domain, + identifier, + createdAt: new Date().getTime(), + updatedAt: new Date().getTime(), + }; + return signinObj; +}; diff --git a/src/pages/background/services/signify.ts b/src/pages/background/services/signify.ts index 4fdde8c2..9a73b395 100644 --- a/src/pages/background/services/signify.ts +++ b/src/pages/background/services/signify.ts @@ -150,6 +150,11 @@ const Signify = () => { return signed_headers; }; + const getAgentID = (): string => { + validateClient(); + return _client?.agent?.pre!; + }; + const createAID = async (name: string) => { validateClient(); let res = await _client?.identifiers().create(name); @@ -166,6 +171,7 @@ const Signify = () => { createAID, generatePasscode, bootAndConnect, + getAgentID, }; }; diff --git a/src/pages/background/signins-utils.ts b/src/pages/background/signins-utils.ts deleted file mode 100644 index c54ee8e6..00000000 --- a/src/pages/background/signins-utils.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { browserStorageService } from "@pages/background/services/browser-storage"; -import { ISignin } from "@config/types"; - -export const getSigninsByDomain = async (domain: string) => { - const signins = (await browserStorageService.getValue( - "signins" - )) as ISignin[]; - return signins?.filter((signin) => signin.domain === domain) ?? []; -}; - -export const updateDomainAutoSigninByIndex = async ( - index: number, - signin: ISignin -) => { - let signins = (await browserStorageService.getValue("signins")) as ISignin[]; - if (signins?.length) { - const newSignins = signins.map((_ele, idx) => { - if (idx !== index && _ele.domain !== signin.domain) return _ele; - - if (idx !== index && _ele.domain === signin.domain) - return { ..._ele, autoSignin: false }; - - return { ..._ele, autoSignin: !signin?.autoSignin }; - }); - await browserStorageService.setValue("signins", newSignins); - } - signins = (await browserStorageService.getValue("signins")) as ISignin[]; - - return { signins }; -}; - -export const deleteSigninByIndex = async (index: number) => { - let signins = (await browserStorageService.getValue("signins")) as ISignin[]; - let deleted = false; - if (signins?.length) { - const newSignins = signins.filter((_ele, idx) => idx !== index); - await browserStorageService.setValue("signins", newSignins); - deleted = newSignins.length !== signins?.length; - } - signins = (await browserStorageService.getValue("signins")) as ISignin[]; - - return { isDeleted: deleted, signins }; -}; From f1947a2a1aac29f03a10313aa70f466f0224de3f Mon Sep 17 00:00:00 2001 From: Hunain Bin Sajid Date: Fri, 29 Mar 2024 20:10:37 +0500 Subject: [PATCH 2/2] fix: user controllerId instead of agentId --- src/pages/background/index.ts | 2 - src/pages/background/resource/signin/index.ts | 6 +- src/pages/background/services/signify.ts | 14 ++-- src/pages/background/services/user.ts | 69 ++++++++++++------- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/pages/background/index.ts b/src/pages/background/index.ts index 01b69b17..f0154cc7 100644 --- a/src/pages/background/index.ts +++ b/src/pages/background/index.ts @@ -1,4 +1,3 @@ -import { browserStorageService } from "@pages/background/services/browser-storage"; import { WEB_APP_PERMS, configService, @@ -209,7 +208,6 @@ chrome.runtime.onMessage.addListener(function ( message.subtype === "disconnect-agent" ) { await signifyService.disconnect(); - await userService.removePasscode(); sendResponse({ data: { isConnected: false } }); } diff --git a/src/pages/background/resource/signin/index.ts b/src/pages/background/resource/signin/index.ts index 640fa9fb..43e5b1c9 100644 --- a/src/pages/background/resource/signin/index.ts +++ b/src/pages/background/resource/signin/index.ts @@ -20,12 +20,14 @@ const getSigninsObject = async (): Promise => { export const getSignins = async (): Promise => { const signinsObj = await getSigninsObject(); - return signinsObj[signifyService.getAgentID()] ?? []; + const controllerId = await signifyService.getControllerID(); + return signinsObj[controllerId] ?? []; }; export const updateSignins = async (signins: ISignin[]) => { const signinsObj = await getSigninsObject(); - signinsObj[signifyService.getAgentID()] = signins; + const controllerId = await signifyService.getControllerID(); + signinsObj[controllerId] = signins; await browserStorageService.setValue("signins", signinsObj); }; diff --git a/src/pages/background/services/signify.ts b/src/pages/background/services/signify.ts index 9a73b395..2ae23a1e 100644 --- a/src/pages/background/services/signify.ts +++ b/src/pages/background/services/signify.ts @@ -20,6 +20,7 @@ const Signify = () => { } catch (error) { console.log("Timer expired, client and passcode zeroed out"); _client = null; + await userService.removeControllerId(); await userService.removePasscode(); } @@ -47,6 +48,8 @@ const Signify = () => { _client = new SignifyClient(agentUrl, passcode, Tier.low, bootUrl); await _client.boot(); await _client.connect(); + const state = await getState(); + await userService.setControllerId(state?.controller?.state?.i); setTimeoutAlarm(); } catch (error) { console.error(error); @@ -60,6 +63,8 @@ const Signify = () => { await ready(); _client = new SignifyClient(agentUrl, passcode, Tier.low); await _client.connect(); + const state = await getState(); + await userService.setControllerId(state?.controller?.state?.i); setTimeoutAlarm(); } catch (error) { console.error(error); @@ -117,6 +122,7 @@ const Signify = () => { const disconnect = async () => { _client = null; + await userService.removeControllerId(); await userService.removePasscode(); }; @@ -150,9 +156,9 @@ const Signify = () => { return signed_headers; }; - const getAgentID = (): string => { - validateClient(); - return _client?.agent?.pre!; + const getControllerID = async (): Promise => { + const controllerId = await userService.getControllerId(); + return controllerId; }; const createAID = async (name: string) => { @@ -171,7 +177,7 @@ const Signify = () => { createAID, generatePasscode, bootAndConnect, - getAgentID, + getControllerID, }; }; diff --git a/src/pages/background/services/user.ts b/src/pages/background/services/user.ts index b9f5140a..f91152d7 100644 --- a/src/pages/background/services/user.ts +++ b/src/pages/background/services/user.ts @@ -1,27 +1,50 @@ -import { browserStorageService } from "@pages/background/services/browser-storage" +import { browserStorageService } from "@pages/background/services/browser-storage"; const USER_ENUMS = { - PASSCODE: "user-passcode" -} + PASSCODE: "user-passcode", + CONTROLLER_ID: "controller-id", +}; const User = () => { - const getPasscode = async () : Promise => { - return await browserStorageService.getValue(USER_ENUMS.PASSCODE) as string; - } - - const removePasscode = async () => { - await browserStorageService.removeKey(USER_ENUMS.PASSCODE); - } - - const setPasscode = async (passcode: string) => { - await browserStorageService.setValue(USER_ENUMS.PASSCODE, passcode) - } - - return { - removePasscode, - getPasscode, - setPasscode - } -} - -export const userService = User() \ No newline at end of file + const getPasscode = async (): Promise => { + return (await browserStorageService.getValue( + USER_ENUMS.PASSCODE + )) as string; + }; + + const removePasscode = async () => { + await browserStorageService.removeKey(USER_ENUMS.PASSCODE); + }; + + const setPasscode = async (passcode: string) => { + await browserStorageService.setValue(USER_ENUMS.PASSCODE, passcode); + }; + + const getControllerId = async (): Promise => { + return (await browserStorageService.getValue( + USER_ENUMS.CONTROLLER_ID + )) as string; + }; + + const removeControllerId = async () => { + await browserStorageService.removeKey(USER_ENUMS.CONTROLLER_ID); + }; + + const setControllerId = async (controllerId: string) => { + await browserStorageService.setValue( + USER_ENUMS.CONTROLLER_ID, + controllerId + ); + }; + + return { + removePasscode, + getPasscode, + setPasscode, + getControllerId, + setControllerId, + removeControllerId, + }; +}; + +export const userService = User();