From 48f7f451d880573cb43dba09213796b57e351246 Mon Sep 17 00:00:00 2001 From: soruly Date: Sat, 18 Jun 2022 11:55:20 +0000 Subject: [PATCH] Add FIDO2 web authn --- .env.example | 5 +- .gitignore | 2 + package-lock.json | 741 +++++++++++++++++++++++++++++++++ package.json | 2 + server.js | 108 ++++- src/get-login.js | 20 + src/get-register.js | 11 + src/post-login.js | 60 +++ src/post-register.js | 47 +++ static/{client.js => index.js} | 0 static/login.js | 44 ++ static/register.js | 41 ++ index.ejs => view/index.ejs | 2 +- view/login.ejs | 21 + view/register.ejs | 21 + 15 files changed, 1101 insertions(+), 24 deletions(-) create mode 100644 src/get-login.js create mode 100644 src/get-register.js create mode 100644 src/post-login.js create mode 100644 src/post-register.js rename static/{client.js => index.js} (100%) create mode 100644 static/login.js create mode 100644 static/register.js rename index.ejs => view/index.ejs (96%) create mode 100644 view/login.ejs create mode 100644 view/register.ejs diff --git a/.env.example b/.env.example index c6efa8d..22730df 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,9 @@ SERVER_PORT=3000 SERVER_ADDR=127.0.0.1 +#SERVER_NAME=localhost #BLACKLIST_UA=Bot|MSIE|Bytespider|Baidu|Sogou|FB_AN|FB_IOS|FB_IAB|Instagram #WHITELIST_COUNTRY=ZZ|HK|TW #GEO_LITE_COUNTRY_PATH=/etc/GeoIP/GeoLite2-Country.mmdb -#GEO_LITE_ASN_PATH=/etc/GeoIP/GeoLite2-ASN.mmdb \ No newline at end of file +#GEO_LITE_ASN_PATH=/etc/GeoIP/GeoLite2-ASN.mmdb +#ENABLE_FIDO2=1 +#ALLOW_REGISTER=1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3674626..5ddcaa0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,5 @@ typings/ # local data storage data/ +registered/ +session/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6cfeae9..499b1e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,16 +10,23 @@ "license": "MIT", "dependencies": { "@maxmind/geoip2-node": "^3.4.0", + "cookie-parser": "^1.4.6", "dotenv": "^16.0.1", "ejs": "^3.1.8", "express": "^4.18.1", "express-rate-limit": "^6.4.0", + "fido2-lib": "^3.2.3", "fs-extra": "^10.1.0" }, "devDependencies": { "prettier": "^2.7.1" } }, + "node_modules/@hexagon/base64": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.0.19.tgz", + "integrity": "sha512-+ZJdUaD9sthgMbdEiYZ5jwiTG/ZtILx0/LSDkdxi/pK/BSmJzIAT+IYuMYSKwjPdGk6odwbJVQ9H8HpepDZy9Q==" + }, "node_modules/@maxmind/geoip2-node": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-3.4.0.tgz", @@ -31,6 +38,42 @@ "maxmind": "^4.2.0" } }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.1.9.tgz", + "integrity": "sha512-Ipio+pXGpL/Vb0qB4GnOgFMgc1RAhKHOVy24rQYLvmOAVp9z/aFb+VdIiQH09NjgvGVmaWOUqSWd9vRHk3xbrg==", + "dependencies": { + "asn1js": "^3.0.4", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz", + "integrity": "sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg==", + "dependencies": { + "@peculiar/asn1-schema": "^2.1.6", + "@peculiar/json-schema": "^1.1.12", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0", + "webcrypto-core": "^1.7.4" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -62,6 +105,19 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -120,6 +176,14 @@ "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.0.tgz", + "integrity": "sha512-TyOlxeS92FcMOaJwAVq5gwqW0vfkWUv5W+ErwdbBzolcUN/9XYpCKWvCV21jjzXU550D9Wt4GgE8Pr1vVbR+wQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -160,6 +224,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cbor-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-1.0.0.tgz", + "integrity": "sha512-2y71EsPT4dJjtS24JIZfzUodm7kNrJJouIL7U4YiO1bl5QJyCHz9j810x/d+A/L+1BWQCE0771TiBrVmDL0MOA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.2", + "node-gyp-build": "^4.2.3" + } + }, + "node_modules/cbor-x": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.2.1.tgz", + "integrity": "sha512-vUgwxN/hCyMUuFsYaMTYhfpK0rwBpGho8CqCLEXSn3tGfaPL3fruB0lrZVGiq1v5EzTrqcrLVxJ2Ul9zqukhMw==", + "dependencies": { + "esbuild": "^0.13.15" + }, + "optionalDependencies": { + "cbor-extract": "^1.0.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -223,6 +309,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -293,6 +399,238 @@ "node": ">= 0.8" } }, + "node_modules/esbuild": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", + "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.13.15", + "esbuild-darwin-64": "0.13.15", + "esbuild-darwin-arm64": "0.13.15", + "esbuild-freebsd-64": "0.13.15", + "esbuild-freebsd-arm64": "0.13.15", + "esbuild-linux-32": "0.13.15", + "esbuild-linux-64": "0.13.15", + "esbuild-linux-arm": "0.13.15", + "esbuild-linux-arm64": "0.13.15", + "esbuild-linux-mips64le": "0.13.15", + "esbuild-linux-ppc64le": "0.13.15", + "esbuild-netbsd-64": "0.13.15", + "esbuild-openbsd-64": "0.13.15", + "esbuild-sunos-64": "0.13.15", + "esbuild-windows-32": "0.13.15", + "esbuild-windows-64": "0.13.15", + "esbuild-windows-arm64": "0.13.15" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", + "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", + "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", + "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", + "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", + "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", + "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", + "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", + "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", + "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", + "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", + "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", + "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", + "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", + "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-windows-32": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", + "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", + "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", + "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -366,6 +704,23 @@ "node >=0.6.0" ] }, + "node_modules/fido2-lib": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/fido2-lib/-/fido2-lib-3.2.3.tgz", + "integrity": "sha512-yERxwu3k8oh360I5s1ujc3k4ZQ2tHmjvcFD3kZHm+hvF4/TBFun39zz914krI1E8+pQgVbqCuxYJ+1HrTz2TFA==", + "dependencies": { + "@hexagon/base64": "^1.0.19", + "@peculiar/webcrypto": "^1.4.0", + "asn1js": "^3.0.2", + "cbor-x": "~1.2.1", + "jose": "^4.7.0", + "pkijs": "^3.0.5", + "tldts": "^5.7.81" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -557,6 +912,14 @@ "node": ">=10" } }, + "node_modules/jose": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.8.1.tgz", + "integrity": "sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -692,6 +1055,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "optional": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -700,6 +1069,17 @@ "node": ">= 0.6" } }, + "node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -732,6 +1112,21 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pkijs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.0.5.tgz", + "integrity": "sha512-J6P30yzU7qSbuJIBaclwN93WbyoxjVlkYMgjQmE9wWkPUTHVu2cH6lkjWcIr2WAub5KH38BA1Eyeb4s9apUPTg==", + "dependencies": { + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -759,6 +1154,22 @@ "node": ">= 0.10" } }, + "node_modules/pvtsutils": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.2.tgz", + "integrity": "sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -917,6 +1328,22 @@ "node": ">=6" } }, + "node_modules/tldts": { + "version": "5.7.82", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-5.7.82.tgz", + "integrity": "sha512-TMYZhjwjxoT5HprsYis5JNd0Y95XuPh5Ta+tkPTVI1RlCT+1RTK3Ejp+r9xoYbV8cF4b7+nuOzQ31fm+3laYNw==", + "dependencies": { + "tldts-core": "^5.7.82" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "5.7.82", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-5.7.82.tgz", + "integrity": "sha512-cfUY8fjmfwJDcocXfFlUbngftKwzx7NzFLcVf8/FTLjRX9hgQE8MIls0KNTRsvYMD6x4oXgZipAAv0WyuDLCxw==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -925,6 +1352,11 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -992,9 +1424,26 @@ "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } + }, + "node_modules/webcrypto-core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.5.tgz", + "integrity": "sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A==", + "dependencies": { + "@peculiar/asn1-schema": "^2.1.6", + "@peculiar/json-schema": "^1.1.12", + "asn1js": "^3.0.1", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } } }, "dependencies": { + "@hexagon/base64": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.0.19.tgz", + "integrity": "sha512-+ZJdUaD9sthgMbdEiYZ5jwiTG/ZtILx0/LSDkdxi/pK/BSmJzIAT+IYuMYSKwjPdGk6odwbJVQ9H8HpepDZy9Q==" + }, "@maxmind/geoip2-node": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-3.4.0.tgz", @@ -1006,6 +1455,36 @@ "maxmind": "^4.2.0" } }, + "@peculiar/asn1-schema": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.1.9.tgz", + "integrity": "sha512-Ipio+pXGpL/Vb0qB4GnOgFMgc1RAhKHOVy24rQYLvmOAVp9z/aFb+VdIiQH09NjgvGVmaWOUqSWd9vRHk3xbrg==", + "requires": { + "asn1js": "^3.0.4", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } + }, + "@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@peculiar/webcrypto": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz", + "integrity": "sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg==", + "requires": { + "@peculiar/asn1-schema": "^2.1.6", + "@peculiar/json-schema": "^1.1.12", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0", + "webcrypto-core": "^1.7.4" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1028,6 +1507,16 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "requires": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + } + }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -1076,6 +1565,11 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "bytestreamjs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.0.tgz", + "integrity": "sha512-TyOlxeS92FcMOaJwAVq5gwqW0vfkWUv5W+ErwdbBzolcUN/9XYpCKWvCV21jjzXU550D9Wt4GgE8Pr1vVbR+wQ==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1101,6 +1595,25 @@ "type-fest": "^1.2.1" } }, + "cbor-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-1.0.0.tgz", + "integrity": "sha512-2y71EsPT4dJjtS24JIZfzUodm7kNrJJouIL7U4YiO1bl5QJyCHz9j810x/d+A/L+1BWQCE0771TiBrVmDL0MOA==", + "optional": true, + "requires": { + "nan": "^2.14.2", + "node-gyp-build": "^4.2.3" + } + }, + "cbor-x": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.2.1.tgz", + "integrity": "sha512-vUgwxN/hCyMUuFsYaMTYhfpK0rwBpGho8CqCLEXSn3tGfaPL3fruB0lrZVGiq1v5EzTrqcrLVxJ2Ul9zqukhMw==", + "requires": { + "cbor-extract": "^1.0.0", + "esbuild": "^0.13.15" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1146,6 +1659,22 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, + "cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + } + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -1197,6 +1726,132 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "esbuild": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", + "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", + "requires": { + "esbuild-android-arm64": "0.13.15", + "esbuild-darwin-64": "0.13.15", + "esbuild-darwin-arm64": "0.13.15", + "esbuild-freebsd-64": "0.13.15", + "esbuild-freebsd-arm64": "0.13.15", + "esbuild-linux-32": "0.13.15", + "esbuild-linux-64": "0.13.15", + "esbuild-linux-arm": "0.13.15", + "esbuild-linux-arm64": "0.13.15", + "esbuild-linux-mips64le": "0.13.15", + "esbuild-linux-ppc64le": "0.13.15", + "esbuild-netbsd-64": "0.13.15", + "esbuild-openbsd-64": "0.13.15", + "esbuild-sunos-64": "0.13.15", + "esbuild-windows-32": "0.13.15", + "esbuild-windows-64": "0.13.15", + "esbuild-windows-arm64": "0.13.15" + } + }, + "esbuild-android-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", + "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", + "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", + "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", + "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", + "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", + "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", + "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", + "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", + "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", + "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", + "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", + "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", + "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", + "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", + "optional": true + }, + "esbuild-windows-32": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", + "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", + "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", + "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", + "optional": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1256,6 +1911,20 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" }, + "fido2-lib": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/fido2-lib/-/fido2-lib-3.2.3.tgz", + "integrity": "sha512-yERxwu3k8oh360I5s1ujc3k4ZQ2tHmjvcFD3kZHm+hvF4/TBFun39zz914krI1E8+pQgVbqCuxYJ+1HrTz2TFA==", + "requires": { + "@hexagon/base64": "^1.0.19", + "@peculiar/webcrypto": "^1.4.0", + "asn1js": "^3.0.2", + "cbor-x": "~1.2.1", + "jose": "^4.7.0", + "pkijs": "^3.0.5", + "tldts": "^5.7.81" + } + }, "filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -1404,6 +2073,11 @@ "minimatch": "^3.0.4" } }, + "jose": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.8.1.tgz", + "integrity": "sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw==" + }, "json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -1499,11 +2173,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "optional": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "optional": true + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -1527,6 +2213,18 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "pkijs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.0.5.tgz", + "integrity": "sha512-J6P30yzU7qSbuJIBaclwN93WbyoxjVlkYMgjQmE9wWkPUTHVu2cH6lkjWcIr2WAub5KH38BA1Eyeb4s9apUPTg==", + "requires": { + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + } + }, "prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -1542,6 +2240,19 @@ "ipaddr.js": "1.9.1" } }, + "pvtsutils": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.2.tgz", + "integrity": "sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ==", + "requires": { + "tslib": "^2.4.0" + } + }, + "pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==" + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -1652,11 +2363,29 @@ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" }, + "tldts": { + "version": "5.7.82", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-5.7.82.tgz", + "integrity": "sha512-TMYZhjwjxoT5HprsYis5JNd0Y95XuPh5Ta+tkPTVI1RlCT+1RTK3Ejp+r9xoYbV8cF4b7+nuOzQ31fm+3laYNw==", + "requires": { + "tldts-core": "^5.7.82" + } + }, + "tldts-core": { + "version": "5.7.82", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-5.7.82.tgz", + "integrity": "sha512-cfUY8fjmfwJDcocXfFlUbngftKwzx7NzFLcVf8/FTLjRX9hgQE8MIls0KNTRsvYMD6x4oXgZipAAv0WyuDLCxw==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -1700,6 +2429,18 @@ "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } + }, + "webcrypto-core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.5.tgz", + "integrity": "sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A==", + "requires": { + "@peculiar/asn1-schema": "^2.1.6", + "@peculiar/json-schema": "^1.1.12", + "asn1js": "^3.0.1", + "pvtsutils": "^1.3.2", + "tslib": "^2.4.0" + } } } } diff --git a/package.json b/package.json index 9bbf803..7f51281 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,12 @@ "homepage": "https://github.com/soruly/2fa#readme", "dependencies": { "@maxmind/geoip2-node": "^3.4.0", + "cookie-parser": "^1.4.6", "dotenv": "^16.0.1", "ejs": "^3.1.8", "express": "^4.18.1", "express-rate-limit": "^6.4.0", + "fido2-lib": "^3.2.3", "fs-extra": "^10.1.0" }, "devDependencies": { diff --git a/server.js b/server.js index ad986f2..c2c77c2 100644 --- a/server.js +++ b/server.js @@ -5,22 +5,51 @@ import crypto from "crypto"; import { performance } from "perf_hooks"; import express from "express"; import rateLimit from "express-rate-limit"; +import cookieParser from "cookie-parser"; +import { Fido2Lib } from "fido2-lib"; + import getIpInfo from "./src/get-ip-info.js"; +import getRegister from "./src/get-register.js"; +import postRegister from "./src/post-register.js"; +import getLogin from "./src/get-login.js"; +import postLogin from "./src/post-login.js"; const { SERVER_ADDR = "0.0.0.0", SERVER_PORT = 3000, + SERVER_NAME, BLACKLIST_UA, WHITELIST_COUNTRY, + ENABLE_FIDO2, + ALLOW_REGISTER, } = process.env; +if (!fs.existsSync("data/latest.json")) fs.outputFileSync("data/latest.json", JSON.stringify([])); +fs.ensureDirSync("registered"); +fs.ensureDirSync("session"); + const app = express(); app.disable("x-powered-by"); app.set("trust proxy", 1); app.set("view engine", "ejs"); -app.set("views", path.resolve(".")); +app.set("views", path.resolve("./view")); + +app.locals.f2l = + ENABLE_FIDO2 && + new Fido2Lib({ + timeout: 60000, + rpId: SERVER_NAME, + rpName: "ACME", + rpIcon: `https://${SERVER_NAME}/favicon.png`, + challengeSize: 128, + attestation: "direct", + cryptoParams: [-7], + // authenticatorAttachment: "cross-platform", + // authenticatorRequireResidentKey: false, + authenticatorUserVerification: "preferred", + }); app.use((req, res, next) => { const { ASN, country } = getIpInfo(req.ip); @@ -63,27 +92,11 @@ app.use((req, res, next) => { }); app.use(express.json()); - -app.use((req, res, next) => { - res.set("Access-Control-Allow-Origin", "*"); - res.set("Access-Control-Allow-Methods", "GET, OPTIONS"); - res.set("Referrer-Policy", "no-referrer"); - res.set("X-Content-Type-Options", "nosniff"); - res.set( - "Content-Security-Policy", - [ - "default-src 'self'", - "base-uri 'none'", - "frame-ancestors 'none'", - "block-all-mixed-content", - ].join("; ") - ); - next(); -}); +app.use(cookieParser()); app.use( rateLimit({ - max: 120, // 120 requests per IP address (per node.js process) + max: 600, // 600 requests per IP address (per node.js process) windowMs: 60 * 1000, // per 1 minute }) ); @@ -91,6 +104,15 @@ app.use( app.get(/[^\/]+\.[^\/]+$/, express.static("./static", { maxAge: 1000 * 60 * 60 * 24 })); app.delete("/", (req, res) => { + if ( + ENABLE_FIDO2 && + !fs + .readdirSync("session") + .map((e) => e.replace(".json", "")) + .includes(req.cookies.session) + ) { + return res.status(403); + } fs.copyFileSync("data/latest.json", `data/${Date.now()}.json`); fs.writeFileSync( "data/latest.json", @@ -104,6 +126,15 @@ app.delete("/", (req, res) => { }); app.post("/", (req, res) => { + if ( + ENABLE_FIDO2 && + !fs + .readdirSync("session") + .map((e) => e.replace(".json", "")) + .includes(req.cookies.session) + ) { + return res.status(403); + } fs.copyFileSync("data/latest.json", `data/${Date.now()}.json`); fs.writeFileSync( "data/latest.json", @@ -112,7 +143,42 @@ app.post("/", (req, res) => { return res.sendStatus(204); }); +app.get("/login", rateLimit({ max: 5, windowMs: 60 * 1000 }), getLogin); +app.post("/login", rateLimit({ max: 5, windowMs: 60 * 1000 }), postLogin); + +app.get("/register", rateLimit({ max: 5, windowMs: 60 * 1000 }), getRegister); +app.post("/register", rateLimit({ max: 5, windowMs: 60 * 1000 }), postRegister); + +app.get("/reg", async (req, res) => { + if (ENABLE_FIDO2 && ALLOW_REGISTER) return res.render("register"); + return res.status(403).send("Registration disabled"); +}); + app.get("/", async (req, res) => { + res.set("Access-Control-Allow-Origin", "*"); + res.set("Access-Control-Allow-Methods", "GET, OPTIONS"); + res.set("Referrer-Policy", "no-referrer"); + res.set("X-Content-Type-Options", "nosniff"); + res.set( + "Content-Security-Policy", + [ + "default-src 'self'", + "base-uri 'none'", + "frame-ancestors 'none'", + "block-all-mixed-content", + ].join("; ") + ); + + if ( + ENABLE_FIDO2 && + !fs + .readdirSync("session") + .map((e) => e.replace(".json", "")) + .includes(req.cookies.session) + ) { + return res.render("login", { ALLOW_REGISTER }); + } + if (req.headers.accept?.toLowerCase() === "text/event-stream") { res.set({ "Cache-Control": "no-cache", @@ -143,13 +209,11 @@ app.get("/", async (req, res) => { return res.render("index", { list: JSON.parse(fs.readFileSync("data/latest.json")).map(({ name, otp }) => ({ name, - otp: getOtp(otp), + otp: "", })), }); }); -if (!fs.existsSync("data/latest.json")) fs.outputFileSync("data/latest.json", JSON.stringify([])); - app.listen(SERVER_PORT, SERVER_ADDR, () => console.log(`Media server listening on ${SERVER_ADDR}:${SERVER_PORT}`) ); diff --git a/src/get-login.js b/src/get-login.js new file mode 100644 index 0000000..087addc --- /dev/null +++ b/src/get-login.js @@ -0,0 +1,20 @@ +import fs from "fs-extra"; + +const { ENABLE_FIDO2 } = process.env; + +export default async (req, res) => { + if (!ENABLE_FIDO2) return res.status(403).send("FIDO2 disabled"); + const assertionOptions = await req.app.locals.f2l.assertionOptions(); + req.app.locals.assertionOptions = assertionOptions; + return res.send({ + ...assertionOptions, + allowCredentials: fs + .readdirSync("registered") + .filter((e) => e.match(/\.pem$/)) + .map((e) => ({ + type: "public-key", + id: Buffer.from(e.replace(".pem", ""), "base64url").toString("base64"), + })), + challenge: Buffer.from(assertionOptions.challenge).toString("base64"), + }); +}; diff --git a/src/get-register.js b/src/get-register.js new file mode 100644 index 0000000..869e678 --- /dev/null +++ b/src/get-register.js @@ -0,0 +1,11 @@ +const { ENABLE_FIDO2, ALLOW_REGISTER } = process.env; + +export default async (req, res) => { + if (!ENABLE_FIDO2 || !ALLOW_REGISTER) return res.status(403).send("Registration disabled"); + const registrationOptions = await req.app.locals.f2l.attestationOptions(); + req.app.locals.registrationOptions = registrationOptions; + return res.send({ + ...registrationOptions, + challenge: Buffer.from(registrationOptions.challenge).toString("base64"), + }); +}; diff --git a/src/post-login.js b/src/post-login.js new file mode 100644 index 0000000..dbea152 --- /dev/null +++ b/src/post-login.js @@ -0,0 +1,60 @@ +import fs from "fs-extra"; +import crypto from "crypto"; + +const { SERVER_NAME, ENABLE_FIDO2 } = process.env; + +export default async (req, res) => { + if (!ENABLE_FIDO2) return res.status(403).send("FIDO2 disabled"); + try { + const authnResult = await req.app.locals.f2l.assertionResult( + { + id: req.body.id, + rawId: new Uint8Array(Buffer.from(req.body.rawId, "base64")).buffer, + response: { + authenticatorData: new Uint8Array( + Buffer.from(req.body.response.authenticatorData, "base64") + ).buffer, + clientDataJSON: req.body.response.clientDataJSON, + signature: req.body.response.signature, + }, + }, + { + userHandle: null, + challenge: req.app.locals.assertionOptions.challenge, + origin: `https://${SERVER_NAME}`, + factor: "either", + publicKey: fs.readFileSync(`registered/${req.body.id}.pem`, "utf8"), + prevCounter: 0, + } + ); + req.app.locals.assertionOptions = null; + console.log(authnResult); + const uuid = crypto.webcrypto.randomUUID(); + fs.outputFileSync( + `session/${uuid}.json`, + JSON.stringify( + { + id: req.body.id, + uuid, + ip: req.ip, + ASN: res.locals.ASN, + country: res.locals.country, + agent: req.headers["user-agent"], + }, + null, + 2 + ) + ); + // session cookie + res.setHeader("Set-Cookie", `session=${uuid}; Path=/; Secure; HttpOnly; SameSite=Strict`); + // res.setHeader( + // "Set-Cookie", + // `session=${uuid}; Path=/; Expires=${new Date( + // Date.now() + 1000 * 60 * 60 * 24 * 365 + // ).toGMTString()}; Secure; HttpOnly; SameSite=Strict` + // ); + return res.sendStatus(204); + } catch (e) { + return res.status(400).send(e.toString()); + } +}; diff --git a/src/post-register.js b/src/post-register.js new file mode 100644 index 0000000..4349730 --- /dev/null +++ b/src/post-register.js @@ -0,0 +1,47 @@ +import fs from "fs-extra"; +const { SERVER_NAME, ENABLE_FIDO2, ALLOW_REGISTER } = process.env; + +export default async (req, res) => { + if (!ENABLE_FIDO2 || !ALLOW_REGISTER) return res.status(403).send("Registration disabled"); + try { + const regResult = await req.app.locals.f2l.attestationResult( + { + id: req.body.id, + rawId: new Uint8Array(Buffer.from(req.body.rawId, "base64")).buffer, + response: { + attestationObject: new Uint8Array( + Buffer.from(req.body.response.attestationObject, "base64") + ).buffer, + clientDataJSON: req.body.response.clientDataJSON, + }, + }, + { + challenge: req.app.locals.registrationOptions.challenge, + origin: `https://${SERVER_NAME}`, + factor: "either", + } + ); + req.app.locals.registrationOptions = null; + console.log(regResult); + fs.outputFileSync( + `registered/${req.body.id}.pem`, + regResult.authnrData.get("credentialPublicKeyPem") + ); + fs.outputFileSync( + `registered/${req.body.id}.json`, + JSON.stringify( + { + ip: req.ip, + ASN: res.locals.ASN, + country: res.locals.country, + agent: req.headers["user-agent"], + }, + null, + 2 + ) + ); + return res.sendStatus(204); + } catch (e) { + return res.status(400).send(e.toString()); + } +}; diff --git a/static/client.js b/static/index.js similarity index 100% rename from static/client.js rename to static/index.js diff --git a/static/login.js b/static/login.js new file mode 100644 index 0000000..1088545 --- /dev/null +++ b/static/login.js @@ -0,0 +1,44 @@ +const arrayBufferToBase64 = (ab) => btoa(String.fromCharCode(...new Uint8Array(ab))); +const base64ToArrayBuffer = (str) => Uint8Array.from(atob(str), (c) => c.charCodeAt(0)).buffer; + +(async (e) => { + const assertionOptions = await fetch("/login").then((e) => e.json()); + assertionOptions.challenge = base64ToArrayBuffer(assertionOptions.challenge); + try { + const { + authenticatorAttachment, + id, + rawId, + response: { authenticatorData, clientDataJSON, signature, userHandle }, + type, + } = await navigator.credentials.get({ + publicKey: { + ...assertionOptions, + allowCredentials: assertionOptions.allowCredentials.map(({ type, id }) => ({ + type, + id: base64ToArrayBuffer(id), + })), + }, + }); + + const res = await fetch("/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + authenticatorAttachment, + id, + rawId: arrayBufferToBase64(rawId), + response: { + authenticatorData: arrayBufferToBase64(authenticatorData), + clientDataJSON: arrayBufferToBase64(clientDataJSON), + signature: arrayBufferToBase64(signature), + userHandle, + }, + type, + }), + }); + if (res.status === 204) window.location.reload(); + } catch (e) { + document.body.innerText = e; + } +})(); diff --git a/static/register.js b/static/register.js new file mode 100644 index 0000000..894a6d1 --- /dev/null +++ b/static/register.js @@ -0,0 +1,41 @@ +const arrayBufferToBase64 = (ab) => btoa(String.fromCharCode(...new Uint8Array(ab))); +const base64ToArrayBuffer = (str) => Uint8Array.from(atob(str), (c) => c.charCodeAt(0)).buffer; + +(async (e) => { + try { + const registrationOptions = await fetch("/register").then((e) => e.json()); + registrationOptions.challenge = base64ToArrayBuffer(registrationOptions.challenge); + registrationOptions.user = { + id: new Uint8Array(16), + name: "soruly", + displayName: "soruly", + }; + const { + authenticatorAttachment, + id, + rawId, + response: { attestationObject, clientDataJSON }, + type, + } = await navigator.credentials.create({ + publicKey: registrationOptions, + }); + + const res = await fetch("/register", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + authenticatorAttachment, + id, + rawId: arrayBufferToBase64(rawId), + response: { + attestationObject: arrayBufferToBase64(attestationObject), + clientDataJSON: arrayBufferToBase64(clientDataJSON), + }, + type, + }), + }); + if (res.status === 204) window.location.href = "/"; + } catch (e) { + document.body.innerText = e; + } +})(); diff --git a/index.ejs b/view/index.ejs similarity index 96% rename from index.ejs rename to view/index.ejs index 465bed1..87e24d1 100644 --- a/index.ejs +++ b/view/index.ejs @@ -38,5 +38,5 @@ - + diff --git a/view/login.ejs b/view/login.ejs new file mode 100644 index 0000000..8f2052b --- /dev/null +++ b/view/login.ejs @@ -0,0 +1,21 @@ + + + + + + + + + + + Authenticator + + + + + + + diff --git a/view/register.ejs b/view/register.ejs new file mode 100644 index 0000000..38c7ca6 --- /dev/null +++ b/view/register.ejs @@ -0,0 +1,21 @@ + + + + + + + + + + + Authenticator + + + + + + +