UNPKG

@reclaimprotocol/js-sdk

Version:

Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.

1,249 lines (1,227 loc) 177 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // package.json var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "@reclaimprotocol/js-sdk", version: "5.8.2", description: "Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.", main: "dist/index.js", types: "dist/index.d.ts", exports: { ".": { types: "./dist/index.d.ts", default: "./dist/index.js" }, "./internal": { types: "./dist/internal.d.ts", default: "./dist/internal.js" } }, keywords: [ "reclaim", "protocol", "blockchain", "proof", "verification", "identity", "claims", "witness", "sdk", "javascript", "typescript", "decentralized", "web3" ], files: [ "dist" ], tsup: { entry: [ "src/index.ts", "src/internal.ts" ], splitting: false, sourcemap: true, clean: true }, scripts: { build: "sh scripts/build.sh", prepare: "npm run build", release: "release-it", test: "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", commitlint: "commitlint --edit" }, repository: { type: "git", url: "https://github.com/reclaimprotocol/reclaim-js-sdk" }, author: "ali <ali@creatoros.co>", license: "See License in <https://github.com/reclaimprotocol/.github/blob/main/LICENSE>", bugs: { url: "https://github.com/reclaimprotocol/reclaim-js-sdk/issues" }, homepage: "https://github.com/reclaimprotocol/reclaim-js-sdk/", publishConfig: { registry: "https://registry.npmjs.org/", access: "public" }, "release-it": { git: { commitMessage: "chore: release ${version}", tagName: "v${version}" }, npm: { publish: true, tag: "latest" }, github: { release: true }, plugins: { "@release-it/conventional-changelog": { preset: "angular" } } }, devDependencies: { "@commitlint/cli": "^17.7.1", "@commitlint/config-conventional": "^17.7.0", "@release-it/conventional-changelog": "10.0.6", "@types/jest": "^30.0.0", "@types/node-forge": "^1.3.14", "@types/qrcode": "^1.5.5", "@types/qs": "^6.9.11", "@types/url-parse": "^1.4.11", "@types/uuid": "^9.0.7", jest: "^30.1.3", "jest-environment-jsdom": "^30.1.2", qs: "^6.11.2", "release-it": "^19.2.4", "ts-jest": "^29.4.1", tsup: "^8.0.1", typescript: "^5.3.3" }, dependencies: { canonicalize: "^2.0.0", ethers: "^6.9.1", "fetch-retry": "^6.0.0", "node-forge": "^1.3.3", qrcode: "^1.5.4", "url-parse": "^1.5.10", uuid: "^9.0.1" }, overrides: { "@conventional-changelog/git-client": "^2.0.0" } }; } }); // src/index.ts var index_exports = {}; __export(index_exports, { ApplicationError: () => ApplicationError, AttestorTeeVerificationError: () => AttestorTeeVerificationError, BackendServerError: () => BackendServerError, CallbackUrlRequiredError: () => CallbackUrlRequiredError, ErrorDuringVerificationError: () => ErrorDuringVerificationError, GetAppCallbackUrlError: () => GetAppCallbackUrlError, GetStatusUrlError: () => GetStatusUrlError, HASH_MATCH_MULTIPLE_DEFAULT: () => HASH_MATCH_MULTIPLE_DEFAULT, HASH_REQUIRED_DEFAULT: () => HASH_REQUIRED_DEFAULT, InavlidParametersError: () => InavlidParametersError, InitError: () => InitError, InitSessionError: () => InitSessionError, InvalidParamError: () => InvalidParamError, InvalidRequestSpecError: () => InvalidRequestSpecError, InvalidSignatureError: () => InvalidSignatureError, NoProviderParamsError: () => NoProviderParamsError, ProofNotValidatedError: () => ProofNotValidatedError, ProofNotVerifiedError: () => ProofNotVerifiedError, ProofSubmissionFailedError: () => ProofSubmissionFailedError, ProviderConfigFetchError: () => ProviderConfigFetchError, ProviderFailedError: () => ProviderFailedError, ProviderNotFoundError: () => ProviderNotFoundError, ReclaimProofRequest: () => ReclaimProofRequest, SessionNotStartedError: () => SessionNotStartedError, SetContextError: () => SetContextError, SetParamsError: () => SetParamsError, SetSignatureError: () => SetSignatureError, SignatureGeneratingError: () => SignatureGeneratingError, SignatureNotFoundError: () => SignatureNotFoundError, StatusUrlError: () => StatusUrlError, TeeVerificationError: () => TeeVerificationError, TimeoutError: () => TimeoutError, UnknownProofsNotValidatedError: () => UnknownProofsNotValidatedError, UpdateSessionError: () => UpdateSessionError, assertValidProofsByHash: () => assertValidProofsByHash, assertValidateProof: () => assertValidateProof, assertVerifiedProof: () => assertVerifiedProof, clearDeviceCache: () => clearDeviceCache, createLinkWithTemplateData: () => createLinkWithTemplateData, createSignDataForClaim: () => createSignDataForClaim, fetchProviderConfigs: () => fetchProviderConfigs, fetchProviderHashRequirementsBy: () => fetchProviderHashRequirementsBy, fetchStatusUrl: () => fetchStatusUrl, generateAttestationNonce: () => generateAttestationNonce, generateInitSignature: () => generateInitSignature, generateSpecsFromRequestSpecTemplate: () => generateSpecsFromRequestSpecTemplate, getAttestors: () => getAttestors, getDeviceType: () => getDeviceType, getHashFromProof: () => getHashFromProof, getHttpProviderClaimParamsFromProof: () => getHttpProviderClaimParamsFromProof, getIdentifierFromClaimInfo: () => getIdentifierFromClaimInfo, getMobileDeviceType: () => getMobileDeviceType, getProviderHashRequirementSpecFromProviderConfig: () => getProviderHashRequirementSpecFromProviderConfig, getProviderHashRequirementsFromSpec: () => getProviderHashRequirementsFromSpec, getProviderParamsAsCanonicalizedString: () => getProviderParamsAsCanonicalizedString, getShortenedUrl: () => getShortenedUrl, hashProofClaimParams: () => hashProofClaimParams, hashRequestSpec: () => hashRequestSpec, initSession: () => initSession, isDesktopDevice: () => isDesktopDevice, isHttpProviderClaimParams: () => isHttpProviderClaimParams, isMobileDevice: () => isMobileDevice, recoverSignersOfSignedClaim: () => recoverSignersOfSignedClaim, runTeeVerification: () => runTeeVerification, takePairsWhereValueIsArray: () => takePairsWhereValueIsArray, takeTemplateParametersFromProofs: () => takeTemplateParametersFromProofs, transformForOnchain: () => transformForOnchain, updateSession: () => updateSession, verifyAttestorTeeAttestation: () => verifyAttestorTeeAttestation, verifyProof: () => verifyProof, verifyTeeAttestation: () => verifyTeeAttestation }); module.exports = __toCommonJS(index_exports); // src/utils/interfaces.ts var RECLAIM_EXTENSION_ACTIONS = { CHECK_EXTENSION: "RECLAIM_EXTENSION_CHECK", EXTENSION_RESPONSE: "RECLAIM_EXTENSION_RESPONSE", START_VERIFICATION: "RECLAIM_START_VERIFICATION", STATUS_UPDATE: "RECLAIM_STATUS_UPDATE" }; // src/Reclaim.ts var import_ethers7 = require("ethers"); var import_canonicalize3 = __toESM(require("canonicalize")); // src/utils/errors.ts function createErrorClass(name) { return class extends Error { constructor(message, innerError) { const fullMessage = innerError ? `${message || ""} caused by ${innerError && typeof innerError === "object" && "name" in innerError ? innerError.name : "Error"}: ${innerError && typeof innerError === "object" && "message" in innerError ? innerError.message : String(innerError)}` : message; super(fullMessage); this.innerError = innerError; this.name = name; if (innerError) { this.stack += ` Caused by: ${innerError && typeof innerError === "object" && "stack" in innerError ? innerError.stack : String(innerError)}`; } } }; } var TimeoutError = createErrorClass("TimeoutError"); var ProofNotVerifiedError = createErrorClass("ProofNotVerifiedError"); var ProofNotValidatedError = createErrorClass("ProofNotValidatedError"); var InvalidRequestSpecError = createErrorClass("InvalidRequestSpecError"); var UnknownProofsNotValidatedError = createErrorClass("UnknownProofsNotValidatedError"); var SessionNotStartedError = createErrorClass("SessionNotStartedError"); var ProviderNotFoundError = createErrorClass("ProviderNotFoundError"); var SignatureGeneratingError = createErrorClass("SignatureGeneratingError"); var SignatureNotFoundError = createErrorClass("SignatureNotFoundError"); var InvalidSignatureError = createErrorClass("InvalidSignatureError"); var UpdateSessionError = createErrorClass("UpdateSessionError"); var InitSessionError = createErrorClass("InitSessionError"); var ProviderFailedError = createErrorClass("ProviderFailedError"); var InvalidParamError = createErrorClass("InvalidParamError"); var ApplicationError = createErrorClass("ApplicationError"); var InitError = createErrorClass("InitError"); var BackendServerError = createErrorClass("BackendServerError"); var GetStatusUrlError = createErrorClass("GetStatusUrlError"); var NoProviderParamsError = createErrorClass("NoProviderParamsError"); var SetParamsError = createErrorClass("SetParamsError"); var SetContextError = createErrorClass("SetContextError"); var SetSignatureError = createErrorClass("SetSignatureError"); var GetAppCallbackUrlError = createErrorClass("GetAppCallbackUrlError"); var StatusUrlError = createErrorClass("StatusUrlError"); var ProviderConfigFetchError = createErrorClass("ProviderConfigFetchError"); var InavlidParametersError = createErrorClass("InavlidParametersError"); var ProofSubmissionFailedError = createErrorClass("ProofSubmissionFailedError"); var ErrorDuringVerificationError = createErrorClass("ErrorDuringVerificationError"); var CallbackUrlRequiredError = createErrorClass("CallbackUrlRequiredError"); var TeeVerificationError = createErrorClass("TeeVerificationError"); var AttestorTeeVerificationError = createErrorClass("AttestorTeeVerificationError"); // src/utils/logger.ts var SimpleLogger = class { constructor() { this.level = "info"; } setLevel(level) { this.level = level; } shouldLog(messageLevel) { const levels = ["error", "warn", "info", "silent"]; return levels.indexOf(this.level) >= levels.indexOf(messageLevel); } log(level, message, ...args) { if (this.shouldLog(level) && this.level !== "silent") { const logFunction = this.getLogFunction(level); console.log("current level", this.level); logFunction(`[${level.toUpperCase()}]`, message, ...args); } } getLogFunction(level) { switch (level) { case "error": return console.error; case "warn": return console.warn; case "info": return console.info; default: return () => { }; } } info(message, ...args) { this.log("info", message, ...args); } warn(message, ...args) { this.log("warn", message, ...args); } error(message, ...args) { this.log("error", message, ...args); } }; var logger = new SimpleLogger(); function setLogLevel(level) { logger.setLevel(level); } var logger_default = { logger, setLogLevel }; // src/utils/validationUtils.ts var import_ethers = require("ethers"); var import_canonicalize2 = __toESM(require("canonicalize")); // src/utils/strings.ts var import_canonicalize = __toESM(require("canonicalize")); function canonicalStringify(params) { if (!params) { return ""; } return (0, import_canonicalize.default)(params) || ""; } // src/utils/validationUtils.ts var logger2 = logger_default.logger; function validateFunctionParams(params, functionName) { params.forEach(({ input, paramName, isString }) => { if (input == null) { logger2.info(`Validation failed: ${paramName} in ${functionName} is null or undefined`); throw new InvalidParamError(`${paramName} passed to ${functionName} must not be null or undefined.`); } if (isString && typeof input !== "string") { logger2.info(`Validation failed: ${paramName} in ${functionName} is not a string`); throw new InvalidParamError(`${paramName} passed to ${functionName} must be a string.`); } if (isString && input.trim() === "") { logger2.info(`Validation failed: ${paramName} in ${functionName} is an empty string`); throw new InvalidParamError(`${paramName} passed to ${functionName} must not be an empty string.`); } }); } function validateFunctionParamsWithFn(param, functionName) { if (!param.isValid()) { logger2.info(`Validation failed: ${param.paramName} in ${functionName} is not valid`); throw new InvalidParamError(`${param.paramName} passed to ${functionName} must be valid.`); } } function validateParameters(parameters) { try { if (typeof parameters !== "object" || parameters === null) { logger2.info(`Parameters validation failed: Provided parameters is not an object`); throw new InavlidParametersError(`The provided parameters is not an object`); } for (const [key, value] of Object.entries(parameters)) { if (typeof key !== "string" || typeof value !== "string") { logger2.info(`Parameters validation failed: Provided parameters is not an object of key value pairs of string and string`); throw new InavlidParametersError(`The provided parameters is not an object of key value pairs of string and string`); } } } catch (e) { logger2.info(`Parameters validation failed: ${e.message}`); throw new InavlidParametersError(`Invalid parameters passed to validateParameters.`, e); } } function validateURL(url, functionName) { try { new URL(url); } catch (e) { logger2.info(`URL validation failed for ${url} in ${functionName}: ${e.message}`); throw new InvalidParamError(`Invalid URL format ${url} passed to ${functionName}.`, e); } } function validateRedirectionMethod(method, functionName) { try { if (method === null || method === void 0) { return; } if (method !== "GET" && method !== "POST") { throw new Error(`Invalid redirection method ${method} passed to ${functionName}.`); } } catch (e) { logger2.info(`Redirection method validation failed for ${method} in ${functionName}: ${e.message}`); throw new InvalidParamError(`Invalid redirection method ${method} passed to ${functionName}.`, e); } } function validateRedirectionBody(records, functionName) { try { if (records === null || records === void 0) { return; } if (Array.isArray(records)) { for (const record of records) { if ("name" in record && record.name && typeof record.name === "string") { if ("value" in record && typeof record.value === "string") { continue; } } throw new Error("Record in form entries do not have a valid name and/or value"); } } else { throw new Error("Redirection body must be an array of objects with name, and value"); } } catch (e) { logger2.info(`Redirection body validation failed for ${records} in ${functionName}: ${e.message}`); throw new InvalidParamError(`Invalid redirection body ${records} passed to ${functionName}.`, e); } } function validateSignature(providerId, signature, applicationId, timestamp) { try { logger2.info(`Starting signature validation for providerId: ${providerId}, applicationId: ${applicationId}, timestamp: ${timestamp}`); const message = (0, import_canonicalize2.default)({ providerId, timestamp }); if (!message) { logger2.info("Failed to canonicalize message for signature validation"); throw new Error("Failed to canonicalize message"); } const messageHash = import_ethers.ethers.keccak256(new TextEncoder().encode(message)); let appId = import_ethers.ethers.verifyMessage( import_ethers.ethers.getBytes(messageHash), import_ethers.ethers.hexlify(signature) ).toLowerCase(); if (import_ethers.ethers.getAddress(appId) !== import_ethers.ethers.getAddress(applicationId)) { logger2.info(`Signature validation failed: Mismatch between derived appId (${appId}) and provided applicationId (${applicationId})`); throw new InvalidSignatureError(`Signature does not match the application id: ${appId}`); } logger2.info(`Signature validated successfully for applicationId: ${applicationId}`); } catch (err) { logger2.info(`Signature validation failed: ${err.message}`); if (err instanceof InvalidSignatureError) { throw err; } throw new InvalidSignatureError(`Failed to validate signature: ${err.message}`); } } function validateContext(context) { if (context && !("contextAddress" in context)) { try { validateFunctionParams([ { input: context, paramName: "context", isString: false } ], "validateContext"); JSON.parse(canonicalStringify(context)); return; } catch (e) { logger2.info(`Context validation failed: Provided JSON serializable context is not valid`); throw new InvalidParamError(`The provided context is not valid`); } } if (!context.contextAddress) { logger2.info(`Context validation failed: Provided context address in context is not valid`); throw new InvalidParamError(`The provided context address in context is not valid`); } if (!context.contextMessage) { logger2.info(`Context validation failed: Provided context message in context is not valid`); throw new InvalidParamError(`The provided context message in context is not valid`); } validateFunctionParams([ { input: context.contextAddress, paramName: "contextAddress", isString: true }, { input: context.contextMessage, paramName: "contextMessage", isString: true } ], "validateContext"); } function validateModalOptions(modalOptions, functionName, paramPrefix = "") { if (modalOptions.title !== void 0) { validateFunctionParams([ { input: modalOptions.title, paramName: `${paramPrefix}title`, isString: true } ], functionName); } if (modalOptions.description !== void 0) { validateFunctionParams([ { input: modalOptions.description, paramName: `${paramPrefix}description`, isString: true } ], functionName); } if (modalOptions.extensionUrl !== void 0) { validateURL(modalOptions.extensionUrl, functionName); validateFunctionParams([ { input: modalOptions.extensionUrl, paramName: `${paramPrefix}extensionUrl`, isString: true } ], functionName); } if (modalOptions.darkTheme !== void 0) { if (typeof modalOptions.darkTheme !== "boolean") { throw new InvalidParamError(`${paramPrefix}darkTheme prop must be a boolean`); } validateFunctionParams([ { input: modalOptions.darkTheme, paramName: `${paramPrefix}darkTheme` } ], functionName); } if (modalOptions.modalPopupTimer !== void 0) { if (typeof modalOptions.modalPopupTimer !== "number" || modalOptions.modalPopupTimer <= 0 || !Number.isInteger(modalOptions.modalPopupTimer)) { throw new InvalidParamError(`${paramPrefix}modalPopupTimer prop must be a valid time in minutes`); } validateFunctionParams([ { input: modalOptions.modalPopupTimer, paramName: `${paramPrefix}modalPopupTimer` } ], functionName); } if (modalOptions.showExtensionInstallButton !== void 0) { if (typeof modalOptions.showExtensionInstallButton !== "boolean") { throw new InvalidParamError(`${paramPrefix}showExtensionInstallButton prop must be a boolean`); } validateFunctionParams([ { input: modalOptions.showExtensionInstallButton, paramName: `${paramPrefix}showExtensionInstallButton` } ], functionName); } } function hashObject(o) { try { const canonicalData = canonicalStringify(o); const messageHash = import_ethers.ethers.keccak256(new TextEncoder().encode(canonicalData)); return messageHash; } catch (e) { logger2.info(`Failed to hash object: ${e.message}`); throw new Error(`Failed to hash object: ${e.message}`); } } // src/utils/helper.ts var logger3 = logger_default.logger; function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function replaceAll(str, find, replace) { if (find === "") return str; return str.replace(new RegExp(escapeRegExp(find), "g"), replace); } function scheduleIntervalEndingTask(sessionId, intervals, onFailureCallback, timeout = 1e3 * 60 * 10) { setTimeout(() => { if (intervals.has(sessionId)) { const message = "Interval ended without receiving proofs"; onFailureCallback(new TimeoutError(message)); logger3.info(message); clearInterval(intervals.get(sessionId)); intervals.delete(sessionId); } }, timeout); } var createVerifyProofResultSuccess = (proofs, isTeeAttestationVerified, isAttestorTeeAttestationVerified) => { return { isVerified: true, isTeeAttestationVerified, isAttestorTeeAttestationVerified, error: void 0, data: proofs.map(createTrustedDataFromProofData), publicData: getPublicDataFromProofs(proofs) }; }; var createVerifyProofResultFailure = (error, isTeeAttestationVerified, isAttestorTeeAttestationVerified) => { return { isVerified: false, isTeeAttestationVerified, isAttestorTeeAttestationVerified, error, data: [], publicData: [] }; }; function createTrustedDataFromProofData(proof) { try { const context = JSON.parse(proof.claimData.context); const _a = context, { extractedParameters } = _a, rest = __objRest(_a, ["extractedParameters"]); return { context: rest, extractedParameters: extractedParameters != null ? extractedParameters : {} }; } catch (e) { return { context: {}, extractedParameters: {} }; } } function getPublicDataFromProofs(proofs) { const data = []; const seenData = /* @__PURE__ */ new Set(); for (const proof of proofs) { const publicData = proof.publicData; if (publicData === null || publicData === void 0) { continue; } try { const hash = hashObject(publicData); if (seenData.has(hash)) { continue; } seenData.add(hash); } catch (_) { } data.push(publicData); } return data; } // src/utils/constants.ts var BACKEND_BASE_URL = "https://api.reclaimprotocol.org"; function setBackendBaseUrl(url) { BACKEND_BASE_URL = url; } var constants = { // Default callback URL for Reclaim protocol get DEFAULT_RECLAIM_CALLBACK_URL() { return `${BACKEND_BASE_URL}/api/sdk/callback?callbackId=`; }, // Default error callback URL for Reclaim protocol get DEFAULT_RECLAIM_CANCEL_CALLBACK_URL() { return `${BACKEND_BASE_URL}/api/sdk/error-callback?callbackId=`; }, // Default status URL for Reclaim sessions get DEFAULT_RECLAIM_STATUS_URL() { return `${BACKEND_BASE_URL}/api/sdk/session/`; }, // Default attestors URL for Reclaim sessions get DEFAULT_ATTESTORS_URL() { return `${BACKEND_BASE_URL}/api/attestors`; }, DEFAULT_PROVIDER_CONFIGS_URL(providerId, exactProviderVersionString, allowedTags) { return `${BACKEND_BASE_URL}/api/providers/${providerId}/configs?versionNumber=${exactProviderVersionString || ""}&allowedTags=${(allowedTags == null ? void 0 : allowedTags.join(",")) || ""}`; }, // Default portal URL DEFAULT_PORTAL_URL: "https://portal.reclaimprotocol.org", // Default sharepage URL DEFAULT_APP_SHARE_PAGE_URL: "https://share.reclaimprotocol.org/verifier", // URL for sharing Reclaim templates RECLAIM_SHARE_URL: "https://share.reclaimprotocol.org/verifier/?template=", // Chrome extension URL for Reclaim Protocol CHROME_EXTENSION_URL: "https://chromewebstore.google.com/detail/reclaim-extension/oafieibbbcepkmenknelhmgaoahamdeh" }; var GCP_CONFIDENTIAL_SPACE_ROOT_CA = `-----BEGIN CERTIFICATE----- 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 uyT5CnJulxSC5CT1 -----END CERTIFICATE-----`; var ATTESTOR_NONCE_PATTERN = /^attestor_public_key:0x([0-9a-fA-F]{40})$/; var GCP_CONFIDENTIAL_SPACE_ISSUER = "https://confidentialcomputing.googleapis.com"; // src/utils/fetch.ts var import_fetch_retry = __toESM(require("fetch-retry")); var MAX_RETRIES = 3; var MAX_RETRY_DELAY_MS = 60 * 1e3; var isHttpResponseStatusRetryable = (statusCode) => { return statusCode === 408 || statusCode === 429 || statusCode >= 500; }; var getRetryDelay = (response) => { const retryAfter = response == null ? void 0 : response.headers.get("Retry-After"); if (!retryAfter) { return void 0; } const trimmed = retryAfter.trim(); if (/^\d+$/.test(trimmed)) { return Math.min(parseInt(trimmed, 10) * 1e3, MAX_RETRY_DELAY_MS); } const date = new Date(trimmed); if (!isNaN(date.getTime())) { return Math.min(Math.max(0, date.getTime() - Date.now()), MAX_RETRY_DELAY_MS); } return void 0; }; var http = { get client() { return (0, import_fetch_retry.default)(globalThis.fetch, { retries: MAX_RETRIES, retryDelay: function(attempt, _, response) { const delay = getRetryDelay(response); if (delay !== void 0) { return delay; } return Math.pow(2, attempt) * 1e3; }, retryOn: (attempt, error, response) => { if (attempt >= MAX_RETRIES) { return false; } if (response && Number.isInteger(response.status)) { return isHttpResponseStatusRetryable(response.status); } return !!error && error.name !== "AbortError"; } }); } }; // src/utils/sessionUtils.ts var logger4 = logger_default.logger; function initSession(providerId, appId, timestamp, signature, versionNumber, orgId) { return __async(this, null, function* () { logger4.info(`Initializing session for providerId: ${providerId}, appId: ${appId}`); try { const response = yield http.client(`${BACKEND_BASE_URL}/api/sdk/init/session/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(__spreadValues({ providerId, appId, timestamp, signature, versionNumber }, orgId ? { orgId } : {})) }); const res = yield response.json(); if (!response.ok) { logger4.info(`Session initialization failed: ${res.message || "Unknown error"}`); throw new InitSessionError(res.message || `Error initializing session with providerId: ${providerId}`); } return res; } catch (err) { logger4.info(`Failed to initialize session for providerId: ${providerId}, appId: ${appId}`, err); throw err; } }); } function updateSession(sessionId, status, orgId) { return __async(this, null, function* () { logger4.info(`Updating session status for sessionId: ${sessionId}, new status: ${status}`); validateFunctionParams( [{ input: sessionId, paramName: "sessionId", isString: true }], "updateSession" ); try { const response = yield http.client(`${BACKEND_BASE_URL}/api/sdk/update/session/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(__spreadValues({ sessionId, status }, orgId ? { orgId } : {})) }); const res = yield response.json(); if (!response.ok) { const errorMessage = `Error updating session with sessionId: ${sessionId}. Status Code: ${response.status}`; logger4.info(errorMessage, res); throw new UpdateSessionError(errorMessage); } logger4.info(`Session status updated successfully for sessionId: ${sessionId}`); return res; } catch (err) { const errorMessage = `Failed to update session with sessionId: ${sessionId}`; logger4.info(errorMessage, err); throw new UpdateSessionError(`Error updating session with sessionId: ${sessionId}`); } }); } function fetchStatusUrl(sessionId) { return __async(this, null, function* () { validateFunctionParams( [{ input: sessionId, paramName: "sessionId", isString: true }], "fetchStatusUrl" ); try { const response = yield http.client(`${constants.DEFAULT_RECLAIM_STATUS_URL}${sessionId}`, { method: "GET", headers: { "Content-Type": "application/json" } }); const res = yield response.json(); if (!response.ok) { const errorMessage = `Error fetching status URL for sessionId: ${sessionId}. Status Code: ${response.status}`; logger4.info(errorMessage, res); throw new StatusUrlError(errorMessage); } return res; } catch (err) { const errorMessage = `Failed to fetch status URL for sessionId: ${sessionId}`; logger4.info(errorMessage, err); throw new StatusUrlError(`Error fetching status URL for sessionId: ${sessionId}`); } }); } function fetchProviderConfigs(providerId, exactProviderVersionString, allowedTags) { return __async(this, null, function* () { validateFunctionParams( [ { input: providerId, paramName: "providerId", isString: true } ], "fetchProviderConfigs" ); if (exactProviderVersionString != null && exactProviderVersionString != void 0) { validateFunctionParams( [ { input: exactProviderVersionString, paramName: "exactProviderVersionString", isString: true } ], "fetchProviderConfigs" ); } try { const response = yield http.client(constants.DEFAULT_PROVIDER_CONFIGS_URL(providerId, exactProviderVersionString, allowedTags), { method: "GET", headers: { "Content-Type": "application/json" } }); const res = yield response.json(); if (!response.ok) { const errorMessage = `Error fetching provider config for providerId: ${providerId}, exactProviderVersionString: ${exactProviderVersionString}. Status Code: ${response.status}`; logger4.info(errorMessage, res); throw new ProviderConfigFetchError(errorMessage); } return res; } catch (err) { const errorMessage = `Failed to fetch provider config for providerId: ${providerId}, exactProviderVersionString: ${exactProviderVersionString}`; logger4.info(errorMessage, err); throw new ProviderConfigFetchError(`Error fetching provider config for providerId: ${providerId}, exactProviderVersionString: ${exactProviderVersionString}`); } }); } // src/utils/proofUtils.ts var import_ethers3 = require("ethers"); // src/witness.ts var import_ethers2 = require("ethers"); function createSignDataForClaim(data) { const identifier = getIdentifierFromClaimInfo(data); const lines = [ identifier, data.owner.toLowerCase(), data.timestampS.toString(), data.epoch.toString() ]; return lines.join("\n"); } function getIdentifierFromClaimInfo(info) { let canonicalContext = info.context || ""; if (canonicalContext.length > 0) { try { const ctx = JSON.parse(canonicalContext); canonicalContext = canonicalStringify(ctx); } catch (e) { throw new Error("unable to parse non-empty context. Must be JSON"); } } const str = `${info.provider} ${info.parameters} ${canonicalContext}`; return import_ethers2.ethers.keccak256(strToUint8Array(str)).toLowerCase(); } function hashProofClaimParams(params) { const serializedParams = getProviderParamsAsCanonicalizedString(params); if (Array.isArray(serializedParams)) { return serializedParams.map( (serialized) => import_ethers2.ethers.keccak256(strToUint8Array(serialized)).toLowerCase() ); } return import_ethers2.ethers.keccak256( strToUint8Array(serializedParams) ).toLowerCase(); } function strToUint8Array(str) { return new TextEncoder().encode(str); } function getProviderParamsAsCanonicalizedString(params) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o; const pairsCount = (_b = (_a = params == null ? void 0 : params.responseMatches) == null ? void 0 : _a.length) != null ? _b : 0; const validCanonicalizedStrings = []; const totalCombinations = 1 << pairsCount; for (let i = 0; i < totalCombinations; i++) { let isValidCombination = true; let includedCount = 0; const currentMatches = []; const currentRedactions = []; for (let j = 0; j < pairsCount; j++) { const isIncluded = (i & 1 << j) !== 0; const match = (_c = params == null ? void 0 : params.responseMatches) == null ? void 0 : _c[j]; const redaction = (_d = params == null ? void 0 : params.responseRedactions) == null ? void 0 : _d[j]; if (isIncluded) { if (match) { currentMatches.push({ value: (_e = match.value) != null ? _e : "", // This needs to be explicitly specified and absence should cause error, but we're choosing to ignore it in this case type: (_f = match.type) != null ? _f : "contains", invert: match.invert || void 0 }); } if (redaction) { currentRedactions.push({ xPath: (_g = redaction.xPath) != null ? _g : "", jsonPath: (_h = redaction.jsonPath) != null ? _h : "", regex: (_i = redaction.regex) != null ? _i : "", hash: redaction.hash || void 0 }); } includedCount++; } else { if (match && !match.isOptional) { isValidCombination = false; break; } } } if (isValidCombination && includedCount > 0) { const filteredParams = { url: (_j = params == null ? void 0 : params.url) != null ? _j : "", // METHOD needs to be explicitly specified and absence or unknown method should cause error, but we're choosing to ignore it in this case method: (_k = params == null ? void 0 : params.method) != null ? _k : "GET", body: (_l = params == null ? void 0 : params.body) != null ? _l : "", responseMatches: currentMatches, responseRedactions: currentRedactions }; validCanonicalizedStrings.push(canonicalStringify(filteredParams)); } } if (validCanonicalizedStrings.length === 0) { const filteredParams = { url: (_m = params == null ? void 0 : params.url) != null ? _m : "", method: (_n = params == null ? void 0 : params.method) != null ? _n : "GET", body: (_o = params == null ? void 0 : params.body) != null ? _o : "", responseMatches: [], responseRedactions: [] }; return [canonicalStringify(filteredParams)]; } return validCanonicalizedStrings; } // src/utils/proofUtils.ts var logger5 = logger_default.logger; function getShortenedUrl(url) { return __async(this, null, function* () { logger5.info(`Attempting to shorten URL: ${url}`); try { validateURL(url, "getShortenedUrl"); const response = yield http.client(`${BACKEND_BASE_URL}/api/sdk/shortener`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fullUrl: url }) }); const res = yield response.json(); if (!response.ok) { logger5.info(`Failed to shorten URL: status=${response.status}`); return url; } const shortenedVerificationUrl = res.result.shortUrl; return shortenedVerificationUrl; } catch (err) { logger5.info(`Error shortening URL: ${url}, Error: ${err}`); return url; } }); } function createLinkWithTemplateData(templateData, sharePagePath) { return __async(this, null, function* () { let template = encodeURIComponent(JSON.stringify(templateData)); template = replaceAll(template, "(", "%28"); template = replaceAll(template, ")", "%29"); const fullLink = sharePagePath ? `${sharePagePath}/?template=${template}` : `${constants.RECLAIM_SHARE_URL}${template}`; try { const shortenedLink = yield getShortenedUrl(fullLink); return shortenedLink; } catch (err) { logger5.info(`Error creating link for sessionId: ${templateData.sessionId}, Error: ${err}`); return fullLink; } }); } function getAttestors() { return __async(this, null, function* () { var _a; const response = yield http.client(constants.DEFAULT_ATTESTORS_URL); if (!response.ok) { (_a = response.body) == null ? void 0 : _a.cancel(); throw new BackendServerError( `Failed to fetch witness addresses: ${response.status}` ); } const { data } = yield response.json(); return data.map((wt) => ({ id: wt.address, url: "" })); }); } function recoverSignersOfSignedClaim({ claim, signatures }) { const dataStr = createSignDataForClaim(__spreadValues({}, claim)); const signers = signatures.map( (signature) => import_ethers3.ethers.verifyMessage(dataStr, import_ethers3.ethers.hexlify(signature)).toLowerCase() ); return signers; } function assertVerifiedProof(proof, attestors) { return __async(this, null, function* () { const signers = recoverSignersOfSignedClaim({ claim: proof.claimData, signatures: proof.signatures.map((signature) => import_ethers3.ethers.getBytes(signature)) }); if (!attestors.some((attestor) => signers.includes(attestor.id.toLowerCase()))) { throw new ProofNotVerifiedError("Identifier mismatch"); } }); } // src/utils/modalUtils.ts var import_qrcode = __toESM(require("qrcode")); var logger6 = logger_default.logger; var QRCodeModal = class { constructor(options = {}) { this.countdownSeconds = 60; this.modalId = "reclaim-qr-modal"; this.options = __spreadValues({ title: "Verify with Reclaim", description: "Scan the QR code with your mobile device to complete verification", extensionUrl: constants.CHROME_EXTENSION_URL, darkTheme: false, modalPopupTimer: 1, // default to 1 minute showExtensionInstallButton: false }, options); } show(requestUrl) { return __async(this, null, function* () { try { this.close(); const modalHTML = this.createModalHTML(); document.body.insertAdjacentHTML("beforeend", modalHTML); yield this.generateQRCode(requestUrl, "reclaim-qr-code"); this.addEventListeners(); this.startAutoCloseTimer(); } catch (error) { logger6.info("Error showing QR code modal:", error); throw error; } }); } close() { if (this.autoCloseTimer) { clearTimeout(this.autoCloseTimer); this.autoCloseTimer = void 0; } if (this.countdownTimer) { clearInterval(this.countdownTimer); this.countdownTimer = void 0; } const modal = document.getElementById(this.modalId); if (modal) { modal.remove(); } if (this.options.onClose) { this.options.onClose(); } } getThemeStyles() { const isDark = this.options.darkTheme; return { modalBackground: isDark ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.5)", cardBackground: isDark ? "#1f2937" : "white", titleColor: isDark ? "#f9fafb" : "#1f2937", textColor: isDark ? "#d1d5db" : "#6b7280", qrBackground: isDark ? "#374151" : "#f9fafb", tipBackground: isDark ? "#1e40af" : "#f0f9ff", tipBorder: isDark ? "#1e40af" : "#e0f2fe", tipTextColor: isDark ? "#dbeafe" : "#0369a1", buttonBackground: isDark ? "#374151" : "#f3f4f6", buttonColor: isDark ? "#f9fafb" : "#374151", buttonHoverBackground: isDark ? "#4b5563" : "#e5e7eb", countdownColor: isDark ? "#6b7280" : "#9ca3af", progressBackground: isDark ? "#4b5563" : "#e5e7eb", progressGradient: isDark ? "linear-gradient(90deg, #3b82f6 0%, #2563eb 50%, #1d4ed8 100%)" : "linear-gradient(90deg, #2563eb 0%, #1d4ed8 50%, #1e40af 100%)", linkColor: isDark ? "#60a5fa" : "#2563eb", extensionButtonBackground: isDark ? "#1e40af" : "#2563eb", extensionButtonHover: isDark ? "#1d4ed8" : "#1d4ed8" }; } createModalHTML() { const styles = this.getThemeStyles(); return ` <div id="${this.modalId}" style=" position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: ${styles.modalBackground}; display: flex; justify-content: center; align-items: center; z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; "> <div style=" background: ${styles.cardBackground}; border-radius: 12px; padding: 32px; max-width: 400px; width: 90%; text-align: center; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); position: relative; "> <button id="reclaim-close-modal" style=" position: absolute; top: 16px; right: 16px; background: none; border: none; cursor: pointer; padding: 4px; border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: background-color 0.2s; width: 32px; height: 32px; " onmouseover="this.style.backgroundColor='${styles.buttonHoverBackground}'" onmouseout="this.style.backgroundColor='transparent'" title="Close modal"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 4L4 12M4 4L12 12" stroke="${styles.buttonColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <h2 style=" margin: 0 0 16px 0; font-size: 24px; font-weight: 600; color: ${styles.titleColor}; ">${this.options.title}</h2> <p style=" margin: 0 0 24px 0; color: ${styles.textColor}; font-size: 14px; line-height: 1.5; ">${this.options.description}</p> <div id="reclaim-qr-code" style=" margin: 0 auto 24px auto; background: ${styles.qrBackground}; border-radius: 8px; display: inline-block; "></div> ${this.options.showExtensionInstallButton ? ` <div style=" margin-bottom: 24px; padding: 16px; background: ${styles.tipBackground}; border: 1px solid ${styles.tipBorder}; border-radius: 8px; "> <p style=" margin: 0 0 12px 0; font-size: 14px; color: ${styles.tipTextColor}; font-weight: 500; ">\u{1F4A1} For a better experience</p> <p style=" margin: 0 0 12px 0; font-size: 13px; color: ${styles.tipTextColor}; line-height: 1.4; ">Install our browser extension for seamless verification without QR codes</p> <a href="${this.options.extensionUrl}" target="_blank" style=" display: inline-block; background: ${styles.extensionButtonBackground};