From b533ceb8df7527f9243992e427d31b02e4cc555a Mon Sep 17 00:00:00 2001 From: Hunain Bin Sajid Date: Tue, 10 Sep 2024 17:07:54 +0500 Subject: [PATCH] feat: add session --- src/components/ui/radio/radio.tsx | 2 +- src/config/types.ts | 5 +- src/pages/background/services/session.ts | 40 +++--- src/pages/background/services/signify.ts | 161 ++++++++++++++--------- src/pages/content/dialog/Dialog.tsx | 48 ++++--- src/pages/content/index.tsx | 14 +- 6 files changed, 158 insertions(+), 112 deletions(-) diff --git a/src/components/ui/radio/radio.tsx b/src/components/ui/radio/radio.tsx index f97fe90..677ff16 100644 --- a/src/components/ui/radio/radio.tsx +++ b/src/components/ui/radio/radio.tsx @@ -12,7 +12,7 @@ interface IRadio { const StyledRadio = styled.input` width: 16px; height: 16px; - accent-color: #61783e; + accent-color: ${({ theme }) => theme?.colors?.primary}; `; const StyledRadioLabel = styled.label` diff --git a/src/config/types.ts b/src/config/types.ts index 561ff91..cea6280 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -93,11 +93,10 @@ export interface ISession { origin: string; aidName: string; signinId: string; - maxReq?: number; + // maxReq?: number; currentReq?: number; } export interface ISessionConfig { - sessionTime: number; - maxReq: number; + sessionOneTime: boolean; } \ No newline at end of file diff --git a/src/pages/background/services/session.ts b/src/pages/background/services/session.ts index 83b4223..557f50d 100644 --- a/src/pages/background/services/session.ts +++ b/src/pages/background/services/session.ts @@ -26,17 +26,17 @@ const Session = () => { if (session.origin !== origin) { throw new Error("Session origin mismatch"); } - if ( - session?.maxReq && - session?.currentReq !== undefined && - session?.currentReq !== null && - session?.currentReq >= 0 - ) { - if (session?.currentReq >= session.maxReq) { - await remove(tabId); - throw new Error("Session max request limit reached"); - } - } + // if ( + // session?.maxReq && + // session?.currentReq !== undefined && + // session?.currentReq !== null && + // session?.currentReq >= 0 + // ) { + // if (session?.currentReq >= session.maxReq) { + // await remove(tabId); + // throw new Error("Session max request limit reached"); + // } + // } if (session.expiry < new Date().getTime()) { await remove(tabId); @@ -50,7 +50,7 @@ const Session = () => { tabId, origin, aidName, - config, + // config, }: Pick< ISession, "origin" | "aidName" | "tabId" | "signinId" @@ -58,21 +58,21 @@ const Session = () => { const sessions = await getSessionsObject(); const expiry = new Date(); // expiry.setSeconds(expiry.getSeconds() + 50); - const sessionTime = config?.sessionTime - ? Math.min(config.sessionTime, SESSION_ENUMS.EXPIRY_IN_MINS) - : SESSION_ENUMS.EXPIRY_IN_MINS; - expiry.setMinutes(expiry.getMinutes() + sessionTime); + // const sessionTime = config?.sessionTime + // ? Math.min(config.sessionTime, SESSION_ENUMS.EXPIRY_IN_MINS) + // : SESSION_ENUMS.EXPIRY_IN_MINS; + expiry.setMinutes(expiry.getMinutes() + SESSION_ENUMS.EXPIRY_IN_MINS); - const sessionMaxRequests = config?.maxReq - ? Math.min(SESSION_ENUMS.MAX_REQUESTS, config?.maxReq) - : undefined; + // const sessionMaxRequests = config?.maxReq + // ? Math.min(SESSION_ENUMS.MAX_REQUESTS, config?.maxReq) + // : undefined; sessions[tabId] = { origin, aidName, tabId, expiry: expiry.getTime(), signinId, - maxReq: sessionMaxRequests, + // maxReq: sessionMaxRequests, currentReq: 0, }; await browserStorageService.setValue(SESSION_ENUMS.SESSIONS, sessions); diff --git a/src/pages/background/services/signify.ts b/src/pages/background/services/signify.ts index fc9e671..99bccb3 100644 --- a/src/pages/background/services/signify.ts +++ b/src/pages/background/services/signify.ts @@ -1,6 +1,14 @@ import browser from "webextension-polyfill"; import * as signinResource from "@pages/background/resource/signin"; -import { SignifyClient, Tier, ready, randomPasscode, Saider, IssueCredentialResult, CredentialData } from "signify-ts"; +import { + SignifyClient, + Tier, + ready, + randomPasscode, + Saider, + IssueCredentialResult, + CredentialData, +} from "signify-ts"; import { sendMessage } from "@src/shared/browser/runtime-utils"; import { sendMessageTab, getCurrentTab } from "@src/shared/browser/tabs-utils"; import { userService } from "@pages/background/services/user"; @@ -8,7 +16,13 @@ import { configService } from "@pages/background/services/config"; import { sessionService } from "@pages/background/services/session"; import { IIdentifier, ISignin, ISessionConfig } from "@config/types"; import { SW_EVENTS } from "@config/event-types"; -import { formatAsCredentialEdgeOrRuleObject, getSchemaFieldOfEdge, parseSchemaEdgeOrRuleSection, setNodeValueInEdge, waitOperation } from "@src/shared/signify-utils"; +import { + formatAsCredentialEdgeOrRuleObject, + getSchemaFieldOfEdge, + parseSchemaEdgeOrRuleSection, + setNodeValueInEdge, + waitOperation, +} from "@src/shared/signify-utils"; const PASSCODE_TIMEOUT = 5; @@ -140,7 +154,10 @@ const Signify = () => { }; // credential identifier => credential.sad.d - const getCredential = async (credentialIdentifier: string, includeCESR: boolean = false) => { + const getCredential = async ( + credentialIdentifier: string, + includeCESR: boolean = false + ) => { validateClient(); return await _client?.credentials().get(credentialIdentifier, includeCESR); }; @@ -182,24 +199,35 @@ const Signify = () => { const response = { credential: credentialResp, identifier: signin?.identifier, - // autoSignin: signin?.autoSignin, }; - const sessionInfo = await sessionService.create({ - tabId, - origin, - aidName: aidName!, - signinId: signin.id, - config - }); - if (sessionInfo?.expiry) { - response.expiry = sessionInfo.expiry; + if (config?.sessionOneTime) { + const sreq = await _client?.createSignedRequest(aidName!, origin, {}); + let jsonHeaders: { [key: string]: string } = {}; + if (sreq?.headers) { + for (const pair of sreq.headers.entries()) { + jsonHeaders[pair[0]] = pair[1]; + } + } + response.headers = jsonHeaders; + } else { + const sessionInfo = await sessionService.create({ + tabId, + origin, + aidName: aidName!, + signinId: signin.id, + config, + }); + if (sessionInfo?.expiry) { + response.expiry = sessionInfo.expiry; + } + + await sendMessageTab(tabId, { + type: "tab", + subtype: "session-info", + data: response, + }); } - await sendMessageTab(tabId, { - type: "tab", - subtype: "session-info", - data: response, - }); resetTimeoutAlarm(); return response; @@ -320,23 +348,23 @@ const Signify = () => { }; /** - * Create a data attestation credential, it is an untargeted ACDC credential i.e. there is no issuee. - * - * @param origin - origin url from where request is being made -- required - * @param credData - credential data object containing the credential attributes -- required - * @param schemaSaid - SAID of the schema -- required - * @param signin - signin object containing identifier or credential -- required - * @returns Promise - returns a signed headers request object - */ + * Create a data attestation credential, it is an untargeted ACDC credential i.e. there is no issuee. + * + * @param origin - origin url from where request is being made -- required + * @param credData - credential data object containing the credential attributes -- required + * @param schemaSaid - SAID of the schema -- required + * @param signin - signin object containing identifier or credential -- required + * @returns Promise - returns a signed headers request object + */ const createAttestationCredential = async ({ origin, credData, schemaSaid, - tabId + tabId, }: { origin: string; - credData: any, - schemaSaid: string, + credData: any; + schemaSaid: string; tabId: number; }): Promise => { // in case the client is not connected, try to connect @@ -347,11 +375,16 @@ const Signify = () => { } const session = await sessionService.get({ tabId, origin }); - let { aid, registry, rules, edge } = await getCreateCredentialPrerequisites(session.aidName, schemaSaid); + let { aid, registry, rules, edge } = await getCreateCredentialPrerequisites( + session?.aidName!, + schemaSaid + ); if (isGroupAid(aid) === true) { - throw new Error(`Attestation credential issuance by multisig identifier ${session.aidName} is not supported yet!`); + throw new Error( + `Attestation credential issuance by multisig identifier ${session.aidName} is not supported yet!` + ); } - + let credArgs: CredentialData = { i: aid.prefix, ri: registry.regk, @@ -359,51 +392,61 @@ const Signify = () => { a: credData, r: rules ? Object.keys(rules).length > 0 - ? Saider.saidify({ d: '', ...rules })[1] + ? Saider.saidify({ d: "", ...rules })[1] : undefined : undefined, e: edge ? Object.keys(edge).length > 0 - ? Saider.saidify({ d: '', ...edge })[1] + ? Saider.saidify({ d: "", ...edge })[1] : undefined - : undefined - } + : undefined, + }; console.log("create credential args: ", credArgs); - let credResult = await createCredential(session.aidName, credArgs) + let credResult = await createCredential(session.aidName, credArgs); if (credResult && _client) { - await waitOperation(_client, credResult.op) + await waitOperation(_client, credResult.op); } return credResult; }; - const getCreateCredentialPrerequisites = async (aidName: string, schemaSaid: string): - Promise<{ aid: any | undefined; schema: any; registry: any, rules: any, edge: any }> => { + const getCreateCredentialPrerequisites = async ( + aidName: string, + schemaSaid: string + ): Promise<{ + aid: any | undefined; + schema: any; + registry: any; + rules: any; + edge: any; + }> => { const aid = await _client?.identifiers().get(aidName); - let registries = await _client?.registries().list(aidName) + let registries = await _client?.registries().list(aidName); if (registries == undefined || registries.length === 0) { throw new Error(`No credential registries found for the AID ${aidName}`); } - let schema = await _client?.schemas().get(schemaSaid) - if (!schema || schema?.title == '404 Not Found') { + let schema = await _client?.schemas().get(schemaSaid); + if (!schema || schema?.title == "404 Not Found") { throw new Error(`Schema not found!`); } - const edgeObject = parseSchemaEdgeOrRuleSection(schema.properties?.e) - let edge = formatAsCredentialEdgeOrRuleObject(edgeObject) - let edgeSchema = getSchemaFieldOfEdge(edge) + const edgeObject = parseSchemaEdgeOrRuleSection(schema.properties?.e); + let edge = formatAsCredentialEdgeOrRuleObject(edgeObject); + let edgeSchema = getSchemaFieldOfEdge(edge); if (edge && edgeSchema) { - let filter = { '-s': edgeSchema, '-a-i': aid?.prefix } - let creds = await _client?.credentials().list({ filter: filter, limit: 50 }) + let filter = { "-s": edgeSchema, "-a-i": aid?.prefix }; + let creds = await _client + ?.credentials() + .list({ filter: filter, limit: 50 }); if (creds && creds?.length > 0) { - edge = setNodeValueInEdge(edge, creds[0]?.sad.d) + edge = setNodeValueInEdge(edge, creds[0]?.sad.d); } } - let parsedRules = parseSchemaEdgeOrRuleSection(schema.properties?.r) - let rules = formatAsCredentialEdgeOrRuleObject(parsedRules) + let parsedRules = parseSchemaEdgeOrRuleSection(schema.properties?.r); + let rules = formatAsCredentialEdgeOrRuleObject(parsedRules); return { aid, schema, registry: registries[0], rules, edge }; }; @@ -424,17 +467,17 @@ const Signify = () => { name: string, args: CredentialData ): Promise => { - const result = await _client?.credentials().issue(name, args) - return result - } + const result = await _client?.credentials().issue(name, args); + return result; + }; const isGroupAid = (aid: any): boolean => { return ( - aid.hasOwnProperty('group') && - typeof aid.group === 'object' && + aid.hasOwnProperty("group") && + typeof aid.group === "object" && aid.group !== null - ) - } + ); + }; return { connect, @@ -451,7 +494,7 @@ const Signify = () => { authorizeSelectedSignin, getSessionInfo, removeSessionInfo, - createAttestationCredential + createAttestationCredential, }; }; diff --git a/src/pages/content/dialog/Dialog.tsx b/src/pages/content/dialog/Dialog.tsx index d173291..6fbc680 100644 --- a/src/pages/content/dialog/Dialog.tsx +++ b/src/pages/content/dialog/Dialog.tsx @@ -71,6 +71,7 @@ interface IDialog { autoSigninObjExists?: boolean; requestId: string; rurl: string; + sessionOneTime: boolean; } const StyledRequestor = styled(Flex)` @@ -94,6 +95,7 @@ export function Dialog({ handleRemove, requestId, rurl, + sessionOneTime, }: IDialog): JSX.Element { const { formatMessage } = useIntl(); const [sessionTime, setSessionTime] = useState(5); @@ -144,13 +146,13 @@ export function Dialog({ const { data, error } = await sendMessage<{ rurl: string; signin: ISignin; - config: { sessionTime: number; maxReq: number }; + config: { sessionOneTime: boolean }; }>({ type: CS_EVENTS.authentication_get_auth_data, data: { rurl, signin: selectedSignin, - config: { sessionTime, maxReq }, + config: { sessionOneTime }, }, }); @@ -224,9 +226,6 @@ export function Dialog({ <> - - {formatMessage({ id: "signin.disclaimer" })} - {eventType !== TAB_STATE.NONE ? ( ))} {signins?.length ? ( - -
- setSessionTime(Number(e.target.value))} - /> - setMaxReq(Number(e.target.value))} - /> -
- -
+ + + + {getHostnameFromUrl(tabUrl) + + (sessionOneTime + ? " is requesting a credential for one time request." + : " is requesting a credential. By signing in, you allow selected credential to sign subsequent requests ")} + + + + + + ) : null} )} diff --git a/src/pages/content/index.tsx b/src/pages/content/index.tsx index a24c78b..c06d1aa 100644 --- a/src/pages/content/index.tsx +++ b/src/pages/content/index.tsx @@ -14,6 +14,7 @@ import { SessionInfo } from "./session-info/session-info"; var tabState = TAB_STATE.NONE; let requestId = ""; let rurl = ""; +let sessionOneTime = false; // Advertize extensionId to web page window.postMessage( @@ -35,6 +36,7 @@ window.addEventListener( return; } console.log("Content script received from web page: " + event.data.type); + console.log("Here", event.data); if (event.data.type) { switch (event.data.type) { case TAB_STATE.SELECT_IDENTIFIER: @@ -62,6 +64,7 @@ window.addEventListener( requestId = event?.data?.requestId ?? ""; rurl = event?.data?.rurl ?? rurl; + sessionOneTime = event?.data?.payload?.session?.oneTime ?? sessionOneTime; insertDialog( data.isConnected, data.tabUrl, @@ -70,7 +73,8 @@ window.addEventListener( tabSigninResp?.data?.autoSigninObj, respVendorData?.data?.vendorData, requestId, - rurl + rurl, + sessionOneTime ); break; case TAB_STATE.CONFIGURE_VENDOR: @@ -331,6 +335,7 @@ browser.runtime.onMessage.addListener(async function ( message.eventType, message.schema ); + insertDialog( data.isConnected, data.tabUrl, @@ -339,7 +344,8 @@ browser.runtime.onMessage.addListener(async function ( tabSigninResp?.data?.autoSigninObj, respVendorData?.data?.vendorData, requestId, - rurl + rurl, + sessionOneTime ); } } @@ -380,7 +386,8 @@ function insertDialog( autoSigninObj: any, vendorData: any, requestId: string, - rurl: string + rurl: string, + sessionOneTime: boolean ) { let rootContainer = document.querySelector("#__root"); @@ -405,6 +412,7 @@ function insertDialog( handleRemove={resetTabState} requestId={requestId} rurl={rurl} + sessionOneTime={sessionOneTime} /> );