tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

helper.js (8277B)


      1 // @ts-check
      2 // Import the types from the TypeScript file
      3 /**
      4 * @typedef {import('../dc-types').GetProtocol} GetProtocol
      5 * @typedef {import('../dc-types').DigitalCredentialGetRequest} DigitalCredentialGetRequest
      6 * @typedef {import('../dc-types').CredentialRequestOptions} CredentialRequestOptions
      7 * @typedef {import('../dc-types').CreateProtocol} CreateProtocol
      8 * @typedef {import('../dc-types').DigitalCredentialCreateRequest} DigitalCredentialCreateRequest
      9 * @typedef {import('../dc-types').CredentialCreationOptions} CredentialCreationOptions
     10 * @typedef {import('../dc-types').SendMessageData} SendMessageData
     11 * @typedef {import('../dc-types').MakeGetOptionsConfig} MakeGetOptionsConfig
     12 * @typedef {import('../dc-types').MakeCreateOptionsConfig} MakeCreateOptionsConfig
     13 * @typedef {import('../dc-types').CredentialMediationRequirement} CredentialMediationRequirement
     14 * @typedef {import('../dc-types').MobileDocumentRequest} MobileDocumentRequest
     15 * @typedef {GetProtocol | CreateProtocol} Protocol
     16 */
     17 
     18 /** @type {Record<Protocol, object | MobileDocumentRequest>} */
     19 const CANONICAL_REQUEST_OBJECTS = {
     20  openid4vci: {
     21    /* Canonical object coming soon */
     22  },
     23  "openid4vp-v1-unsigned": {
     24    /* Canonical object coming soon */
     25  },
     26  "openid4vp-v1-signed": {
     27    /* Canonical object coming soon */
     28  },
     29  "openid4vp-v1-multisigned": {
     30    /* Canonical object coming soon */
     31  },
     32  /** @type MobileDocumentRequest **/
     33  "org-iso-mdoc": {
     34    deviceRequest:
     35      "omd2ZXJzaW9uYzEuMGtkb2NSZXF1ZXN0c4GhbGl0ZW1zUmVxdWVzdNgYWIKiZ2RvY1R5cGV1b3JnLmlzby4xODAxMy41LjEubURMam5hbWVTcGFjZXOhcW9yZy5pc28uMTgwMTMuNS4x9pWthZ2Vfb3Zlcl8yMfRqZ2l2ZW5fbmFtZfRrZmFtaWx5X25hbWX0cmRyaXZpbmdfcHJpdmlsZWdlc_RocG9ydHJhaXT0",
     36    encryptionInfo:
     37      "gmVkY2FwaaJlbm9uY2VYICBetSsDkKlE_G9JSIHwPzr3ctt6Ol9GgmCH8iGdGQNJcnJlY2lwaWVudFB1YmxpY0tleaQBAiABIVggKKm1iPeuOb9bDJeeJEL4QldYlWvY7F_K8eZkmYdS9PwiWCCm9PLEmosiE_ildsE11lqq4kDkjhfQUKPpbX-Hm1ZSLg",
     38  },
     39 };
     40 
     41 /**
     42 * Internal helper to create final options from a list of requests.
     43 *
     44 * @template {DigitalCredentialGetRequest[] | DigitalCredentialCreateRequest[]} TRequests
     45 * @template {CredentialRequestOptions | CredentialCreationOptions} TOptions
     46 * @param {TRequests} requests
     47 * @param {CredentialMediationRequirement} [mediation]
     48 * @param {AbortSignal} [signal]
     49 * @returns {TOptions}
     50 */
     51 function makeOptionsFromRequests(requests, mediation, signal) {
     52  /** @type {TOptions} */
     53  const options = /** @type {TOptions} */ ({ digital: { requests } });
     54 
     55  if (mediation) {
     56    options.mediation = mediation;
     57  }
     58 
     59  if (signal) {
     60    options.signal = signal;
     61  }
     62 
     63  return options;
     64 }
     65 
     66 /**
     67 * Build requests from protocols, using canonical data for each protocol.
     68 * For create operations with explicit data, uses that data for all protocols.
     69 *
     70 * @template Req
     71 * @param {Protocol[]} protocols
     72 * @param {Record<string, (data?: MobileDocumentRequest | object) => Req>} mapping
     73 * @param {MobileDocumentRequest | object} [explicitData] - Explicit data for create operations
     74 * @returns {Req[]}
     75 * @throws {Error} If an unknown protocol string is encountered.
     76 */
     77 function buildRequestsFromProtocols(protocols, mapping, explicitData) {
     78  return protocols.map((protocol) => {
     79    if (!(protocol in mapping)) {
     80      throw new Error(`Unknown request type within array: ${protocol}`);
     81    }
     82    // Use explicit data if provided (for create with data), otherwise canonical data
     83    return mapping[protocol](explicitData);
     84  });
     85 }
     86 
     87 /** @type {{
     88 *   get: Record<GetProtocol, (data?: MobileDocumentRequest | object) => DigitalCredentialGetRequest>;
     89 *   create: Record<CreateProtocol, (data?: object) => DigitalCredentialCreateRequest>;
     90 * }} */
     91 const allMappings = {
     92  get: {
     93    "org-iso-mdoc": (
     94      data = { ...CANONICAL_REQUEST_OBJECTS["org-iso-mdoc"] },
     95    ) => {
     96      return { protocol: "org-iso-mdoc", data };
     97    },
     98    "openid4vp-v1-unsigned": (
     99      data = { ...CANONICAL_REQUEST_OBJECTS["openid4vp-v1-unsigned"] },
    100    ) => {
    101      return { protocol: "openid4vp-v1-unsigned", data };
    102    },
    103    "openid4vp-v1-signed": (
    104      data = { ...CANONICAL_REQUEST_OBJECTS["openid4vp-v1-signed"] },
    105    ) => {
    106      return { protocol: "openid4vp-v1-signed", data };
    107    },
    108    "openid4vp-v1-multisigned": (
    109      data = { ...CANONICAL_REQUEST_OBJECTS["openid4vp-v1-multisigned"] },
    110    ) => {
    111      return { protocol: "openid4vp-v1-multisigned", data };
    112    },
    113  },
    114  create: {
    115    "openid4vci": (data = { ...CANONICAL_REQUEST_OBJECTS["openid4vci"] }) => {
    116      return { protocol: "openid4vci", data };
    117    },
    118  },
    119 };
    120 
    121 /**
    122 * Generic helper to create credential options from config with protocol already set.
    123 * @template {MakeGetOptionsConfig | MakeCreateOptionsConfig} TConfig
    124 * @template {DigitalCredentialGetRequest | DigitalCredentialCreateRequest} TRequest
    125 * @template {CredentialRequestOptions | CredentialCreationOptions} TOptions
    126 * @param {TConfig} config - Configuration options with protocol already defaulted
    127 * @param {Record<string, (data?: MobileDocumentRequest | object) => TRequest>} mapping - Protocol to request mapping
    128 * @returns {TOptions}
    129 */
    130 function makeCredentialOptionsFromConfig(config, mapping) {
    131  const { protocol, requests = [], data, mediation, signal } = config;
    132 
    133  // Validate that we have either a protocol or requests
    134  if (!protocol && !requests?.length) {
    135    throw new Error("No protocol. Can't make options.");
    136  }
    137 
    138  /** @type {TRequest[]} */
    139  const  allRequests = [];
    140 
    141  allRequests.push(.../** @type {TRequest[]} */ (requests));
    142 
    143  if (protocol) {
    144    const protocolArray = Array.isArray(protocol) ? protocol : [protocol];
    145    const protocolRequests = buildRequestsFromProtocols(protocolArray, mapping, data);
    146    allRequests.push(...protocolRequests);
    147  }
    148 
    149  return /** @type {TOptions} */ (makeOptionsFromRequests(allRequests, mediation, signal));
    150 }
    151 
    152 /**
    153 * Creates options for getting credentials.
    154 * @export
    155 * @param {MakeGetOptionsConfig} [config={}] - Configuration options
    156 * @returns {CredentialRequestOptions}
    157 */
    158 export function makeGetOptions(config = {}) {
    159  /** @type {MakeGetOptionsConfig} */
    160  const configWithDefaults = {
    161    protocol: ["openid4vp-v1-unsigned", "org-iso-mdoc"],
    162    ...config,
    163  };
    164 
    165  return /** @type {CredentialRequestOptions} */ (
    166    makeCredentialOptionsFromConfig(configWithDefaults, allMappings.get)
    167  );
    168 }
    169 
    170 /**
    171 * Creates options for creating credentials.
    172 * @export
    173 * @param {MakeCreateOptionsConfig} [config={}] - Configuration options
    174 * @returns {CredentialCreationOptions}
    175 */
    176 export function makeCreateOptions(config = {}) {
    177  /** @type {MakeCreateOptionsConfig} */
    178  const configWithDefaults = {
    179    protocol: "openid4vci",
    180    ...config,
    181  };
    182 
    183  return /** @type {CredentialCreationOptions} */ (
    184    makeCredentialOptionsFromConfig(configWithDefaults, allMappings.create)
    185  );
    186 }
    187 
    188 /**
    189 * Sends a message to an iframe and return the response.
    190 *
    191 * @param {HTMLIFrameElement} iframe - The iframe element to send the message to.
    192 * @param {SendMessageData} data - The data to be sent to the iframe.
    193 * @returns {Promise<any>} - A promise that resolves with the response from the iframe.
    194 */
    195 export function sendMessage(iframe, data) {
    196  return new Promise((resolve, reject) => {
    197    if (!iframe.contentWindow) {
    198      reject(
    199        new Error(
    200          "iframe.contentWindow is undefined, cannot send message (something is wrong with the test that called this).",
    201        ),
    202      );
    203      return;
    204    }
    205    window.addEventListener("message", function messageListener(event) {
    206      if (event.source === iframe.contentWindow) {
    207        window.removeEventListener("message", messageListener);
    208        resolve(event.data);
    209      }
    210    });
    211    iframe.contentWindow.postMessage(data, "*");
    212  });
    213 }
    214 
    215 /**
    216 * Load an iframe with the specified URL and wait for it to load.
    217 *
    218 * @param {HTMLIFrameElement} iframe
    219 * @param {string|URL} url
    220 * @returns {Promise<void>}
    221 */
    222 export function loadIframe(iframe, url) {
    223  return new Promise((resolve, reject) => {
    224    iframe.addEventListener("load", () => resolve(), { once: true });
    225    iframe.addEventListener("error", (event) => reject(event.error), {
    226      once: true,
    227    });
    228    if (!iframe.isConnected) {
    229      document.body.appendChild(iframe);
    230    }
    231    iframe.src = url.toString();
    232  });
    233 }