diff --git a/package-lock.json b/package-lock.json index 433afce4..c59dac30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cheqd/credential-service", - "version": "2.11.0-develop.2", + "version": "2.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/credential-service", - "version": "2.11.0-develop.2", + "version": "2.11.0", "license": "Apache-2.0", "dependencies": { "@cheqd/did-provider-cheqd": "^3.6.11", @@ -55,7 +55,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^9.2.2", + "@semantic-release/github": "^9.2.3", "@semantic-release/npm": "^11.0.1", "@semantic-release/release-notes-generator": "^12.1.0", "@types/cookie-parser": "^1.4.6", @@ -79,7 +79,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-config-typescript": "^3.0.0", "jest": "^29.7.0", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "semantic-release": "^22.0.7", "swagger-jsdoc": "^6.2.8", "ts-jest": "^29.1.1", @@ -4064,9 +4064,9 @@ } }, "node_modules/@expo/cli": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.10.14.tgz", - "integrity": "sha512-IIZ9mYYHpNkK9XJAWLPtwTwZmasDq/NJsHLPjLtw5la4ANjWWwKYUcl3XKBECKovSDn9WHEQHGsBz6cyKS88Mg==", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.10.15.tgz", + "integrity": "sha512-CIpfIB5oB/s/op6Ke5M7TI4/yOi5raTR9ps9UD+ExazonTDAzEtXANVWmAR7Z4+wUyqycniWxTpICcaxri2a3A==", "optional": true, "peer": true, "dependencies": { @@ -5163,9 +5163,9 @@ } }, "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", "engines": { "node": ">=14" } @@ -5236,6 +5236,11 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@ipld/dag-cbor": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz", @@ -6631,9 +6636,9 @@ } }, "node_modules/@logto/client": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@logto/client/-/client-2.2.3.tgz", - "integrity": "sha512-xq4LhQ6ItbukAHgsMDcgfspTpdpO5sSfSEugpOrGP/nLwzGTfBO78OSUfMdBQEDr5+3SRmONuSjUBBwssOLINA==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@logto/client/-/client-2.2.4.tgz", + "integrity": "sha512-IcB+bDScybd6TyXKHN4/TMm81Hhy9Q39OkAbrjzwFVVKorq5wA3JfvVgJUhbznkdyvcKLfezK3lK6V230mT15g==", "dependencies": { "@logto/js": "^2.1.3", "@silverhand/essentials": "^2.6.2", @@ -7001,12 +7006,12 @@ "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.3.tgz", - "integrity": "sha512-gm4KmW+pdAfCO5cXJyRZnNfnPE9r6OGpRG8JZpI0eSo1XVk7LXoRcdS7aP4L9azdV0ncHazsLAI0knKjr+snPg==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.4.tgz", + "integrity": "sha512-MvZx4WvfhBnt7PtH5XE7HORsO7bBk4er1FgRIUr1qJ89NR2I6bWjGyKsxk8z42FPQ34hFQm0Baanh4gzdZR4gQ==", "dev": true, "dependencies": { - "@octokit/types": "^12.2.0" + "@octokit/types": "^12.3.0" }, "engines": { "node": ">= 18" @@ -7079,14 +7084,305 @@ } }, "node_modules/@octokit/types": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.2.0.tgz", - "integrity": "sha512-ZkdHqHJdifVndN7Pha10+qrgAjy3AcG//Vmjr/o5UFuTiYCcMhqDj39Yr9VM9zJ/42KO2xAYhV7cvLnLI9Kvwg==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.3.0.tgz", + "integrity": "sha512-nJ8X2HRr234q3w/FcovDlA+ttUU4m1eJAourvfUUtwAWeqL8AsyRqfnLvVnYn3NFbUnsmzQCzLNdFerPwdmcDQ==", "dev": true, "dependencies": { "@octokit/openapi-types": "^19.0.2" } }, + "node_modules/@parcel/watcher": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.3.0.tgz", + "integrity": "sha512-pW7QaFiL11O0BphO+bq3MgqeX/INAk9jgBldVDYjlQPO4VddoZnF22TcF9onMhnLVHuNqBJeRf+Fj7eezi/+rQ==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.3.0", + "@parcel/watcher-darwin-arm64": "2.3.0", + "@parcel/watcher-darwin-x64": "2.3.0", + "@parcel/watcher-freebsd-x64": "2.3.0", + "@parcel/watcher-linux-arm-glibc": "2.3.0", + "@parcel/watcher-linux-arm64-glibc": "2.3.0", + "@parcel/watcher-linux-arm64-musl": "2.3.0", + "@parcel/watcher-linux-x64-glibc": "2.3.0", + "@parcel/watcher-linux-x64-musl": "2.3.0", + "@parcel/watcher-win32-arm64": "2.3.0", + "@parcel/watcher-win32-ia32": "2.3.0", + "@parcel/watcher-win32-x64": "2.3.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.3.0.tgz", + "integrity": "sha512-f4o9eA3dgk0XRT3XhB0UWpWpLnKgrh1IwNJKJ7UJek7eTYccQ8LR7XUWFKqw6aEq5KUNlCcGvSzKqSX/vtWVVA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.3.0.tgz", + "integrity": "sha512-mKY+oijI4ahBMc/GygVGvEdOq0L4DxhYgwQqYAz/7yPzuGi79oXrZG52WdpGA1wLBPrYb0T8uBaGFo7I6rvSKw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.3.0.tgz", + "integrity": "sha512-20oBj8LcEOnLE3mgpy6zuOq8AplPu9NcSSSfyVKgfOhNAc4eF4ob3ldj0xWjGGbOF7Dcy1Tvm6ytvgdjlfUeow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.3.0.tgz", + "integrity": "sha512-7LftKlaHunueAEiojhCn+Ef2CTXWsLgTl4hq0pkhkTBFI3ssj2bJXmH2L67mKpiAD5dz66JYk4zS66qzdnIOgw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.3.0.tgz", + "integrity": "sha512-1apPw5cD2xBv1XIHPUlq0cO6iAaEUQ3BcY0ysSyD9Kuyw4MoWm1DV+W9mneWI+1g6OeP6dhikiFE6BlU+AToTQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.3.0.tgz", + "integrity": "sha512-mQ0gBSQEiq1k/MMkgcSB0Ic47UORZBmWoAWlMrTW6nbAGoLZP+h7AtUM7H3oDu34TBFFvjy4JCGP43JlylkTQA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.3.0.tgz", + "integrity": "sha512-LXZAExpepJew0Gp8ZkJ+xDZaTQjLHv48h0p0Vw2VMFQ8A+RKrAvpFuPVCVwKJCr5SE+zvaG+Etg56qXvTDIedw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.3.0.tgz", + "integrity": "sha512-P7Wo91lKSeSgMTtG7CnBS6WrA5otr1K7shhSjKHNePVmfBHDoAOHYRXgUmhiNfbcGk0uMCHVcdbfxtuiZCHVow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.3.0.tgz", + "integrity": "sha512-+kiRE1JIq8QdxzwoYY+wzBs9YbJ34guBweTK8nlzLKimn5EQ2b2FSC+tAOpq302BuIMjyuUGvBiUhEcLIGMQ5g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.3.0.tgz", + "integrity": "sha512-ejBAX8H0ZGsD8lSICDNyMbSEtPMWgDL0WFCt/0z7hyf5v8Imz4rAM8xY379mBsECkq/Wdqa5WEDLqtjZ+6NxfA==", + "bundleDependencies": [ + "napi-wasm" + ], + "dependencies": { + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "napi-wasm": "^1.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.3.0.tgz", + "integrity": "sha512-35gXCnaz1AqIXpG42evcoP2+sNL62gZTMZne3IackM+6QlfMcJLy3DrjuL6Iks7Czpd3j4xRBzez3ADCj1l7Aw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.3.0.tgz", + "integrity": "sha512-FJS/IBQHhRpZ6PiCjFt1UAcPr0YmCLHRbTc00IBTrelEjlmmgIVLeOx4MSXzx2HFEy5Jo5YdhGpxCuqCyDJ5ow==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.3.0.tgz", + "integrity": "sha512-dLx+0XRdMnVI62kU3wbXvbIRhLck4aE28bIGKbRGS7BJNt54IIj9+c/Dkqb+7DJEbHUZAX1bwaoM8PqVlHJmCA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" + }, "node_modules/@peculiar/asn1-schema": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", @@ -7234,21 +7530,21 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@react-native-community/cli": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz", - "integrity": "sha512-Ou8eDlF+yh2rzXeCTpMPYJ2fuqsusNOhmpYPYNQJQ2h6PvaF30kPomflgRILems+EBBuggRtcT+I+1YH4o/q6w==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.10.tgz", + "integrity": "sha512-bIx0t5s9ewH1PlcEcuQUD+UnVrCjPGAfjhVR5Gew565X60nE+GTIHRn70nMv9G4he/amBF+Z+vf5t8SNZEWMwg==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-clean": "11.3.7", - "@react-native-community/cli-config": "11.3.7", - "@react-native-community/cli-debugger-ui": "11.3.7", - "@react-native-community/cli-doctor": "11.3.7", - "@react-native-community/cli-hermes": "11.3.7", - "@react-native-community/cli-plugin-metro": "11.3.7", - "@react-native-community/cli-server-api": "11.3.7", - "@react-native-community/cli-tools": "11.3.7", - "@react-native-community/cli-types": "11.3.7", + "@react-native-community/cli-clean": "11.3.10", + "@react-native-community/cli-config": "11.3.10", + "@react-native-community/cli-debugger-ui": "11.3.10", + "@react-native-community/cli-doctor": "11.3.10", + "@react-native-community/cli-hermes": "11.3.10", + "@react-native-community/cli-plugin-metro": "11.3.10", + "@react-native-community/cli-server-api": "11.3.10", + "@react-native-community/cli-tools": "11.3.10", + "@react-native-community/cli-types": "11.3.10", "chalk": "^4.1.2", "commander": "^9.4.1", "execa": "^5.0.0", @@ -7266,26 +7562,26 @@ } }, "node_modules/@react-native-community/cli-clean": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.7.tgz", - "integrity": "sha512-twtsv54ohcRyWVzPXL3F9VHGb4Qhn3slqqRs3wEuRzjR7cTmV2TIO2b1VhaqF4HlCgNd+cGuirvLtK2JJyaxMg==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.10.tgz", + "integrity": "sha512-g6QjW+DSqoWRHzmIQW3AH22k1AnynWuOdy2YPwYEGgPddTeXZtJphIpEVwDOiC0L4mZv2VmiX33/cGNUwO0cIA==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "execa": "^5.0.0", "prompts": "^2.4.0" } }, "node_modules/@react-native-community/cli-config": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.7.tgz", - "integrity": "sha512-FDBLku9xskS+bx0YFJFLCmUJhEZ4/MMSC9qPYOGBollWYdgE7k/TWI0IeYFmMALAnbCdKQAYP5N29N55Tad8lg==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.10.tgz", + "integrity": "sha512-YYu14nm1JYLS6mDRBz78+zDdSFudLBFpPkhkOoj4LuBhNForQBIqFFHzQbd9/gcguJxfW3vlYSnudfaUI7oGLg==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "cosmiconfig": "^5.1.0", "deepmerge": "^4.3.0", @@ -7294,9 +7590,9 @@ } }, "node_modules/@react-native-community/cli-debugger-ui": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.7.tgz", - "integrity": "sha512-aVmKuPKHZENR8SrflkMurZqeyLwbKieHdOvaZCh1Nn/0UC5CxWcyST2DB2XQboZwsvr3/WXKJkSUO+SZ1J9qTQ==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.10.tgz", + "integrity": "sha512-kyitGV3RsjlXIioq9lsuawha2GUBPCTAyXV6EBlm3qlyF3dMniB3twEvz+fIOid/e1ZeucH3Tzy5G3qcP8yWoA==", "optional": true, "peer": true, "dependencies": { @@ -7304,16 +7600,16 @@ } }, "node_modules/@react-native-community/cli-doctor": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.7.tgz", - "integrity": "sha512-YEHUqWISOHnsl5+NM14KHelKh68Sr5/HeEZvvNdIcvcKtZic3FU7Xd1WcbNdo3gCq5JvzGFfufx02Tabh5zmrg==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.10.tgz", + "integrity": "sha512-DpMsfCWKZ15L9nFK/SyDvpl5v6MjV+arMHMC1i8kR+DOmf2xWmp/pgMywKk0/u50yGB9GwxBHt3i/S/IMK5Ylg==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-config": "11.3.7", - "@react-native-community/cli-platform-android": "11.3.7", - "@react-native-community/cli-platform-ios": "11.3.7", - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-config": "11.3.10", + "@react-native-community/cli-platform-android": "11.3.10", + "@react-native-community/cli-platform-ios": "11.3.10", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "command-exists": "^1.2.8", "envinfo": "^7.7.2", @@ -7507,27 +7803,27 @@ "peer": true }, "node_modules/@react-native-community/cli-hermes": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.7.tgz", - "integrity": "sha512-chkKd8n/xeZkinRvtH6QcYA8rjNOKU3S3Lw/3Psxgx+hAYV0Gyk95qJHTalx7iu+PwjOOqqvCkJo5jCkYLkoqw==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.10.tgz", + "integrity": "sha512-vqINuzAlcHS9ImNwJtT43N7kfBQ7ro9A8O1Gpc5TQ0A8V36yGG8eoCHeauayklVVgMZpZL6f6mcoLLr9IOgBZQ==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-platform-android": "11.3.7", - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-platform-android": "11.3.10", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "hermes-profile-transformer": "^0.0.6", "ip": "^1.1.5" } }, "node_modules/@react-native-community/cli-platform-android": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.7.tgz", - "integrity": "sha512-WGtXI/Rm178UQb8bu1TAeFC/RJvYGnbHpULXvE20GkmeJ1HIrMjkagyk6kkY3Ej25JAP2R878gv+TJ/XiRhaEg==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.10.tgz", + "integrity": "sha512-RGu9KuDIXnrcNkacSHj5ETTQtp/D/835L6veE2jMigO21p//gnKAjw3AVLCysGr8YXYfThF8OSOALrwNc94puQ==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "execa": "^5.0.0", "glob": "^7.1.3", @@ -7535,13 +7831,13 @@ } }, "node_modules/@react-native-community/cli-platform-ios": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.7.tgz", - "integrity": "sha512-Z/8rseBput49EldX7MogvN6zJlWzZ/4M97s2P+zjS09ZoBU7I0eOKLi0N9wx+95FNBvGQQ/0P62bB9UaFQH2jw==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.10.tgz", + "integrity": "sha512-JjduMrBM567/j4Hvjsff77dGSLMA0+p9rr0nShlgnKPcc+0J4TDy0hgWpUceM7OG00AdDjpetAPupz0kkAh4cQ==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-xml-parser": "^4.0.12", @@ -7683,14 +7979,14 @@ } }, "node_modules/@react-native-community/cli-plugin-metro": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.7.tgz", - "integrity": "sha512-0WhgoBVGF1f9jXcuagQmtxpwpfP+2LbLZH4qMyo6OtYLWLG13n2uRep+8tdGzfNzl1bIuUTeE9yZSAdnf9LfYQ==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.10.tgz", + "integrity": "sha512-ZYAc5Hc+QVqJgj1XFbpKnIPbSJ9xKcBnfQrRhR+jFyt2DWx85u4bbzY1GSVc/USs0UbSUXv4dqPbnmOJz52EYQ==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-server-api": "11.3.7", - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-server-api": "11.3.10", + "@react-native-community/cli-tools": "11.3.10", "chalk": "^4.1.2", "execa": "^5.0.0", "metro": "0.76.8", @@ -7703,14 +7999,14 @@ } }, "node_modules/@react-native-community/cli-server-api": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.7.tgz", - "integrity": "sha512-yoFyGdvR3HxCnU6i9vFqKmmSqFzCbnFSnJ29a+5dppgPRetN+d//O8ard/YHqHzToFnXutAFf2neONn23qcJAg==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.10.tgz", + "integrity": "sha512-WEwHWIpqx3gA6Da+lrmq8+z78E1XbxxjBlvHAXevhjJj42N4SO417eZiiUVrFzEFVVJSUee9n9aRa0kUR+0/2w==", "optional": true, "peer": true, "dependencies": { - "@react-native-community/cli-debugger-ui": "11.3.7", - "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-debugger-ui": "11.3.10", + "@react-native-community/cli-tools": "11.3.10", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", @@ -7721,9 +8017,9 @@ } }, "node_modules/@react-native-community/cli-tools": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.7.tgz", - "integrity": "sha512-peyhP4TV6Ps1hk+MBHTFaIR1eI3u+OfGBvr5r0wPwo3FAJvldRinMgcB/TcCcOBXVORu7ba1XYjkubPeYcqAyA==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.10.tgz", + "integrity": "sha512-4kCuCwVcGagSrNg9vxMNVhynwpByuC/J5UnKGEet3HuqmoDhQW15m18fJXiehA8J+u9WBvHduefy9nZxO0C06Q==", "optional": true, "peer": true, "dependencies": { @@ -7908,9 +8204,9 @@ } }, "node_modules/@react-native-community/cli-types": { - "version": "11.3.7", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.7.tgz", - "integrity": "sha512-OhSr/TiDQkXjL5YOs8+hvGSB+HltLn5ZI0+A3DCiMsjUgTTsYh+Z63OtyMpNjrdCEFcg0MpfdU2uxstCS6Dc5g==", + "version": "11.3.10", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.10.tgz", + "integrity": "sha512-0FHK/JE7bTn0x1y8Lk5m3RISDHIBQqWLltO2Mf7YQ6cAeKs8iNOJOeKaHJEY+ohjsOyCziw+XSC4cY57dQrwNA==", "optional": true, "peer": true, "dependencies": { @@ -8173,9 +8469,9 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.2.tgz", - "integrity": "sha512-S9tTms9MSrFnW97inhNhEqQiTNmls1zgE3bCxLbWhom0vClRe43B9N9D5NVepTwbr7wXehXJG+ok8B8neG1bsA==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.3.tgz", + "integrity": "sha512-FAjXb1F84CVI6IG8fWi+XS9ErYD+s3MHkP03zBa3+GyUrV4kqwYu/WPppIciHxujGFR51SAWPkOY5rnH6ZlrxA==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", @@ -8186,7 +8482,7 @@ "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", - "globby": "^13.1.4", + "globby": "^14.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^6.0.0", @@ -8587,6 +8883,18 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", + "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", @@ -11459,31 +11767,23 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@walletconnect/keyvaluestorage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.0.2.tgz", - "integrity": "sha512-U/nNG+VLWoPFdwwKx0oliT4ziKQCEoQ27L5Hhw8YOFGA2Po9A9pULUYNWhDgHkrb0gYDNt//X7wABcEWWBd3FQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.0.tgz", + "integrity": "sha512-CjDBs1WmLGstYRoxWx9oAskTKj1deu7gvPycxZo2jYMa85hAYe762AITKWW1i2OJ8y9+5WJTGDAy3inVl8pjtw==", "dependencies": { - "safe-json-utils": "^1.1.1", - "tslib": "1.14.1" + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" }, "peerDependencies": { - "@react-native-async-storage/async-storage": "1.x", - "lokijs": "1.x" + "@react-native-async-storage/async-storage": "1.x" }, "peerDependenciesMeta": { "@react-native-async-storage/async-storage": { "optional": true - }, - "lokijs": { - "optional": true } } }, - "node_modules/@walletconnect/keyvaluestorage/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/@walletconnect/logger": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.0.1.tgz", @@ -11963,7 +12263,6 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -12183,7 +12482,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -12224,6 +12522,25 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", @@ -12838,6 +13155,14 @@ "node": ">=0.6" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -12987,7 +13312,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -13274,9 +13598,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001561", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", - "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "funding": [ { "type": "opencollective", @@ -13352,6 +13676,43 @@ "node": "*" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -13394,6 +13755,14 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/citty": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.4.tgz", + "integrity": "sha512-Q3bK1huLxzQrvj7hImJ7Z1vKYJRPQCDnd0EjXfHMidcjecGOMuLrmuQmtWmFkuKLcMThlGh1yCKG8IEc6VeNXQ==", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -13461,13 +13830,29 @@ "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "dependencies": { - "string-width": "^4.2.0" + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": "10.* || >= 12.*" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cliui": { @@ -13520,6 +13905,14 @@ "node": ">=0.10.0" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -13780,6 +14173,14 @@ "node": ">= 0.6" } }, + "node_modules/consola": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -13901,6 +14302,11 @@ "node": ">= 0.6" } }, + "node_modules/cookie-es": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz", + "integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ==" + }, "node_modules/cookie-parser": { "version": "1.4.6", "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", @@ -14204,7 +14610,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "devOptional": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -14610,6 +15015,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.3.tgz", + "integrity": "sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ==" + }, "node_modules/del": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", @@ -14684,6 +15094,14 @@ "optional": true, "peer": true }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -14693,15 +15111,15 @@ } }, "node_modules/deprecated-react-native-prop-types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-4.1.0.tgz", - "integrity": "sha512-WfepZHmRbbdTvhcolb8aOKEvQdcmTMn5tKLbqbXmkBvjFjRVWAYqsXk/DBsV8TZxws8SdGHLuHaJrHSQUPRdfw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-4.2.3.tgz", + "integrity": "sha512-2rLTiMKidIFFYpIVM69UnQKngLqQfL6I11Ch8wGSBftS18FUXda+o2we2950X+1dmbgps28niI3qwyH4eX3Z1g==", "optional": true, "peer": true, "dependencies": { - "@react-native/normalize-colors": "*", - "invariant": "*", - "prop-types": "*" + "@react-native/normalize-colors": "<0.73.0", + "invariant": "^2.2.4", + "prop-types": "^15.8.1" } }, "node_modules/deprecation": { @@ -14710,6 +15128,11 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, + "node_modules/destr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.2.tgz", + "integrity": "sha512-65AlobnZMiCET00KaFFjUefxDX0khFA/E4myqZ7a6Sq1yZtR8+FVIvilVX66vF2uobSumxooYZChiRPCKNqhmg==" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -14728,8 +15151,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "optional": true, - "peer": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -14960,9 +15381,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.579", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.579.tgz", - "integrity": "sha512-bJKvA+awBIzYR0xRced7PrQuRIwGQPpo6ZLP62GAShahU9fWpsNN2IP6BSP1BLDDSbxvBVRGAMWlvVVq3npmLA==" + "version": "1.4.582", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.582.tgz", + "integrity": "sha512-89o0MGoocwYbzqUUjc+VNpeOFSOK9nIdC5wY4N+PVUarUK0MtjyTjks75AZS2bW4Kl8MdewdFsWaH0jLy+JNoA==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -15317,9 +15738,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true, "peer": true }, @@ -15679,7 +16100,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "devOptional": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -15702,7 +16122,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "devOptional": true, "engines": { "node": ">=10" }, @@ -15736,14 +16155,14 @@ } }, "node_modules/expo": { - "version": "49.0.17", - "resolved": "https://registry.npmjs.org/expo/-/expo-49.0.17.tgz", - "integrity": "sha512-NZRvA7tqbAM2ZSAaXYVl1tuODLA5GfxY6+IJk23kcWdRclfG2Djc65pgCqJoAchyncKYfqEQDzuAw1q8fOncdQ==", + "version": "49.0.18", + "resolved": "https://registry.npmjs.org/expo/-/expo-49.0.18.tgz", + "integrity": "sha512-BrPtTxBlE7pFG1ZDi1fqq4pGbS5IcTg4bH9TTeUbJOTTs43W+QkXzsylmT0omf8nADOHGx9EFgufPneBcU1F1w==", "optional": true, "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "0.10.14", + "@expo/cli": "0.10.15", "@expo/config": "8.1.2", "@expo/config-plugins": "7.2.5", "@expo/vector-icons": "^13.0.0", @@ -16304,9 +16723,9 @@ } }, "node_modules/file-type": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.6.0.tgz", - "integrity": "sha512-uLqXnIAIyy8K9rnvdU9IYi3WIL+6qVBWn24kThYOPlnyU+6yrr2oarn+j7seMLh1wOEG4hEjRP6a30IiKR9OaA==", + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", @@ -16323,7 +16742,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -16575,9 +16993,9 @@ } }, "node_modules/flat-cache": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", - "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { "flatted": "^3.2.9", @@ -16585,7 +17003,7 @@ "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { @@ -16854,6 +17272,11 @@ "node": ">=4" } }, + "node_modules/get-port-please": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.1.tgz", + "integrity": "sha512-3UBAyM3u4ZBVYDsxOQfJDxEa6XTbpBDrOjp4mf7ExFRt5BKs/QywQQiJsh2B+hxcZLSapWqCRvElUe8DnKcFHA==" + }, "node_modules/get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", @@ -16987,19 +17410,32 @@ } }, "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", + "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", + "@sindresorhus/merge-streams": "^1.0.0", + "fast-glob": "^3.3.2", "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -17054,6 +17490,21 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/h3": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.8.2.tgz", + "integrity": "sha512-1Ca0orJJlCaiFY68BvzQtP2lKLk46kcLAxVM8JgYbtm2cUg6IY7pjpYgWMwUvDO9QI30N5JAukOKoT8KD3Q0PQ==", + "dependencies": { + "cookie-es": "^1.0.0", + "defu": "^6.1.2", + "destr": "^2.0.1", + "iron-webcrypto": "^0.10.1", + "radix3": "^1.1.0", + "ufo": "^1.3.0", + "uncrypto": "^0.1.3", + "unenv": "^1.7.4" + } + }, "node_modules/hamt-sharding": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/hamt-sharding/-/hamt-sharding-3.0.2.tgz", @@ -17329,6 +17780,15 @@ "node": ">= 14" } }, + "node_modules/http-shutdown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", + "integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, "node_modules/http-status-codes": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", @@ -17351,7 +17811,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "devOptional": true, "engines": { "node": ">=10.17.0" } @@ -17376,6 +17835,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -17648,6 +18112,29 @@ "loose-envify": "^1.0.0" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -17933,6 +18420,14 @@ "resolved": "https://registry.npmjs.org/it-all/-/it-all-1.0.6.tgz", "integrity": "sha512-3cmCc6Heqe3uWi3CVM/k51fa/XbMFpQVzFoDsV0IZNHSQDyAXl3c4MjHkFX5kF3922OGj7Myv1nSEUgRtcuM1A==" }, + "node_modules/iron-webcrypto": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-0.10.1.tgz", + "integrity": "sha512-QGOS8MRMnj/UiOa+aMIgfyHcvkhqNUsUxb1XzskENvbo+rEfp6TOwqd1KPuDzXC4OnGHcMSVxDGRoilqB8ViqA==", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -17954,6 +18449,17 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "devOptional": true }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -17998,8 +18504,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "optional": true, - "peer": true, "bin": { "is-docker": "cli.js" }, @@ -18019,7 +18523,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -18061,7 +18564,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -18144,7 +18646,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -18198,7 +18699,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "devOptional": true, "engines": { "node": ">=8" }, @@ -18261,8 +18761,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "optional": true, - "peer": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -18278,8 +18776,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "devOptional": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/iso-url": { "version": "1.2.1", @@ -19453,6 +19950,14 @@ "optional": true, "peer": true }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/joi": { "version": "17.11.0", "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", @@ -19693,6 +20198,11 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -20399,6 +20909,34 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "devOptional": true }, + "node_modules/listhen": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.5.5.tgz", + "integrity": "sha512-LXe8Xlyh3gnxdv4tSjTjscD1vpr/2PRpzq8YIaMJgyKzRG8wdISlWVWnGThJfHnlJ6hmLt2wq1yeeix0TEbuoA==", + "dependencies": { + "@parcel/watcher": "^2.3.0", + "@parcel/watcher-wasm": "2.3.0", + "citty": "^0.1.4", + "clipboardy": "^3.0.0", + "consola": "^3.2.3", + "defu": "^6.1.2", + "get-port-please": "^3.1.1", + "h3": "^1.8.1", + "http-shutdown": "^1.2.2", + "jiti": "^1.20.0", + "mlly": "^1.4.2", + "node-forge": "^1.3.1", + "pathe": "^1.1.1", + "std-env": "^3.4.3", + "ufo": "^1.3.0", + "untun": "^0.1.2", + "uqr": "^0.1.2" + }, + "bin": { + "listen": "bin/listhen.mjs", + "listhen": "bin/listhen.mjs" + } + }, "node_modules/lit": { "version": "2.7.6", "resolved": "https://registry.npmjs.org/lit/-/lit-2.7.6.tgz", @@ -20529,6 +21067,11 @@ "optional": true, "peer": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -20545,6 +21088,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -21000,9 +21548,9 @@ } }, "node_modules/marked": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.5.tgz", - "integrity": "sha512-14QG3shv8Kg/xc0Yh6TNkMj90wXH9mmldi5941I2OevfJ/FQAFLEwtwU2/FfgSAOMlWHrEukWSGQf8MiVYNG2A==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -21012,9 +21560,9 @@ } }, "node_modules/marked-terminal": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.0.0.tgz", - "integrity": "sha512-6rruICvqRfA4N+Mvdc0UyDbLA0A0nI5omtARIlin3P2F+aNc3EbW91Rd9HTuD0v9qWyHmNIu8Bt40gAnPfldsg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", + "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", "dev": true, "dependencies": { "ansi-escapes": "^6.2.0", @@ -21028,7 +21576,7 @@ "node": ">=16.0.0" }, "peerDependencies": { - "marked": ">=1 <10" + "marked": ">=1 <11" } }, "node_modules/marked-terminal/node_modules/ansi-escapes": { @@ -21168,8 +21716,7 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "devOptional": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "node_modules/merge2": { "version": "1.4.1", @@ -21863,7 +22410,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "devOptional": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -21876,7 +22422,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -21907,7 +22452,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "devOptional": true, "engines": { "node": ">=6" } @@ -22040,6 +22584,17 @@ "node": ">=10" } }, + "node_modules/mlly": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", + "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", + "dependencies": { + "acorn": "^8.10.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.3.0" + } + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -22062,6 +22617,14 @@ "@motionone/vue": "^10.16.2" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -22410,12 +22973,15 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.4.1.tgz", + "integrity": "sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==" + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "optional": true, - "peer": true, "engines": { "node": ">= 6.13.0" } @@ -22612,10 +23178,13 @@ } }, "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", + "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, "engines": { "node": "14 || >=16.14" } @@ -22624,7 +23193,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -22825,7 +23393,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "devOptional": true, "dependencies": { "path-key": "^3.0.0" }, @@ -25597,6 +26164,16 @@ "node": ">= 0.4" } }, + "node_modules/ofetch": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.3.3.tgz", + "integrity": "sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==", + "dependencies": { + "destr": "^2.0.1", + "node-fetch-native": "^1.4.0", + "ufo": "^1.3.0" + } + }, "node_modules/on-exit-leak-free": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", @@ -25633,7 +26210,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "devOptional": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -26160,7 +26736,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "devOptional": true, "engines": { "node": ">=8" } @@ -26185,6 +26760,11 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==" + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -26304,7 +26884,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, "engines": { "node": ">=8.6" }, @@ -26509,6 +27088,16 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/pkg-up": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", @@ -27005,9 +27594,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -27528,6 +28117,11 @@ "node": ">= 6" } }, + "node_modules/radix3": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.0.tgz", + "integrity": "sha512-pNsHDxbGORSvuSScqNJ+3Km6QAVqk8CfsCBIEoDgpqLrkD2f3QM4I7d1ozJJ172OmIcoUcerZaNWqtLkRXTV3A==" + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -27634,16 +28228,16 @@ "peer": true }, "node_modules/react-native": { - "version": "0.72.6", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.6.tgz", - "integrity": "sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==", + "version": "0.72.7", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.7.tgz", + "integrity": "sha512-dqVFojOO9rOvyFbbM3/v9/GJR355OSuBhEY4NQlMIRc2w0Xch5MT/2uPoq3+OvJ+5h7a8LFAco3fucSffG0FbA==", "optional": true, "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.2.1", - "@react-native-community/cli": "11.3.7", - "@react-native-community/cli-platform-android": "11.3.7", - "@react-native-community/cli-platform-ios": "11.3.7", + "@react-native-community/cli": "11.3.10", + "@react-native-community/cli-platform-android": "11.3.10", + "@react-native-community/cli-platform-ios": "11.3.10", "@react-native/assets-registry": "^0.72.0", "@react-native/codegen": "^0.72.7", "@react-native/gradle-plugin": "^0.72.11", @@ -27653,7 +28247,7 @@ "abort-controller": "^3.0.0", "anser": "^1.4.9", "base64-js": "^1.1.2", - "deprecated-react-native-prop-types": "4.1.0", + "deprecated-react-native-prop-types": "^4.2.3", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.5", "invariant": "^2.2.4", @@ -27939,6 +28533,17 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", @@ -27992,6 +28597,25 @@ "esprima": "~4.0.0" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -28339,11 +28963,6 @@ "optional": true, "peer": true }, - "node_modules/safe-json-utils": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-json-utils/-/safe-json-utils-1.1.1.tgz", - "integrity": "sha512-SAJWGKDs50tAbiDXLf89PDwt9XYkWyANFWVzn4dTXl5QyI8t2o/bW5/OJl3lvc2WVU4MEpTo9Yz5NVFNsp+OJQ==" - }, "node_modules/safe-stable-stringify": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", @@ -28725,10 +29344,13 @@ } }, "node_modules/semantic-release/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", + "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, "engines": { "node": "14 || >=16.14" } @@ -29048,7 +29670,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "devOptional": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -29060,7 +29681,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "devOptional": true, "engines": { "node": ">=8" } @@ -29275,12 +29895,12 @@ } }, "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -29605,6 +30225,11 @@ "node": ">=8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -29613,6 +30238,11 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.5.0.tgz", + "integrity": "sha512-JGUEaALvL0Mf6JCfYnJOTcobY+Nc7sG/TemDRBqCA0wEr4DER7zDchaaixTlmOxAjG1uRJmX82EQcxwTQTkqVA==" + }, "node_modules/str2buf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/str2buf/-/str2buf-1.3.0.tgz", @@ -29740,7 +30370,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "devOptional": true, "engines": { "node": ">=6" } @@ -29955,9 +30584,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.3.tgz", - "integrity": "sha512-/OgHfO96RWXF+p/EOjEnvKNEh94qAG/VHukgmVKh5e6foX9kas1WbjvQnDDj0sSTAMr9MHRBqAWytDcQi0VOrg==" + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.4.tgz", + "integrity": "sha512-Ppghvj6Q8XxH5xiSrUjEeCUitrasGtz7v9FCUIBR/4t89fACQ4FnUT9D0yfodUYhB+PrCmYmxwe/2jTDLslHDw==" }, "node_modules/swagger-ui-express": { "version": "5.0.0", @@ -30344,7 +30973,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, @@ -30844,6 +31472,11 @@ "node": "*" } }, + "node_modules/ufo": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.1.tgz", + "integrity": "sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==" + }, "node_modules/uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -30913,6 +31546,11 @@ "multiformats": "^12.0.1" } }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==" + }, "node_modules/undici": { "version": "5.27.2", "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", @@ -30929,6 +31567,18 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unenv": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.7.4.tgz", + "integrity": "sha512-fjYsXYi30It0YCQYqLOcT6fHfMXsBr2hw9XC7ycf8rTG7Xxpe3ZssiqUnD0khrjiZEmkBXWLwm42yCSCH46fMw==", + "dependencies": { + "consola": "^3.2.3", + "defu": "^6.1.2", + "mime": "^3.0.0", + "node-fetch-native": "^1.4.0", + "pathe": "^1.1.1" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -30982,6 +31632,18 @@ "node": ">=4" } }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -31038,6 +31700,83 @@ "node": ">= 0.8" } }, + "node_modules/unstorage": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.9.0.tgz", + "integrity": "sha512-VpD8ZEYc/le8DZCrny3bnqKE4ZjioQxBRnWE+j5sGNvziPjeDlaS1NaFFHzl/kkXaO3r7UaF8MGQrs14+1B4pQ==", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^3.5.3", + "destr": "^2.0.1", + "h3": "^1.7.1", + "ioredis": "^5.3.2", + "listhen": "^1.2.2", + "lru-cache": "^10.0.0", + "mri": "^1.2.0", + "node-fetch-native": "^1.2.0", + "ofetch": "^1.1.1", + "ufo": "^1.2.0" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.4.1", + "@azure/cosmos": "^3.17.3", + "@azure/data-tables": "^13.2.2", + "@azure/identity": "^3.2.3", + "@azure/keyvault-secrets": "^4.7.0", + "@azure/storage-blob": "^12.14.0", + "@capacitor/preferences": "^5.0.0", + "@planetscale/database": "^1.8.0", + "@upstash/redis": "^1.22.0", + "@vercel/kv": "^0.2.2", + "idb-keyval": "^6.2.1" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "idb-keyval": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", + "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", @@ -31046,6 +31785,19 @@ "node": ">=8" } }, + "node_modules/untun": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.2.tgz", + "integrity": "sha512-wLAMWvxfqyTiBODA1lg3IXHQtjggYLeTK7RnSfqtOXixWJ3bAa2kK/HHmOOg19upteqO3muLvN6O/icbyQY33Q==", + "dependencies": { + "citty": "^0.1.3", + "consola": "^3.2.3", + "pathe": "^1.1.1" + }, + "bin": { + "untun": "bin/untun.mjs" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -31075,6 +31827,11 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -31417,7 +32174,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "devOptional": true, "dependencies": { "isexe": "^2.0.0" }, diff --git a/package.json b/package.json index 9c1f1c7f..6c96de65 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^9.2.2", + "@semantic-release/github": "^9.2.3", "@semantic-release/npm": "^11.0.1", "@semantic-release/release-notes-generator": "^12.1.0", "@types/cookie-parser": "^1.4.6", @@ -118,7 +118,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-config-typescript": "^3.0.0", "jest": "^29.7.0", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "semantic-release": "^22.0.7", "swagger-jsdoc": "^6.2.8", "ts-jest": "^29.1.1", diff --git a/src/app.ts b/src/app.ts index 4a544400..7ac48ddc 100644 --- a/src/app.ts +++ b/src/app.ts @@ -13,7 +13,7 @@ import { AccountController } from './controllers/customer.js'; import { Authentication } from './middleware/authentication.js'; import { Connection } from './database/connection/connection.js'; import { RevocationController } from './controllers/revocation.js'; -import { CORS_ALLOWED_ORIGINS, CORS_ERROR_MSG, configLogToExpress } from './types/constants.js'; +import { CORS_ALLOWED_ORIGINS, CORS_ERROR_MSG } from './types/constants.js'; import { LogToWebHook } from './middleware/hook.js'; import { Middleware } from './middleware/middleware.js'; @@ -22,7 +22,6 @@ dotenv.config(); // Define Swagger file import swaggerDocument from './static/swagger.json' assert { type: 'json' }; -import { handleAuthRoutes, withLogto } from '@logto/express'; let swaggerOptions = {}; if (process.env.ENABLE_AUTHENTICATION === 'true') { @@ -81,8 +80,8 @@ class App { ); // Authentication functions/methods this.express.use(async (_req, _res, next) => await auth.setup(next)); - this.express.use(handleAuthRoutes(configLogToExpress)); - this.express.use(withLogto(configLogToExpress)); + this.express.use(async (_req, _res, next) => await auth.wrapperHandleAuthRoutes(_req, _res, next)); + this.express.use(async (_req, _res, next) => await auth.withLogtoWrapper(_req, _res, next)); if (process.env.ENABLE_EXTERNAL_DB === 'true') { this.express.use(async (req, res, next) => await auth.guard(req, res, next)); } @@ -167,6 +166,7 @@ class App { // Account API app.get('/account', new AccountController().get); + app.get('/account/idtoken', new AccountController().getIdToken); // LogTo webhooks app.post('/account/bootstrap', LogToWebHook.verifyHookSignature, new AccountController().bootstrap); diff --git a/src/controllers/customer.ts b/src/controllers/customer.ts index ad8fac64..79c5332a 100644 --- a/src/controllers/customer.ts +++ b/src/controllers/customer.ts @@ -2,7 +2,7 @@ import type { Request, Response } from 'express'; import { CheqdNetwork, checkBalance } from '@cheqd/sdk'; import { TESTNET_MINIMUM_BALANCE, DEFAULT_DENOM_EXPONENT } from '../types/constants.js'; import { CustomerService } from '../services/customer.js'; -import { LogToHelper } from '../middleware/auth/logto.js'; +import { LogToHelper } from '../middleware/auth/logto-helper.js'; import { FaucetHelper } from '../helpers/faucet.js'; import { StatusCodes } from 'http-status-codes'; import { LogToWebHook } from '../middleware/hook.js'; @@ -65,6 +65,53 @@ export class AccountController { } } + /** + * @openapi + * + * /account/idtoken: + * get: + * tags: [Account] + * summary: Fetch IdToken. + * description: This endpoint returns IdToken as JWT with list of user roles inside + * responses: + * 200: + * description: The request was successful. + * content: + * application/json: + * idToken: + * type: string + * 400: + * $ref: '#/components/schemas/InvalidRequest' + * 401: + * $ref: '#/components/schemas/UnauthorizedError' + * 500: + * $ref: '#/components/schemas/InternalError' + */ + public async getIdToken(request: Request, response: Response) { + if (!request.user || !request.session.idToken) { + return response.status(StatusCodes.BAD_REQUEST).json({ + error: 'Seems like authorisation process was corrupted. Please contact administrator.', + }); + } + + const identityStrategySetup = new IdentityServiceStrategySetup(response.locals.customer.customerId); + let apiKey = await identityStrategySetup.agent.getAPIKey(response.locals.customer, response.locals.user); + // If there is no API key for the customer - create it + if (!apiKey) { + apiKey = await identityStrategySetup.agent.setAPIKey( + request.session.idToken, + response.locals.customer, + response.locals.user + ); + } else if (apiKey.isExpired()) { + // If API key is expired - update it + apiKey = await identityStrategySetup.agent.updateAPIKey(apiKey, request.session.idToken); + } + return response.status(StatusCodes.OK).json({ + idToken: apiKey?.apiKey, + }); + } + public async setupDefaultRole(request: Request, response: Response) { if (request.body) { const { body } = request; diff --git a/src/database/entities/api.key.entity.ts b/src/database/entities/api.key.entity.ts new file mode 100644 index 00000000..95c3e4e1 --- /dev/null +++ b/src/database/entities/api.key.entity.ts @@ -0,0 +1,66 @@ +import { BeforeInsert, BeforeUpdate, Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +import * as dotenv from 'dotenv'; +import { CustomerEntity } from './customer.entity.js'; +import { UserEntity } from './user.entity.js'; +dotenv.config(); + +@Entity('apiKey') +export class APIKeyEntity { + @PrimaryGeneratedColumn('uuid') + apiKeyId!: string; + + @Column({ + type: 'text', + nullable: false, + }) + apiKey!: string; + + @Column({ + type: 'timestamptz', + nullable: false, + }) + expiresAt!: Date; + + @Column({ + type: 'timestamptz', + nullable: false, + }) + createdAt!: Date; + + @Column({ + type: 'timestamptz', + nullable: true, + }) + updatedAt!: Date; + + @ManyToOne(() => CustomerEntity, (customer) => customer.customerId, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'customerId' }) + customer!: CustomerEntity; + + @ManyToOne(() => UserEntity, (user) => user.logToId, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'userId' }) + user!: UserEntity; + + @BeforeInsert() + setCreatedAt() { + this.createdAt = new Date(); + } + + @BeforeUpdate() + setUpdateAt() { + this.updatedAt = new Date(); + } + + public isExpired(): boolean { + return this.expiresAt < new Date(); + } + + constructor(apiKeyId: string, apiKey: string, expiresAt: Date, customer: CustomerEntity, user: UserEntity) { + this.apiKeyId = apiKeyId; + this.apiKey = apiKey; + this.expiresAt = expiresAt; + this.customer = customer; + this.user = user; + } +} diff --git a/src/database/migrations/CreateApiKeyMigration.ts b/src/database/migrations/CreateApiKeyMigration.ts new file mode 100644 index 00000000..597b20c9 --- /dev/null +++ b/src/database/migrations/CreateApiKeyMigration.ts @@ -0,0 +1,50 @@ +import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'; + +export class CreateAPIKeyTable1695740345977 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const table = new Table({ + name: 'apiKey', + columns: [ + { + name: 'apiKeyId', + type: 'uuid', + isPrimary: true, + isGenerated: true, + generationStrategy: 'uuid', + }, + { name: 'apiKey', type: 'text', isNullable: false }, + { name: 'customerId', type: 'uuid', isNullable: false }, + { name: 'userId', type: 'text', isNullable: false }, + { name: 'expiresAt', type: 'timestamptz', isNullable: false }, + { name: 'createdAt', type: 'timestamptz', isNullable: false }, + { name: 'updatedAt', type: 'timestamptz', isNullable: true }, + ], + }); + + await queryRunner.createTable(table); + + await queryRunner.createForeignKey( + table, + new TableForeignKey({ + columnNames: ['customerId'], + referencedColumnNames: ['customerId'], + referencedTableName: 'customer', + onDelete: 'CASCADE', + }) + ); + + await queryRunner.createForeignKey( + table, + new TableForeignKey({ + columnNames: ['userId'], + referencedColumnNames: ['logToId'], + referencedTableName: 'user', + onDelete: 'CASCADE', + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + throw new Error('illegal_operation: cannot roll back initial migration'); + } +} diff --git a/src/database/types/types.ts b/src/database/types/types.ts index 11f0b1f6..4583d8ff 100644 --- a/src/database/types/types.ts +++ b/src/database/types/types.ts @@ -26,6 +26,8 @@ import { AlterTableKey1695740345977 } from '../migrations/AlterTableKey.js'; import { KeyEntity } from '../entities/key.entity.js'; import { IdentifierEntity } from '../entities/identifier.entity.js'; import { MigrateData1695740345977 } from '../migrations/MigrateData.js'; +import { APIKeyEntity } from '../entities/api.key.entity.js'; +import { CreateAPIKeyTable1695740345977 } from '../migrations/CreateApiKeyMigration.js'; dotenv.config(); const { EXTERNAL_DB_CONNECTION_URL, EXTERNAL_DB_CERT } = process.env; @@ -84,6 +86,7 @@ export class Postgres implements AbstractDatabase { AlterTableIdentifier1695740345977, AlterTableKey1695740345977, MigrateData1695740345977, + CreateAPIKeyTable1695740345977, ], entities: [ ...Entities, @@ -96,6 +99,7 @@ export class Postgres implements AbstractDatabase { ResourceEntity, KeyEntity, IdentifierEntity, + APIKeyEntity, ], logging: ['error', 'info', 'warn'], }); diff --git a/src/middleware/auth/base-auth-handler.ts b/src/middleware/auth/base-auth-handler.ts new file mode 100644 index 00000000..a0636cac --- /dev/null +++ b/src/middleware/auth/base-auth-handler.ts @@ -0,0 +1,165 @@ +import type { Request, Response } from 'express'; +import { StatusCodes } from 'http-status-codes'; +import type { IncomingHttpHeaders } from 'http'; +import type { IOAuthProvider } from './oauth/base.js'; +import { LogToProvider } from './oauth/logto-provider.js'; +import { SwaggerUserInfoFetcher } from './user-info-fetcher/swagger-ui.js'; +import { APITokenUserInfoFetcher } from './user-info-fetcher/api-token.js'; +import type { IUserInfoFetcher } from './user-info-fetcher/base.js'; +import { IAuthHandler, RuleRoutine, IAPIGuard } from './routine.js'; +import type { IAuthResponse, MethodToScopeRule } from '../../types/authentication.js'; + +export class BaseAPIGuard extends RuleRoutine implements IAPIGuard { + userInfoFetcher: IUserInfoFetcher = {} as IUserInfoFetcher; + + async guardAPI(request: Request, oauthProvider: IOAuthProvider): Promise { + // Reset all variables + this.reset(); + // Preps + this.preps(request); + // Firstly - try to find the rule for the request + const rule = this.findRule(request.path, request.method, this.getNamespace()); + // If the rule is not found - skip the auth check + if (!rule.isEmpty()) { + this.setRule(rule); + // If the rule is found and it allows unauthorized - return ok + this.setIsAllowedUnauthorized(rule.isAllowedUnauthorized()); + } + // Rule can has allowedUnauthorised to true - it means that it's allowed for all + // Rule can has empty namespace - it means that it's allowed for all namespaces + let _res = this.applyDefaults(request); + if (_res) { + return _res; + } + // Set userId. Usually it will be the LogTo userId + // Here we could get UserId from API Key or from user's token + _res = await this.userInfoFetcher.fetchUserInfo(request, oauthProvider); + if (_res.error) { + return _res; + } + this.setScopes(_res.data.scopes); + this.setUserId(_res.data.userId); + // Checks if the list of scopes from user enough to make an action + if (!this.areValidScopes(this.getRule(), this.getScopes())) { + return this.returnError( + StatusCodes.FORBIDDEN, + `Unauthorized error: Your account is not authorized to carry out this action.` + ); + } + return this.returnOk(); + } + + protected preps(request: Request) { + // Setup the namespace + // Here we just trying to get the network value from the request + // The validation depends on the rule for the request + const namespace = this.getNamespaceFromRequest(request); + if (namespace) { + this.setNamespace(namespace); + } + } + + protected applyDefaults(request: Request): IAuthResponse | void { + const rule = this.getRule(); + const namespace = this.getNamespace(); + + if (rule.isEmpty()) { + return this.returnError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Internal error: Issue with finding the rule for the path ${request.path}` + ); + } + // If the rule allows unauthorized - return ok + if (rule.isAllowedUnauthorized()) { + return this.returnOk(); + } + // Namespace should be testnet or mainnet or '' if isSkipNamespace is true + // Otherwise - raise an error. + if (!namespace && !rule.isSkipNamespace()) { + return this.returnError( + StatusCodes.INTERNAL_SERVER_ERROR, + 'Seems like there is no information about the network in the request.' + ); + } + } + + public isValidScope(rule: MethodToScopeRule, scope: string): boolean { + return rule.validate(scope); + } + + public areValidScopes(rule: MethodToScopeRule, scopes: string[]): boolean { + for (const scope of scopes) { + if (this.isValidScope(rule, scope)) { + return true; + } + } + return false; + } +} + +export class BaseAuthHandler extends BaseAPIGuard implements IAuthHandler { + private nextHandler: IAuthHandler; + oauthProvider: IOAuthProvider; + private bearerTokenIdentifier = 'Bearer'; + private pathSkip = ['/swagger', '/static', '/logto', '/account/bootstrap']; + + constructor() { + super(); + this.userInfoFetcher = new SwaggerUserInfoFetcher(); + // For now we use only one provider - LogTo + this.oauthProvider = new LogToProvider(); + this.nextHandler = {} as IAuthHandler; + } + + public extractBearerTokenFromHeaders({ authorization }: IncomingHttpHeaders): string | unknown { + if (authorization && authorization.startsWith(this.bearerTokenIdentifier)) { + return authorization.slice(this.bearerTokenIdentifier.length + 1); + } + return undefined; + } + + private chooseUserFetcherStrategy(request: Request): void { + const token = this.extractBearerTokenFromHeaders(request.headers) as string; + if (token) { + this.setUserInfoStrategy(new APITokenUserInfoFetcher(token)); + } else { + this.setUserInfoStrategy(new SwaggerUserInfoFetcher()); + } + } + + public setOAuthProvider(oauthProvider: IOAuthProvider): void { + this.oauthProvider = oauthProvider; + } + + public setUserInfoStrategy(strategy: IUserInfoFetcher): void { + this.userInfoFetcher = strategy; + } + + // interface implementation + async guardAPI(request: Request): Promise { + this.chooseUserFetcherStrategy(request); + return super.guardAPI(request, this.oauthProvider); + } + + public setNext(handler: IAuthHandler): IAuthHandler { + this.nextHandler = handler; + return handler; + } + + public async handle(request: Request, response: Response): Promise { + if (Object.keys(this.nextHandler).length !== 0) { + return this.nextHandler.handle(request, response); + } + // If request.path was not registered in the routeToScope, then skip the auth check + return this.returnOk(); + } + + public skipPath(path: string): boolean { + for (const ps of this.pathSkip) { + if (path === '/' || path.startsWith(ps)) { + return true; + } + } + return false; + } +} diff --git a/src/middleware/auth/base-auth.ts b/src/middleware/auth/base-auth.ts deleted file mode 100644 index 16505491..00000000 --- a/src/middleware/auth/base-auth.ts +++ /dev/null @@ -1,306 +0,0 @@ -import type { Request, Response } from 'express'; -import * as dotenv from 'dotenv'; -import { StatusCodes } from 'http-status-codes'; -import stringify from 'json-stringify-safe'; -import { DefaultNetworkPattern } from '../../types/shared.js'; -import { MethodToScope, IAuthResourceHandler, Namespaces, IAuthResponse } from '../../types/authentication.js'; -import { LogToHelper } from './logto.js'; -import InvalidTokenError from 'jwt-decode'; -import jwt_decode from 'jwt-decode'; - -dotenv.config(); - -export abstract class AbstractAuthHandler implements IAuthResourceHandler { - private nextHandler: IAuthResourceHandler; - private namespace: Namespaces; - private isAllowedUnauthorized: boolean; - private token: string; - private scopes: string[]; - private logToHelper: LogToHelper; - - public logToId: string; - - private routeToScoupe: MethodToScope[] = []; - private static pathSkip = ['/swagger', '/static', '/logto', '/account/bootstrap', '/auth/user-info']; - - constructor() { - this.nextHandler = {} as IAuthResourceHandler; - this.namespace = '' as Namespaces; - this.token = '' as string; - this.isAllowedUnauthorized = false; - this.scopes = []; - this.logToId = '' as string; - this.logToHelper = new LogToHelper(); - } - - private reset() { - this.namespace = '' as Namespaces; - this.token = '' as string; - this.scopes = []; - this.logToId = '' as string; - this.isAllowedUnauthorized = false; - } - - public async commonPermissionCheck(request: Request): Promise { - // Reset all variables - this.reset(); - - // Setup the namespace - // Here we just trying to get the network value from the request - // The validation depends on the rule for the request - const namespace = this.getNamespaceFromRequest(request); - if (namespace) { - this.namespace = namespace; - } - - // Firstly - try to find the rule for the request - const rule = this.findRule(request.path, request.method, this.getNamespace()); - - if (!rule) { - return this.returnError( - StatusCodes.INTERNAL_SERVER_ERROR, - `Internal error: Issue with finding the rule for the path ${request.path}` - ); - } - - // If the rule is found and it allows unauthorized - return ok - this.isAllowedUnauthorized = rule.isAllowedUnauthorized(); - - if (rule.isAllowedUnauthorized()) { - return this.returnOk(); - } - // Namespace should be testnet or mainnet or '' if isSkipNamespace is true - // Otherwise - raise an error. - if (!this.namespace && !rule?.isSkipNamespace()) { - return this.returnError( - StatusCodes.INTERNAL_SERVER_ERROR, - 'Seems like there is no information about the network in the request.' - ); - } - // If the user is not authenticated - return error - if (!request.user.isAuthenticated) { - return this.returnError( - StatusCodes.UNAUTHORIZED, - "Unauthorized error: Seems like you are not authenticated. Please follow the authentication process using 'LogIn' button" - ); - } - // Tries to get customerId from the logTo user structure - if (request.user && request.user.claims) { - this.logToId = request.user.claims.sub; - } else { - return this.returnError( - StatusCodes.BAD_GATEWAY, - 'Internal error: Seems like authentication process was corrupted and there are problems with getting customerId' - ); - } - // Tries to get scopes for current user and check that required scopes are present - const _resp = await this.logToHelper.getUserScopes(this.getLogToId()); - if (_resp.status !== 200) { - return _resp; - } - if (_resp.data) { - this.scopes = _resp.data; - } - // Checks if the list of scopes from user enough to make an action - if (!this.areValidScopes(rule, this.getScopes())) { - return this.returnError( - StatusCodes.FORBIDDEN, - `Unauthorized error: Your account is not authorized to carry out this action.` - ); - } - return this.returnOk(); - } - - private returnOk(): IAuthResponse { - return { - status: StatusCodes.OK, - error: '', - data: { - logToId: this.getLogToId(), - scopes: this.getScopes() as string[], - namespace: this.getNamespace(), - isAllowedUnauthorized: this.getIsAllowedUnauthorized(), - }, - }; - } - - private returnError(status: number, error: string): IAuthResponse { - return { - status: status, - error: error, - data: { - logToId: '', - scopes: [], - namespace: this.getNamespace(), - isAllowedUnauthorized: this.getIsAllowedUnauthorized(), - }, - }; - } - - // interface implementation - public setNext(handler: IAuthResourceHandler): IAuthResourceHandler { - this.nextHandler = handler; - return handler; - } - - public async handle(request: Request, response: Response): Promise { - if (Object.keys(this.nextHandler).length !== 0) { - return this.nextHandler.handle(request, response); - } - // If request.path was not registered in the routeToScope, then skip the auth check - return this.returnOk(); - } - - public skipPath(path: string): boolean { - for (const ps of AbstractAuthHandler.pathSkip) { - if (path === '/' || path.startsWith(ps)) { - return true; - } - } - return false; - } - - // common utils - public setLogToHelper(logToHelper: LogToHelper) { - this.logToHelper = logToHelper; - } - - private findNetworkInBody(body: string): string | null { - const matches = body.match(DefaultNetworkPattern); - if (matches && matches.length > 0) { - return matches[1]; - } - return null; - } - - private switchNetwork(network: string): Namespaces | null { - switch (network) { - case 'testnet': { - return Namespaces.Testnet; - } - case 'mainnet': { - return Namespaces.Mainnet; - } - default: { - return null; - } - } - } - - public getNamespaceFromRequest(req: Request): Namespaces | null { - let network: string | null = ''; - - if (req && req.body && req.body.credential) { - const { credential } = req.body; - let decoded = ''; - let issuerDid = ''; - // Try to get issuer DID - if (credential && credential.issuer) { - issuerDid = credential.issuer.id; - } - network = this.findNetworkInBody(issuerDid); - if (network) { - return this.switchNetwork(network); - } - try { - decoded = jwt_decode(req.body.credential); - } catch (e) { - // If it's not a JWT - just skip it - if (!(e instanceof InvalidTokenError)) { - throw e; - } - } - // if not - try to search for decoded credential - network = this.findNetworkInBody(stringify(decoded)); - if (network) { - return this.switchNetwork(network); - } - } - // Try to search in request body - if (req && req.body) { - network = this.findNetworkInBody(stringify(req.body)); - if (network) { - return this.switchNetwork(network); - } - } - // Try to search in request path - if (req && req.path) { - network = this.findNetworkInBody(decodeURIComponent(req.path)); - if (network) { - return this.switchNetwork(network); - } - } - // For DID create we specify it as a separate parameter in body - if (req.body && req.body.network) { - return this.switchNetwork(req.body.network); - } - - return null; - } - - // Getters - public getNamespace(): Namespaces { - return this.namespace; - } - - public getToken(): string { - return this.token; - } - - public getScopes(): string[] { - return this.scopes; - } - - public getLogToId(): string { - return this.logToId; - } - - public getIsAllowedUnauthorized(): boolean { - return this.isAllowedUnauthorized; - } - - public getAllLogToScopes(): string[] | void { - if (this.logToHelper) { - return this.logToHelper.getAllScopes(); - } - } - - public getDefaultLogToScopes(): string[] | void { - if (this.logToHelper) { - return this.logToHelper.getDefaultScopes(); - } - } - - public getAllLogToResources(): string[] | void { - if (this.logToHelper) { - return this.logToHelper.getAllResourcesWithNames(); - } - } - - // Route and scope related funcs - public registerRoute(route: string, method: string, scope: string, options = {}): void { - this.routeToScoupe.push(new MethodToScope(route, method, scope, options)); - } - - public findRule(route: string, method: string, namespace = Namespaces.Testnet): MethodToScope | null { - for (const rule of this.routeToScoupe) { - if (rule.doesRuleMatches(route, method, namespace)) { - return rule; - } - } - return null; - } - - public isValidScope(rule: MethodToScope, scope: string): boolean { - return rule.validate(scope); - } - - public areValidScopes(rule: MethodToScope, scopes: string[]): boolean { - for (const scope of scopes) { - if (this.isValidScope(rule, scope)) { - return true; - } - } - return false; - } -} diff --git a/src/middleware/auth/logto.ts b/src/middleware/auth/logto-helper.ts similarity index 86% rename from src/middleware/auth/logto.ts rename to src/middleware/auth/logto-helper.ts index 28812656..2f406529 100644 --- a/src/middleware/auth/logto.ts +++ b/src/middleware/auth/logto-helper.ts @@ -2,15 +2,18 @@ import type { ICommonErrorResponse } from '../../types/authentication'; import { StatusCodes } from 'http-status-codes'; import jwt from 'jsonwebtoken'; import * as dotenv from 'dotenv'; +import type { IOAuthProvider } from './oauth/base.js'; +import { OAuthProvider } from './oauth/base.js'; dotenv.config(); -export class LogToHelper { +export class LogToHelper extends OAuthProvider implements IOAuthProvider { private m2mToken: string; private allScopes: string[]; private allResourceWithNames: string[]; public defaultScopes: string[]; constructor() { + super(); this.m2mToken = ''; this.allScopes = []; this.defaultScopes = []; @@ -51,7 +54,6 @@ export class LogToHelper { }; return Date.now() >= exp * 1000; } - public getAllScopes(): string[] { return this.allScopes; } @@ -96,6 +98,7 @@ export class LogToHelper { }; } + // Scopes public async getUserScopes(userId: string): Promise { const scopes = [] as string[]; const roles = await this.getRolesForUser(userId); @@ -104,14 +107,131 @@ export class LogToHelper { } // Check that default role is set for (const role of roles.data) { - const _s = await this.askRoleForScopes(role.id); + const _s = await this.getScopesForRole(role.id); if (_s.status === StatusCodes.OK) { scopes.push(..._s.data); } } return this.returnOk(scopes); } + private async setDefaultScopes(): Promise { + const _r = await this.getAllResources(); + if (_r.status !== StatusCodes.OK) { + return this.returnError( + StatusCodes.BAD_GATEWAY, + `Looks like ${process.env.LOGTO_DEFAULT_RESOURCE_URL} is not setup on LogTo side` + ); + } + for (const r of _r.data) { + if (r.indicator === process.env.LOGTO_DEFAULT_RESOURCE_URL) { + const _rr = await this.getScopesForResource(r.id); + if (_rr.status === StatusCodes.OK) { + this.defaultScopes = _rr.data; + return this.returnOk({}); + } else { + return _rr; + } + } + } + return this.returnError( + StatusCodes.BAD_GATEWAY, + `Looks like resource with id ${process.env.LOGTO_DEFAULT_RESOURCE_URL} is not placed on LogTo` + ); + } + private async setAllScopes(): Promise { + const allResources = await this.getAllResources(); + if (allResources.status !== StatusCodes.OK) { + return this.returnError(StatusCodes.BAD_GATEWAY, `setAllScopes: Error while getting all resources`); + } + for (const resource of allResources.data) { + if (resource.id !== 'management-api') { + const scopes = await this.getScopesForResource(resource.id); + if (scopes.status !== StatusCodes.OK) { + return this.returnError( + StatusCodes.BAD_GATEWAY, + `setAllScopes: Error while getting the scopes for ${resource.id}` + ); + } + this.allScopes = this.allScopes.concat(scopes.data); + } + } + return this.returnOk({}); + } + private async getScopesForRole(roleId: string): Promise { + const uri = new URL(`/api/roles/${roleId}/scopes`, process.env.LOGTO_ENDPOINT); + const scopes = []; + try { + const metadata = await this.getToLogto(uri, 'GET'); + if (metadata && metadata.status !== StatusCodes.OK) { + return this.returnError( + StatusCodes.BAD_GATEWAY, + `askRoleForScopes: Error while getting the all scopes for the role ${roleId}` + ); + } + for (const sc of metadata.data) { + scopes.push(sc.name); + } + return this.returnOk(scopes); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `askRoleForScopes ${err}`); + } + } + private async getScopesForResource(resourceId: string): Promise { + const uri = new URL(`/api/resources/${resourceId}/scopes`, process.env.LOGTO_ENDPOINT); + const scopes = []; + + try { + const metadata = await this.getToLogto(uri, 'GET'); + if (metadata && metadata.status !== StatusCodes.OK) { + return this.returnError( + StatusCodes.BAD_GATEWAY, + `askResourceForScopes: Error while getting the all scopes for the resource ${resourceId}` + ); + } + for (const sc of metadata.data) { + scopes.push(sc.name); + } + return this.returnOk(scopes); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `askResourceForScopes ${err}`); + } + } + public async getScopesForRolesList(roles: string[]): Promise { + const scopes = []; + for (const role of roles) { + const roleId = await this.getRoleIdByName(role); + if (roleId.status !== StatusCodes.OK) { + return this.returnError(StatusCodes.BAD_GATEWAY, roleId.error); + } + const _r = await this.getScopesForRole(roleId.data); + if (_r.status !== StatusCodes.OK) { + return _r; + } + scopes.push(..._r.data); + } + return this.returnOk(scopes); + } + + // Roles + public async getRolesForUser(userId: string): Promise { + const uri = new URL(`/api/users/${userId}/roles`, process.env.LOGTO_ENDPOINT); + try { + // Note: By default, the API returns first 20 roles. + // If our roles per user grows to more than 20, we need to implement pagination + return await this.getToLogto(uri, 'GET'); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getRolesForUser ${err}`); + } + } + private async getRoleInfo(roleId: string): Promise { + const uri = new URL(`/api/roles/${roleId}`, process.env.LOGTO_ENDPOINT); + try { + return await this.getToLogto(uri, 'GET'); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getRoleInfo ${err}`); + } + } private async assignDefaultRoleForUser(userId: string, roleId: string): Promise { const userInfo = await this.getUserInfo(userId); const uri = new URL(`/api/users/${userId}/roles`, process.env.LOGTO_ENDPOINT); @@ -144,18 +264,28 @@ export class LogToHelper { return this.returnError(StatusCodes.BAD_GATEWAY, `getRolesForUser ${err}`); } } - - public async getRolesForUser(userId: string): Promise { - const uri = new URL(`/api/users/${userId}/roles`, process.env.LOGTO_ENDPOINT); + private async getRoleIdByName(roleName: string): Promise { + const uri = new URL(`/api/roles`, process.env.LOGTO_ENDPOINT); try { - // Note: By default, the API returns first 20 roles. - // If our roles per user grows to more than 20, we need to implement pagination - return await this.getToLogto(uri, 'GET'); + const metadata = await this.getToLogto(uri, 'GET'); + if (metadata && metadata.status !== StatusCodes.OK) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getRoleIdByName: Error while getting the all roles`); + } + for (const role of metadata.data) { + if (role.name === roleName) { + return this.returnOk(role.id); + } + } + return this.returnError( + StatusCodes.BAD_GATEWAY, + `getRoleIdByName: Could not find role with name ${roleName}` + ); } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `getRolesForUser ${err}`); + return this.returnError(StatusCodes.BAD_GATEWAY, `getRoleIdByName ${err}`); } } + // Users public async updateCustomData(userId: string, customData: any): Promise { const uri = new URL(`/api/users/${userId}/custom-data`, process.env.LOGTO_ENDPOINT); try { @@ -167,7 +297,48 @@ export class LogToHelper { return this.returnError(500, `updateCustomData ${err}`); } } + private async getUserInfo(userId: string): Promise { + const uri = new URL(`/api/users/${userId}`, process.env.LOGTO_ENDPOINT); + try { + return await this.getToLogto(uri, 'GET'); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getUserInfo ${err}`); + } + } + public async getCustomData(userId: string): Promise { + const uri = new URL(`/api/users/${userId}/custom-data`, process.env.LOGTO_ENDPOINT); + try { + return await this.getToLogto(uri, 'GET'); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getCustomData ${err}`); + } + } + // Resources + private async setAllResourcesWithNames(): Promise { + const allResources = await this.getAllResources(); + if (allResources.status !== StatusCodes.OK) { + return this.returnError( + StatusCodes.BAD_GATEWAY, + `setAllResourcesWithNames: Error while getting all resources` + ); + } + for (const resource of allResources.data) { + this.allResourceWithNames.push(resource.indicator); + } + return this.returnOk({}); + } + public async getAllResources(): Promise { + const uri = new URL(`/api/resources`, process.env.LOGTO_ENDPOINT); + + try { + return await this.getToLogto(uri, 'GET'); + } catch (err) { + return this.returnError(StatusCodes.BAD_GATEWAY, `getAllResources ${err}`); + } + } + + // Utils private async patchToLogto(uri: URL, body: any, headers: any = {}): Promise { const response = await fetch(uri, { headers: { @@ -183,7 +354,6 @@ export class LogToHelper { } return this.returnOk({}); } - private async postToLogto(uri: URL, body: any, headers: any = {}): Promise { const response = await fetch(uri, { headers: { @@ -199,7 +369,6 @@ export class LogToHelper { } return this.returnOk({}); } - private async getToLogto(uri: URL, headers: any = {}): Promise { const response = await fetch(uri, { headers: { @@ -215,59 +384,6 @@ export class LogToHelper { const metadata = await response.json(); return this.returnOk(metadata); } - - private async getUserInfo(userId: string): Promise { - const uri = new URL(`/api/users/${userId}`, process.env.LOGTO_ENDPOINT); - try { - return await this.getToLogto(uri, 'GET'); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `getUserInfo ${err}`); - } - } - - public async getCustomData(userId: string): Promise { - const uri = new URL(`/api/users/${userId}/custom-data`, process.env.LOGTO_ENDPOINT); - try { - return await this.getToLogto(uri, 'GET'); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `getCustomData ${err}`); - } - } - - private async getRoleInfo(roleId: string): Promise { - const uri = new URL(`/api/roles/${roleId}`, process.env.LOGTO_ENDPOINT); - try { - return await this.getToLogto(uri, 'GET'); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `getRoleInfo ${err}`); - } - } - - private async setDefaultScopes(): Promise { - const _r = await this.getAllResources(); - if (_r.status !== StatusCodes.OK) { - return this.returnError( - StatusCodes.BAD_GATEWAY, - `Looks like ${process.env.LOGTO_DEFAULT_RESOURCE_URL} is not setup on LogTo side` - ); - } - for (const r of _r.data) { - if (r.indicator === process.env.LOGTO_DEFAULT_RESOURCE_URL) { - const _rr = await this.askResourceForScopes(r.id); - if (_rr.status === StatusCodes.OK) { - this.defaultScopes = _rr.data; - return this.returnOk({}); - } else { - return _rr; - } - } - } - return this.returnError( - StatusCodes.BAD_GATEWAY, - `Looks like resource with id ${process.env.LOGTO_DEFAULT_RESOURCE_URL} is not placed on LogTo` - ); - } - private async setM2MToken(): Promise { const searchParams = new URLSearchParams({ grant_type: 'client_credentials', @@ -300,90 +416,4 @@ export class LogToHelper { return this.returnError(StatusCodes.BAD_GATEWAY, 'Error while communicating with authority server'); } } - - private async setAllScopes(): Promise { - const allResources = await this.getAllResources(); - if (allResources.status !== StatusCodes.OK) { - return this.returnError(StatusCodes.BAD_GATEWAY, `setAllScopes: Error while getting all resources`); - } - for (const resource of allResources.data) { - if (resource.id !== 'management-api') { - const scopes = await this.askResourceForScopes(resource.id); - if (scopes.status !== StatusCodes.OK) { - return this.returnError( - StatusCodes.BAD_GATEWAY, - `setAllScopes: Error while getting the scopes for ${resource.id}` - ); - } - this.allScopes = this.allScopes.concat(scopes.data); - } - } - return this.returnOk({}); - } - - private async setAllResourcesWithNames(): Promise { - const allResources = await this.getAllResources(); - if (allResources.status !== StatusCodes.OK) { - return this.returnError( - StatusCodes.BAD_GATEWAY, - `setAllResourcesWithNames: Error while getting all resources` - ); - } - for (const resource of allResources.data) { - this.allResourceWithNames.push(resource.indicator); - } - return this.returnOk({}); - } - - private async askRoleForScopes(roleId: string): Promise { - const uri = new URL(`/api/roles/${roleId}/scopes`, process.env.LOGTO_ENDPOINT); - const scopes = []; - - try { - const metadata = await this.getToLogto(uri, 'GET'); - if (metadata && metadata.status !== StatusCodes.OK) { - return this.returnError( - StatusCodes.BAD_GATEWAY, - `askRoleForScopes: Error while getting the all scopes for the role ${roleId}` - ); - } - for (const sc of metadata.data) { - scopes.push(sc.name); - } - return this.returnOk(scopes); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `askRoleForScopes ${err}`); - } - } - - private async askResourceForScopes(resourceId: string): Promise { - const uri = new URL(`/api/resources/${resourceId}/scopes`, process.env.LOGTO_ENDPOINT); - const scopes = []; - - try { - const metadata = await this.getToLogto(uri, 'GET'); - if (metadata && metadata.status !== StatusCodes.OK) { - return this.returnError( - StatusCodes.BAD_GATEWAY, - `askResourceForScopes: Error while getting the all scopes for the resource ${resourceId}` - ); - } - for (const sc of metadata.data) { - scopes.push(sc.name); - } - return this.returnOk(scopes); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `askResourceForScopes ${err}`); - } - } - - private async getAllResources(): Promise { - const uri = new URL(`/api/resources`, process.env.LOGTO_ENDPOINT); - - try { - return await this.getToLogto(uri, 'GET'); - } catch (err) { - return this.returnError(StatusCodes.BAD_GATEWAY, `getAllResources ${err}`); - } - } } diff --git a/src/middleware/auth/oauth/base.ts b/src/middleware/auth/oauth/base.ts new file mode 100644 index 00000000..88184109 --- /dev/null +++ b/src/middleware/auth/oauth/base.ts @@ -0,0 +1,34 @@ +import type { ICommonErrorResponse } from '../../../types/authentication.js'; + +const { LOGTO_ENDPOINT } = process.env; + +export interface IOAuthProvider { + endpoint_issuer: string; + endpoint_jwks: string; + getAllScopes(): string[] | void; + getDefaultScopes(): string[] | void; + getAllResourcesWithNames(): string[] | void; + getUserScopes(userId: string): Promise; + getScopesForRoles(rolesList: string[]): Promise; +} + +export abstract class OAuthProvider implements IOAuthProvider { + endpoint_issuer: string = LOGTO_ENDPOINT + '/oidc'; + endpoint_jwks: string = LOGTO_ENDPOINT + '/oidc/jwks'; + + getAllScopes(): string[] | void { + throw new Error('Method not implemented.'); + } + getDefaultScopes(): string[] | void { + throw new Error('Method not implemented.'); + } + getAllResourcesWithNames(): string[] | void { + throw new Error('Method not implemented.'); + } + getUserScopes(userId: string): Promise { + throw new Error('Method not implemented.'); + } + getScopesForRoles(rolesList: string[]): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/middleware/auth/oauth/logto-provider.ts b/src/middleware/auth/oauth/logto-provider.ts new file mode 100644 index 00000000..6b920605 --- /dev/null +++ b/src/middleware/auth/oauth/logto-provider.ts @@ -0,0 +1,47 @@ +import type { ICommonErrorResponse } from '../../../types/authentication.js'; +import { LogToHelper } from '../logto-helper.js'; +import { IOAuthProvider, OAuthProvider } from './base.js'; + +export class LogToProvider extends OAuthProvider implements IOAuthProvider { + private logToHelper: LogToHelper; + + constructor() { + super(); + this.logToHelper = new LogToHelper(); + } + + public setHelper(logToHelper: LogToHelper): void { + this.logToHelper = logToHelper; + } + + public getAllScopes(): string[] | void { + if (this.logToHelper) { + return this.logToHelper.getAllScopes(); + } + } + + public getDefaultScopes(): string[] | void { + if (this.logToHelper) { + return this.logToHelper.getDefaultScopes(); + } + } + + public getAllResourcesWithNames(): string[] | void { + if (this.logToHelper) { + return this.logToHelper.getAllResourcesWithNames(); + } + } + + public async getUserScopes(userId: string): Promise { + return await this.logToHelper.getUserScopes(userId); + } + + public async getScopesForRoles(rolesList: string[]): Promise { + if (this.logToHelper) { + const scopes = await this.logToHelper.getScopesForRolesList(rolesList); + if (scopes) { + return scopes.data; + } + } + } +} diff --git a/src/middleware/auth/account-auth.ts b/src/middleware/auth/routes/account-auth.ts similarity index 60% rename from src/middleware/auth/account-auth.ts rename to src/middleware/auth/routes/account-auth.ts index 58f34f09..d7ca8ffb 100644 --- a/src/middleware/auth/account-auth.ts +++ b/src/middleware/auth/routes/account-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class AccountAuthHandler extends AbstractAuthHandler { +export class AccountAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/account', 'GET', 'read:account', { skipNamespace: true }); @@ -11,6 +11,6 @@ export class AccountAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/account')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/routes/auth-user-info.ts b/src/middleware/auth/routes/auth-user-info.ts new file mode 100644 index 00000000..0d8f72fb --- /dev/null +++ b/src/middleware/auth/routes/auth-user-info.ts @@ -0,0 +1,16 @@ +import type { Request, Response } from 'express'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; + +export class AuthInfoHandler extends BaseAuthHandler { + constructor() { + super(); + this.registerRoute('/auth/user-info', 'GET', '', { skipNamespace: true, allowUnauthorized: true }); + } + public async handle(request: Request, response: Response): Promise { + if (!request.path.includes('/auth/user-info')) { + return super.handle(request, response); + } + return this.guardAPI(request); + } +} diff --git a/src/middleware/auth/credential-auth.ts b/src/middleware/auth/routes/credential-auth.ts similarity index 81% rename from src/middleware/auth/credential-auth.ts rename to src/middleware/auth/routes/credential-auth.ts index e49784b0..73b0f94f 100644 --- a/src/middleware/auth/credential-auth.ts +++ b/src/middleware/auth/routes/credential-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class CredentialAuthHandler extends AbstractAuthHandler { +export class CredentialAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/credential/issue', 'POST', 'issue:credential:testnet'); @@ -21,6 +21,6 @@ export class CredentialAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/credential/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/credential-status-auth.ts b/src/middleware/auth/routes/credential-status-auth.ts similarity index 87% rename from src/middleware/auth/credential-status-auth.ts rename to src/middleware/auth/routes/credential-status-auth.ts index 4707563a..3504953e 100644 --- a/src/middleware/auth/credential-status-auth.ts +++ b/src/middleware/auth/routes/credential-status-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class CredentialStatusAuthHandler extends AbstractAuthHandler { +export class CredentialStatusAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/credential-status/create/encrypted', 'POST', 'create-encrypted:credential-status:testnet'); @@ -39,6 +39,6 @@ export class CredentialStatusAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/credential-status/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/did-auth.ts b/src/middleware/auth/routes/did-auth.ts similarity index 81% rename from src/middleware/auth/did-auth.ts rename to src/middleware/auth/routes/did-auth.ts index add68888..335a05bf 100644 --- a/src/middleware/auth/did-auth.ts +++ b/src/middleware/auth/routes/did-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class DidAuthHandler extends AbstractAuthHandler { +export class DidAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/did/create', 'POST', 'create:did:testnet'); @@ -21,6 +21,6 @@ export class DidAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/did/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/key-auth.ts b/src/middleware/auth/routes/key-auth.ts similarity index 69% rename from src/middleware/auth/key-auth.ts rename to src/middleware/auth/routes/key-auth.ts index 0f207846..5f769e4a 100644 --- a/src/middleware/auth/key-auth.ts +++ b/src/middleware/auth/routes/key-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class KeyAuthHandler extends AbstractAuthHandler { +export class KeyAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/key/create', 'POST', 'create:key', { skipNamespace: true }); @@ -14,6 +14,6 @@ export class KeyAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/key/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/presentation-auth.ts b/src/middleware/auth/routes/presentation-auth.ts similarity index 63% rename from src/middleware/auth/presentation-auth.ts rename to src/middleware/auth/routes/presentation-auth.ts index 219ace5a..3af2fe40 100644 --- a/src/middleware/auth/presentation-auth.ts +++ b/src/middleware/auth/routes/presentation-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class PresentationAuthHandler extends AbstractAuthHandler { +export class PresentationAuthHandler extends BaseAuthHandler { constructor() { super(); // Unauthorized routes @@ -12,6 +12,6 @@ export class PresentationAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/presentation/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/resource-auth.ts b/src/middleware/auth/routes/resource-auth.ts similarity index 70% rename from src/middleware/auth/resource-auth.ts rename to src/middleware/auth/routes/resource-auth.ts index 80763c6f..9182b853 100644 --- a/src/middleware/auth/resource-auth.ts +++ b/src/middleware/auth/routes/resource-auth.ts @@ -1,8 +1,8 @@ import type { Request, Response } from 'express'; -import { AbstractAuthHandler } from './base-auth.js'; -import type { IAuthResponse } from '../../types/authentication.js'; +import { BaseAuthHandler } from '../base-auth-handler.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; -export class ResourceAuthHandler extends AbstractAuthHandler { +export class ResourceAuthHandler extends BaseAuthHandler { constructor() { super(); this.registerRoute('/resource/create', 'POST', 'create:resource:testnet'); @@ -14,6 +14,6 @@ export class ResourceAuthHandler extends AbstractAuthHandler { if (!request.path.includes('/resource/')) { return super.handle(request, response); } - return this.commonPermissionCheck(request); + return this.guardAPI(request); } } diff --git a/src/middleware/auth/routine.ts b/src/middleware/auth/routine.ts new file mode 100644 index 00000000..5c4da9de --- /dev/null +++ b/src/middleware/auth/routine.ts @@ -0,0 +1,226 @@ +import type { Request, Response } from 'express'; +import * as dotenv from 'dotenv'; +import { StatusCodes } from 'http-status-codes'; +import stringify from 'json-stringify-safe'; +import { DefaultNetworkPattern } from '../../types/shared.js'; +import { MethodToScopeRule, Namespaces, IAuthResponse, ICommonErrorResponse } from '../../types/authentication.js'; +import InvalidTokenError from 'jwt-decode'; +import jwt_decode from 'jwt-decode'; +import type { IOAuthProvider } from './oauth/base.js'; +import type { IUserInfoFetcher } from './user-info-fetcher/base.js'; +dotenv.config(); + +export interface IAPIGuard { + guardAPI(request: Request, oauthProvider: IOAuthProvider): Promise; +} + +export interface IAuthParams { + reset(): void; + // Getters + getUserId(): string; + getScopes(): string[]; + getNamespace(): Namespaces; + getIsAllowedUnauthorized(): boolean; + getRule(): MethodToScopeRule; + getRouteToScopeList(): MethodToScopeRule[]; + // Setters + setUserId(userId: string): void; + setScopes(scopes: string[]): void; + setNamespace(namespace: Namespaces): void; + setIsAllowedUnauthorized(isAllowedUnauthorized: boolean): void; + setRule(MethodToScopeRule: MethodToScopeRule): void; + pushToRuleList(MethodToScopeRule: MethodToScopeRule): void; +} + +export class AuthParams implements IAuthParams { + userId: string; + scopes: string[]; + namespace: Namespaces; + isAllowedUnauthorized: boolean; + rule: MethodToScopeRule; + routeToScoupeList: MethodToScopeRule[] = []; + + constructor() { + this.namespace = '' as Namespaces; + this.scopes = []; + this.userId = '' as string; + this.isAllowedUnauthorized = false; + this.rule = {} as MethodToScopeRule; + this.routeToScoupeList = []; + } + + // Getters + public getUserId(): string { + return this.userId; + } + public getScopes(): string[] { + return this.scopes; + } + public getNamespace(): Namespaces { + return this.namespace; + } + public getIsAllowedUnauthorized(): boolean { + return this.isAllowedUnauthorized; + } + public getRule(): MethodToScopeRule { + return this.rule; + } + public getRouteToScopeList(): MethodToScopeRule[] { + return this.routeToScoupeList; + } + + //Setters + public setUserId(userId: string): void { + this.userId = userId; + } + public setScopes(scopes: string[]): void { + this.scopes = scopes; + } + public setNamespace(namespace: Namespaces): void { + this.namespace = namespace; + } + public setIsAllowedUnauthorized(isAllowedUnauthorized: boolean): void { + this.isAllowedUnauthorized = isAllowedUnauthorized; + } + public setRule(methodToScopeRule: MethodToScopeRule): void { + this.rule = methodToScopeRule; + } + public pushToRuleList(methodToScopeRule: MethodToScopeRule): void { + this.routeToScoupeList.push(methodToScopeRule); + } + + // Reset + reset() { + this.namespace = '' as Namespaces; + this.scopes = []; + this.userId = '' as string; + this.isAllowedUnauthorized = false; + this.rule = {} as MethodToScopeRule; + } +} + +// Simple interface for building the response/result +export interface IReturn { + returnOk(): ICommonErrorResponse; + returnError(status: number, error: string): ICommonErrorResponse; +} + +export class AuthReturn extends AuthParams implements IReturn { + returnOk(): IAuthResponse { + return { + status: StatusCodes.OK, + error: '', + data: { + userId: this.getUserId(), + scopes: this.getScopes() as string[], + namespace: this.getNamespace(), + isAllowedUnauthorized: this.getIsAllowedUnauthorized(), + }, + }; + } + + returnError(status: number, error: string): IAuthResponse { + return { + status: status, + error: error, + data: { + userId: '', + scopes: [], + namespace: this.getNamespace(), + isAllowedUnauthorized: this.getIsAllowedUnauthorized(), + }, + }; + } +} + +export interface IAuthHandler extends IAPIGuard, IReturn, IAuthParams { + oauthProvider: IOAuthProvider; + setOAuthProvider(oauthProvider: IOAuthProvider): void; + setUserInfoStrategy(strategy: IUserInfoFetcher): void; + setNext(handler: IAuthHandler): IAuthHandler; + handle(request: Request, response: Response): Promise; +} + +export class RuleRoutine extends AuthReturn implements IReturn { + protected getNamespaceFromRequest(req: Request): Namespaces | null { + let network: string | null = ''; + + if (req && req.body && req.body.credential) { + const { credential } = req.body; + let decoded = ''; + let issuerDid = ''; + // Try to get issuer DID + if (credential && credential.issuer) { + issuerDid = credential.issuer.id; + } + network = this.findNetworkInBody(issuerDid); + if (network) { + return this.switchNetwork(network); + } + try { + decoded = jwt_decode(req.body.credential); + } catch (e) { + // If it's not a JWT - just skip it + if (!(e instanceof InvalidTokenError)) { + throw e; + } + } + // if not - try to search for decoded credential + network = this.findNetworkInBody(stringify(decoded)); + if (network) { + return this.switchNetwork(network); + } + } + // Try to search in request body + if (req && req.body) { + network = this.findNetworkInBody(stringify(req.body)); + if (network) { + return this.switchNetwork(network); + } + } + // Try to search in request path + if (req && req.path) { + network = this.findNetworkInBody(decodeURIComponent(req.path)); + if (network) { + return this.switchNetwork(network); + } + } + // For DID create we specify it as a separate parameter in body + if (req.body && req.body.network) { + return this.switchNetwork(req.body.network); + } + + return null; + } + protected findNetworkInBody(body: string): string | null { + const matches = body.match(DefaultNetworkPattern); + if (matches && matches.length > 0) { + return matches[1]; + } + return null; + } + protected switchNetwork(network: string): Namespaces | null { + switch (network) { + case 'testnet': { + return Namespaces.Testnet; + } + case 'mainnet': { + return Namespaces.Mainnet; + } + default: { + return null; + } + } + } + protected findRule(route: string, method: string, namespace = Namespaces.Testnet): MethodToScopeRule { + for (const rule of this.getRouteToScopeList()) { + if (rule.doesRuleMatches(route, method, namespace)) { + return rule; + } + } + return {} as MethodToScopeRule; + } + protected registerRoute(route: string, method: string, scope: string, options = {}): void { + this.pushToRuleList(new MethodToScopeRule(route, method, scope, options)); + } +} diff --git a/src/middleware/auth/user-info-fetcher/api-token.ts b/src/middleware/auth/user-info-fetcher/api-token.ts new file mode 100644 index 00000000..26f8b661 --- /dev/null +++ b/src/middleware/auth/user-info-fetcher/api-token.ts @@ -0,0 +1,51 @@ +import type { Request } from 'express'; +import { AuthReturn } from '../routine.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; +import { StatusCodes } from 'http-status-codes'; +import type { IUserInfoFetcher } from './base.js'; +import type { IOAuthProvider } from '../oauth/base.js'; +import { createRemoteJWKSet, jwtVerify } from 'jose'; + +import * as dotenv from 'dotenv'; +dotenv.config(); + +export class APITokenUserInfoFetcher extends AuthReturn implements IUserInfoFetcher { + token: string; + + constructor(token: string) { + super(); + this.token = token; + } + + async fetchUserInfo(request: Request, oauthProvider: IOAuthProvider): Promise { + return this.verifyJWTToken(this.token as string, oauthProvider); + } + + public async verifyJWTToken(token: string, oauthProvider: IOAuthProvider): Promise { + try { + const { payload } = await jwtVerify( + token, // The raw Bearer Token extracted from the request header + createRemoteJWKSet(new URL(oauthProvider.endpoint_jwks)), // generate a jwks using jwks_uri inquired from Logto server + { + // expected issuer of the token, should be issued by the Logto server + issuer: oauthProvider.endpoint_issuer, + // expected audience token, should be the resource indicator of the current API + audience: process.env.LOGTO_APP_ID, + } + ); + // Setup the scopes from the token + if (!payload.roles) { + return this.returnError(StatusCodes.UNAUTHORIZED, `Unauthorized error: No roles found in the token.`); + } + const scopes = await oauthProvider.getScopesForRoles(payload.roles as string[]); + if (!scopes) { + return this.returnError(StatusCodes.UNAUTHORIZED, `Unauthorized error: No scopes found for the roles.`); + } + this.setScopes(scopes); + this.setUserId(payload.sub as string); + return this.returnOk(); + } catch (error) { + return this.returnError(StatusCodes.INTERNAL_SERVER_ERROR, `Unexpected error: ${error}`); + } + } +} diff --git a/src/middleware/auth/user-info-fetcher/base.ts b/src/middleware/auth/user-info-fetcher/base.ts new file mode 100644 index 00000000..9366d30b --- /dev/null +++ b/src/middleware/auth/user-info-fetcher/base.ts @@ -0,0 +1,7 @@ +import type { Request } from 'express'; +import type { IAuthResponse } from '../../../types/authentication.js'; +import type { IOAuthProvider } from '../oauth/base.js'; + +export interface IUserInfoFetcher { + fetchUserInfo(request: Request, oauthProvider: IOAuthProvider): Promise; +} diff --git a/src/middleware/auth/user-info-fetcher/swagger-ui.ts b/src/middleware/auth/user-info-fetcher/swagger-ui.ts new file mode 100644 index 00000000..a01a6ec7 --- /dev/null +++ b/src/middleware/auth/user-info-fetcher/swagger-ui.ts @@ -0,0 +1,36 @@ +import type { Request } from 'express'; +import { AuthReturn } from '../routine.js'; +import type { IOAuthProvider } from '../oauth/base.js'; +import type { IAuthResponse } from '../../../types/authentication.js'; +import { StatusCodes } from 'http-status-codes'; +import type { IUserInfoFetcher } from './base.js'; + +export class SwaggerUserInfoFetcher extends AuthReturn implements IUserInfoFetcher { + async fetchUserInfo(request: Request, oauthProvider: IOAuthProvider): Promise { + // If the user is not authenticated - return error + if (!request.user.isAuthenticated) { + return this.returnError( + StatusCodes.UNAUTHORIZED, + "Unauthorized error: Seems like you are not authenticated. Please follow the authentication process using 'LogIn' button" + ); + } + // Tries to get customerId from the logTo user structure + if (request.user && request.user.claims) { + this.setUserId(request.user.claims.sub); + } else { + return this.returnError( + StatusCodes.BAD_GATEWAY, + 'Internal error: Seems like authentication process was corrupted and there are problems with getting customerId' + ); + } + // Tries to get scopes for current user and check that required scopes are present + const _resp = await oauthProvider.getUserScopes(this.getUserId()); + if (_resp.status !== 200) { + return _resp; + } + if (_resp.data) { + this.setScopes(_resp.data); + } + return this.returnOk(); + } +} diff --git a/src/middleware/authentication.ts b/src/middleware/authentication.ts index d0aff8bc..065bc880 100644 --- a/src/middleware/authentication.ts +++ b/src/middleware/authentication.ts @@ -2,23 +2,27 @@ import { Request, Response, NextFunction, response } from 'express'; import { StatusCodes } from 'http-status-codes'; import * as dotenv from 'dotenv'; -import { AccountAuthHandler } from './auth/account-auth.js'; -import { CredentialAuthHandler } from './auth/credential-auth.js'; -import { DidAuthHandler } from './auth/did-auth.js'; -import { KeyAuthHandler } from './auth/key-auth.js'; -import { CredentialStatusAuthHandler } from './auth/credential-status-auth.js'; -import { ResourceAuthHandler } from './auth/resource-auth.js'; -import type { AbstractAuthHandler } from './auth/base-auth.js'; -import { LogToHelper } from './auth/logto.js'; -import { PresentationAuthHandler } from './auth/presentation-auth.js'; +import { AccountAuthHandler } from './auth/routes/account-auth.js'; +import { CredentialAuthHandler } from './auth/routes/credential-auth.js'; +import { DidAuthHandler } from './auth/routes/did-auth.js'; +import { KeyAuthHandler } from './auth/routes/key-auth.js'; +import { CredentialStatusAuthHandler } from './auth/routes/credential-status-auth.js'; +import { ResourceAuthHandler } from './auth/routes/resource-auth.js'; +import type { BaseAuthHandler } from './auth/base-auth-handler.js'; +import { LogToHelper } from './auth/logto-helper.js'; +import { PresentationAuthHandler } from './auth/routes/presentation-auth.js'; import { UserService } from '../services/user.js'; +import { configLogToExpress } from '../types/constants.js'; +import { handleAuthRoutes, withLogto } from '@logto/express'; +import { LogToProvider } from './auth/oauth/logto-provider.js'; +import { AuthInfoHandler } from './auth/routes/auth-user-info.js'; dotenv.config(); const { ENABLE_EXTERNAL_DB } = process.env; export class Authentication { - private authHandler: AbstractAuthHandler; + private authHandler: BaseAuthHandler; private isSetup = false; private logToHelper: LogToHelper; @@ -32,8 +36,12 @@ export class Authentication { if (!this.isSetup) { const _r = await this.logToHelper.setup(); if (_r.status !== StatusCodes.OK) { - return response.status(StatusCodes.BAD_GATEWAY).json(_r.error); + return response.status(StatusCodes.BAD_GATEWAY).json({ + error: _r.error, + }); } + const oauthProvider = new LogToProvider(); + oauthProvider.setHelper(this.logToHelper); const didAuthHandler = new DidAuthHandler(); const keyAuthHandler = new KeyAuthHandler(); @@ -41,16 +49,18 @@ export class Authentication { const credentialStatusAuthHandler = new CredentialStatusAuthHandler(); const resourceAuthHandler = new ResourceAuthHandler(); const presentationAuthHandler = new PresentationAuthHandler(); + const authInfoHandler = new AuthInfoHandler(); // Set logToHelper. We do it for avoiding re-asking LogToHelper.setup() in each auth handler // cause it does a lot of requests to LogTo - this.authHandler.setLogToHelper(this.logToHelper); - didAuthHandler.setLogToHelper(this.logToHelper); - keyAuthHandler.setLogToHelper(this.logToHelper); - credentialAuthHandler.setLogToHelper(this.logToHelper); - credentialStatusAuthHandler.setLogToHelper(this.logToHelper); - resourceAuthHandler.setLogToHelper(this.logToHelper); - presentationAuthHandler.setLogToHelper(this.logToHelper); + this.authHandler.setOAuthProvider(oauthProvider); + didAuthHandler.setOAuthProvider(oauthProvider); + keyAuthHandler.setOAuthProvider(oauthProvider); + credentialAuthHandler.setOAuthProvider(oauthProvider); + credentialStatusAuthHandler.setOAuthProvider(oauthProvider); + resourceAuthHandler.setOAuthProvider(oauthProvider); + presentationAuthHandler.setOAuthProvider(oauthProvider); + authInfoHandler.setOAuthProvider(oauthProvider); // Set chain of responsibility this.authHandler @@ -59,7 +69,8 @@ export class Authentication { .setNext(credentialAuthHandler) .setNext(credentialStatusAuthHandler) .setNext(resourceAuthHandler) - .setNext(presentationAuthHandler); + .setNext(presentationAuthHandler) + .setNext(authInfoHandler); this.isSetup = true; } @@ -88,6 +99,28 @@ export class Authentication { next(); } + public async wrapperHandleAuthRoutes(request: Request, response: Response, next: NextFunction) { + const resources = await this.logToHelper.getAllResourcesWithNames(); + return handleAuthRoutes({ ...configLogToExpress, scopes: ['roles'], resources: resources as string[] })( + request, + response, + next + ); + } + + public async withLogtoWrapper(request: Request, response: Response, next: NextFunction) { + if (this.authHandler.skipPath(request.path)) return next(); + try { + return withLogto({ ...configLogToExpress, scopes: ['roles'] })(request, response, next); + } catch (err) { + return response.status(500).send({ + authenticated: false, + error: `${err}`, + customerId: null, + }); + } + } + public async guard(request: Request, response: Response, next: NextFunction) { const { provider } = request.body as { claim: string; provider: string }; if (this.authHandler.skipPath(request.path)) return next(); @@ -103,15 +136,15 @@ export class Authentication { // Only for rules when it's not allowed for unauthorized users // we need to find customer and assign it to the response.locals if (!_resp.data.isAllowedUnauthorized) { - const user = await UserService.instance.get(_resp.data.logToId); + const user = await UserService.instance.get(_resp.data.userId); if (!user) { return response.status(StatusCodes.NOT_FOUND).json({ - error: `Looks like user with logToId ${_resp.data.logToId} is not found`, + error: `Looks like user with logToId ${_resp.data.userId} is not found`, }); } if (user && !user.customer) { return response.status(StatusCodes.NOT_FOUND).json({ - error: `Looks like user with logToId ${_resp.data.logToId} is not assigned to any CredentialService customer`, + error: `Looks like user with logToId ${_resp.data.userId} is not assigned to any CredentialService customer`, }); } response.locals.customer = user.customer; diff --git a/src/services/api_key.ts b/src/services/api_key.ts new file mode 100644 index 00000000..a09ac860 --- /dev/null +++ b/src/services/api_key.ts @@ -0,0 +1,91 @@ +import type { Repository } from 'typeorm'; +import { decodeJWT } from 'did-jwt'; +import { Connection } from '../database/connection/connection.js'; + +import * as dotenv from 'dotenv'; +import type { CustomerEntity } from '../database/entities/customer.entity.js'; +import { APIKeyEntity } from '../database/entities/api.key.entity.js'; +import type { UserEntity } from '../database/entities/user.entity.js'; +import { v4 } from 'uuid'; +dotenv.config(); + +export class APIKeyService { + public apiKeyRepository: Repository; + + public static instance = new APIKeyService(); + + constructor() { + this.apiKeyRepository = Connection.instance.dbConnection.getRepository(APIKeyEntity); + } + + public async create(apiKey: string, customer: CustomerEntity, user: UserEntity): Promise { + const apiKeyId = v4(); + if (!apiKey) { + throw new Error('API key is not specified'); + } + if (!customer) { + throw new Error('API key customer is not specified'); + } + if (!user) { + throw new Error('API key user is not specified'); + } + if (!customer) { + throw new Error('Customer id is not specified'); + } + const expiresAt = await this.getExpiryDate(apiKey); + const apiKeyEntity = new APIKeyEntity(apiKeyId, apiKey, expiresAt, customer, user); + const apiKeyRecord = (await this.apiKeyRepository.insert(apiKeyEntity)).identifiers[0]; + if (!apiKeyRecord) throw new Error(`Cannot create a new API key`); + return apiKeyEntity; + } + + public async update( + apiKeyId: string, + apiKey?: string, + expiresAt?: Date, + customer?: CustomerEntity, + user?: UserEntity + ) { + const existingAPIKey = await this.apiKeyRepository.findOneBy({ apiKeyId }); + if (!existingAPIKey) { + throw new Error(`API with key id ${apiKeyId} not found`); + } + if (apiKey) { + existingAPIKey.apiKey = apiKey; + } + if (expiresAt) { + existingAPIKey.expiresAt = expiresAt; + } + if (customer) { + existingAPIKey.customer = customer; + } + if (user) { + existingAPIKey.user = user; + } + + return await this.apiKeyRepository.save(existingAPIKey); + } + + public async get(apiKeyId: string) { + return await this.apiKeyRepository.findOne({ + where: { apiKeyId }, + relations: ['customer', 'user'], + }); + } + + public async find(where: Record) { + try { + return await this.apiKeyRepository.find({ + where: where, + relations: ['customer', 'user'], + }); + } catch { + return []; + } + } + + public async getExpiryDate(apiKey: string): Promise { + const decrypted = await decodeJWT(apiKey); + return new Date(decrypted.payload.exp ? decrypted.payload.exp * 1000 : 0); + } +} diff --git a/src/services/identity/abstract.ts b/src/services/identity/abstract.ts index 51d4233d..8866646c 100644 --- a/src/services/identity/abstract.ts +++ b/src/services/identity/abstract.ts @@ -31,7 +31,7 @@ import type { FeePaymentOptions, ITrackOperation, StatusOptions, - TrackResult, + ITrackResult, UpdateEncryptedStatusListOptions, UpdateUnencryptedStatusListOptions, VeramoAgent, @@ -40,6 +40,8 @@ import type { import type { IIdentityService } from './index.js'; import type { CustomerEntity } from '../../database/entities/customer.entity.js'; import type { KeyEntity } from '../../database/entities/key.entity.js'; +import type { UserEntity } from '../../database/entities/user.entity'; +import type { APIKeyEntity } from '../../database/entities/api.key.entity'; export abstract class AbstractIdentityService implements IIdentityService { agent?: VeramoAgent; @@ -144,7 +146,7 @@ export abstract class AbstractIdentityService implements IIdentityService { ): Promise { throw new Error(`Not supported`); } - trackOperation(trackOperation: ITrackOperation): Promise { + trackOperation(trackOperation: ITrackOperation): Promise { throw new Error(`Not supported`); } revokeCredentials( @@ -200,4 +202,13 @@ export abstract class AbstractIdentityService implements IIdentityService { ): Promise { throw new Error(`Not supported`); } + setAPIKey(apiKey: string, customer: CustomerEntity, user: UserEntity): Promise { + throw new Error(`Not supported`); + } + updateAPIKey(apiKey: APIKeyEntity, newApiKey: string): Promise { + throw new Error(`Not supported`); + } + getAPIKey(customer: CustomerEntity, user: UserEntity): Promise { + throw new Error(`Not supported`); + } } diff --git a/src/services/identity/index.ts b/src/services/identity/index.ts index 92499703..1e5414fb 100644 --- a/src/services/identity/index.ts +++ b/src/services/identity/index.ts @@ -37,7 +37,7 @@ import type { ITrackOperation, SearchStatusListResult, StatusOptions, - TrackResult, + ITrackResult, UpdateEncryptedStatusListOptions, UpdateUnencryptedStatusListOptions, VeramoAgent, @@ -45,6 +45,8 @@ import type { } from '../../types/shared'; import type { CustomerEntity } from '../../database/entities/customer.entity.js'; import type { KeyEntity } from '../../database/entities/key.entity.js'; +import type { UserEntity } from '../../database/entities/user.entity.js'; +import type { APIKeyEntity } from '../../database/entities/api.key.entity.js'; dotenv.config(); @@ -95,7 +97,7 @@ export interface IIdentityService { statusOptions: CreateEncryptedStatusListOptions, customer: CustomerEntity ): Promise; - trackOperation(trackOperation: ITrackOperation): Promise; + trackOperation(trackOperation: ITrackOperation): Promise; updateUnencryptedStatusList2021( did: string, statusOptions: UpdateUnencryptedStatusListOptions, @@ -145,6 +147,9 @@ export interface IIdentityService { customer: CustomerEntity, symmetricKey: string ): Promise; + setAPIKey(apiKey: string, customer: CustomerEntity, user: UserEntity): Promise; + updateAPIKey(apiKey: APIKeyEntity, newApiKey: string): Promise; + getAPIKey(customer: CustomerEntity, user: UserEntity): Promise; } export class IdentityServiceStrategySetup { diff --git a/src/services/identity/postgres.ts b/src/services/identity/postgres.ts index 163d5b9b..267c3207 100644 --- a/src/services/identity/postgres.ts +++ b/src/services/identity/postgres.ts @@ -34,7 +34,7 @@ import { CreateEncryptedStatusListOptions, FeePaymentOptions, UpdateEncryptedStatusListOptions, - TrackResult, + ITrackResult, ITrackOperation, } from '../../types/shared.js'; import { Connection } from '../../database/connection/connection.js'; @@ -53,6 +53,9 @@ import { OPERATION_CATEGORY_NAME_CREDENTIAL_STATUS, OPERATION_CATEGORY_NAME_RESOURCE, } from '../../types/constants.js'; +import type { UserEntity } from '../../database/entities/user.entity.js'; +import { APIKeyService } from '../api_key.js'; +import type { APIKeyEntity } from '../../database/entities/api.key.entity.js'; dotenv.config(); @@ -470,7 +473,7 @@ export class PostgresIdentityService extends DefaultIdentityService { } } - async trackOperation(trackOperation: ITrackOperation): Promise { + async trackOperation(trackOperation: ITrackOperation): Promise { // For now it tracks only resource-related operations but in future we will track all other actions switch (trackOperation.category) { case OPERATION_CATEGORY_NAME_RESOURCE: @@ -488,7 +491,7 @@ export class PostgresIdentityService extends DefaultIdentityService { } } - async trackResourceOperation(trackOperation: ITrackOperation): Promise { + async trackResourceOperation(trackOperation: ITrackOperation): Promise { const customer = trackOperation.customer; const did = trackOperation.did; const resource = trackOperation.data.resource; @@ -526,4 +529,44 @@ export class PostgresIdentityService extends DefaultIdentityService { error: '', }; } + async setAPIKey(apiKey: string, customer: CustomerEntity, user: UserEntity): Promise { + const keys = await APIKeyService.instance.find({ customer: customer, user: user }); + if (keys.length > 0) { + throw new Error(`API key for customer ${customer.customerId} and user ${user.logToId} already exists`); + } + const apiKeyEntity = await APIKeyService.instance.create(apiKey, customer, user); + if (!apiKeyEntity) { + throw new Error(`Cannot create API key for customer ${customer.customerId} and user ${user.logToId}`); + } + return apiKeyEntity; + } + + async updateAPIKey(apiKey: APIKeyEntity, newApiKey: string): Promise { + const key = await APIKeyService.instance.get(apiKey.apiKeyId); + if (!key) { + throw new Error(`API key with id ${apiKey.apiKeyId} not found`); + } + const apiKeyEntity = await APIKeyService.instance.update( + key.apiKeyId, + newApiKey, + await APIKeyService.instance.getExpiryDate(newApiKey) + ); + if (!apiKeyEntity) { + throw new Error(`Cannot update API key with id ${apiKey.apiKeyId}`); + } + return apiKeyEntity; + } + + async getAPIKey(customer: CustomerEntity, user: UserEntity): Promise { + const keys = await APIKeyService.instance.find({ customer: customer, user: user }); + if (keys.length > 1) { + throw new Error( + `For the customer with customer id ${customer.customerId} and user with logToId ${user.logToId} there more then 1 API key` + ); + } + if (keys.length == 0) { + return undefined; + } + return keys[0]; + } } diff --git a/src/services/operation.ts b/src/services/operation.ts index 0ed4e0b6..45106786 100644 --- a/src/services/operation.ts +++ b/src/services/operation.ts @@ -3,91 +3,81 @@ import type { Repository } from 'typeorm'; import { Connection } from '../database/connection/connection.js'; import * as dotenv from 'dotenv'; -import { PaymentAccountEntity } from '../database/entities/payment.account.entity.js'; -import type { CustomerEntity } from '../database/entities/customer.entity.js'; -import type { KeyEntity } from '../database/entities/key.entity.js'; -import { getCosmosAccount } from '@cheqd/sdk'; +import { OperationEntity } from '../database/entities/operation.entity.js'; +import { v4 } from 'uuid'; dotenv.config(); -export class PaymentAccountService { - public paymentAccountRepository: Repository; +export class OperationService { + public operationRepository: Repository; - public static instance = new PaymentAccountService(); + public static instance = new OperationService(); constructor() { - this.paymentAccountRepository = Connection.instance.dbConnection.getRepository(PaymentAccountEntity); + this.operationRepository = Connection.instance.dbConnection.getRepository(OperationEntity); } public async create( - namespace: string, - isDefault: boolean, - customer: CustomerEntity, - key: KeyEntity - ): Promise { - const address = getCosmosAccount(key.kid); - const existing = await this.find({ address: address }); - if (!address) { - throw new Error('Account address is not specified'); + category: string, + operationName: string, + defaultFee: number, + deprecated = false + ): Promise { + if (!category) { + throw new Error('Operation category is not specified'); } - if (existing.length > 0) { - throw new Error( - `Cannot create a new payment account since the payment account with same address ${address} already exists` - ); + if (!operationName) { + throw new Error('Operation name is not specified'); } - if (!namespace) { - throw new Error('Account namespace is not specified'); + if (!defaultFee) { + throw new Error('Operation default fee is not specified'); } - if (!customer) { - throw new Error('Customer id is not specified'); + if (!deprecated) { + throw new Error('Operation deprecated is not specified'); } - if (!key) { - throw new Error('Key id is not specified'); - } - const paymentAccount = new PaymentAccountEntity(address, namespace, isDefault, customer, key); - const paymentAccountEntity = (await this.paymentAccountRepository.insert(paymentAccount)).identifiers[0]; - if (!paymentAccountEntity) throw new Error(`Cannot create a new payment account`); + const operationId = v4(); + const operationEntity = new OperationEntity(operationId, category, operationName, defaultFee, deprecated); + const operation = (await this.operationRepository.insert(operationEntity)).identifiers[0]; + if (!operation) throw new Error(`Cannot create a new operation`); - return paymentAccount; + return operationEntity; } public async update( - address: string, - namespace?: string, - isDefault?: boolean, - customer?: CustomerEntity, - key?: KeyEntity + operationId: string, + category: string, + operationName: string, + defaultFee: number, + deprecated = false ) { - const existingPaymentAccount = await this.paymentAccountRepository.findOneBy({ address }); - if (!existingPaymentAccount) { - throw new Error(`address not found`); + const existingOperation = await this.get(operationId); + if (!existingOperation) { + throw new Error(`Operation with id ${operationId} does not exist`); } - if (customer) { - existingPaymentAccount.customer = customer; + if (category) { + existingOperation.category = category; } - if (namespace) { - existingPaymentAccount.namespace = namespace; + if (operationName) { + existingOperation.operationName = operationName; } - if (key) { - existingPaymentAccount.key = key; + if (defaultFee) { + existingOperation.defaultFee = defaultFee; } - if (isDefault) { - existingPaymentAccount.isDefault = isDefault; + if (deprecated) { + existingOperation.deprecated = deprecated; } - return await this.paymentAccountRepository.save(existingPaymentAccount); + return await this.operationRepository.save(existingOperation); } - public async get(address: string) { - return await this.paymentAccountRepository.findOne({ - where: { address }, - relations: ['customer', 'key'], + public async get(operationId: string) { + return await this.operationRepository.findOne({ + where: { operationId }, }); } public async find(where: Record) { - return await this.paymentAccountRepository.find({ + return await this.operationRepository.find({ where: where, - relations: ['customer', 'key'], }); } } diff --git a/src/static/swagger.json b/src/static/swagger.json index c9dfc995..961a61d2 100644 --- a/src/static/swagger.json +++ b/src/static/swagger.json @@ -431,6 +431,36 @@ } } }, + "/account/idtoken": { + "get": { + "tags": [ + "Account" + ], + "summary": "Fetch IdToken.", + "description": "This endpoint returns IdToken as JWT with list of user roles inside", + "responses": { + "200": { + "description": "The request was successful.", + "content": { + "application/json": { + "idToken": { + "type": "string" + } + } + } + }, + "400": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "401": { + "$ref": "#/components/schemas/UnauthorizedError" + }, + "500": { + "$ref": "#/components/schemas/InternalError" + } + } + } + }, "/key/create": { "post": { "tags": [ diff --git a/src/types/authentication.ts b/src/types/authentication.ts index 7e16fa00..6cdf1061 100644 --- a/src/types/authentication.ts +++ b/src/types/authentication.ts @@ -1,11 +1,9 @@ -import type { Request, Response } from 'express'; - export enum Namespaces { Testnet = 'testnet', Mainnet = 'mainnet', } -export class MethodToScope { +export class MethodToScopeRule { private route: string; private method: string; private scope: string; @@ -42,6 +40,7 @@ export class MethodToScope { } private checkScope(namespace: string): boolean { + // If scope is empty or namespace is not needed - return true if (this.scope === '' || this.isSkipNamespace()) { return true; } @@ -63,12 +62,16 @@ export class MethodToScope { public isSkipNamespace(): boolean { return this.options.skipNamespace; } + + public isEmpty(): boolean { + return this.route === '' && this.method === '' && this.scope === ''; + } } -export interface IAuthResponse { +export interface IAuthResponse extends ICommonErrorResponse { status: number; data: { - logToId: string; + userId: string; scopes: string[]; namespace: Namespaces; isAllowedUnauthorized: boolean; @@ -81,15 +84,3 @@ export interface ICommonErrorResponse { error: string; data: any; } - -export interface IAuthResourceHandler { - setNext(handler: IAuthResourceHandler): IAuthResourceHandler; - handle(request: Request, response: Response): Promise; - skipPath(path: string): boolean; - - // Getters - getNamespace(): string; - getScopes(): string[] | unknown; - getLogToId(): string; - getToken(): string; -} diff --git a/src/types/constants.ts b/src/types/constants.ts index 5fdb6e67..cc6808fc 100644 --- a/src/types/constants.ts +++ b/src/types/constants.ts @@ -40,7 +40,7 @@ export const configLogToExpress = { (function () { throw new Error('APPLICATION_BASE_URL is not defined'); })(), - getAccessToken: true, + getAccessToken: false, fetchUserInfo: true, }; diff --git a/src/types/shared.ts b/src/types/shared.ts index d16e476d..e63f8e07 100644 --- a/src/types/shared.ts +++ b/src/types/shared.ts @@ -387,7 +387,7 @@ export interface IResourceTrack { symmetricKey: string; } -export interface TrackResult { +export interface ITrackResult { created: boolean; error?: string; }