From c77c8ee9373cca546f3721df860101afe9d9fc9e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:13:56 +0200 Subject: [PATCH] take the TS source code off of @openid/appauth and compile it manually so that we can make it into a proper esmodule --- src/Umbraco.Web.UI.Client/package-lock.json | 596 +++++++++--------- src/Umbraco.Web.UI.Client/package.json | 15 +- .../src/external/base64-js/index.ts | 1 + .../src/external/openid/LICENSE | 176 ++++++ .../external/openid/authorization_request.ts | 122 ++++ .../openid/authorization_request_handler.ts | 161 +++++ .../external/openid/authorization_response.ts | 79 +++ .../authorization_service_configuration.ts | 81 +++ .../src/external/openid/crypto_utils.ts | 98 +++ .../src/external/openid/errors.ts | 24 + .../src/external/openid/flags.ts | 21 + .../src/external/openid/index.ts | 34 +- .../src/external/openid/logger.ts | 71 +++ .../src/external/openid/query_string_utils.ts | 64 ++ .../external/openid/redirect_based_handler.ts | 147 +++++ .../external/openid/revoke_token_request.ts | 73 +++ .../src/external/openid/storage.ts | 101 +++ .../src/external/openid/token_request.ts | 96 +++ .../external/openid/token_request_handler.ts | 88 +++ .../src/external/openid/token_response.ts | 137 ++++ .../src/external/openid/types.ts | 32 + .../src/external/openid/xhr.ts | 115 ++++ src/Umbraco.Web.UI.Client/tsconfig.json | 1 + 23 files changed, 2006 insertions(+), 327 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/external/base64-js/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/LICENSE create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/authorization_request.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/authorization_request_handler.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/authorization_response.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/authorization_service_configuration.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/crypto_utils.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/errors.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/flags.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/logger.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/query_string_utils.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/redirect_based_handler.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/revoke_token_request.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/storage.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/token_request.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/token_request_handler.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/token_response.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/openid/xhr.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 865262002a..db9ee79f23 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -9,11 +9,11 @@ "version": "14.0.0-beta003", "license": "MIT", "dependencies": { - "@openid/appauth": "^1.3.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^9.0.8", "@umbraco-ui/uui": "1.7.2", "@umbraco-ui/uui-css": "1.7.2", + "base64-js": "^1.5.1", "dompurify": "^3.0.9", "element-internals-polyfill": "^1.3.10", "lit": "^3.1.2", @@ -44,10 +44,10 @@ "@types/mocha": "^10.0.1", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^7.1.0", - "@web/dev-server-esbuild": "^1.0.1", + "@web/dev-server-esbuild": "^1.0.2", "@web/dev-server-import-maps": "^0.2.0", "@web/dev-server-rollup": "^0.6.1", - "@web/test-runner": "^0.18.0", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.11.0", "babel-loader": "^9.1.3", "eslint": "^8.56.0", @@ -68,9 +68,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "remark-gfm": "^3.0.1", - "rollup": "^4.12.0", - "rollup-plugin-esbuild": "^6.1.0", - "rollup-plugin-import-css": "^3.4.0", + "rollup": "^4.14.1", + "rollup-plugin-esbuild": "^6.1.1", + "rollup-plugin-import-css": "^3.5.0", "rollup-plugin-web-worker-loader": "^1.6.1", "simple-icons": "^11.11.0", "storybook": "^7.6.17", @@ -3343,19 +3343,6 @@ "lit-html": "^2.0.0 || ^3.0.0" } }, - "node_modules/@openid/appauth": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@openid/appauth/-/appauth-1.3.1.tgz", - "integrity": "sha512-e54kpi219wES2ijPzeHe1kMnT8VKH8YeTd1GAn9BzVBmutz3tBgcG1y8a4pziNr4vNjFnuD4W446Ua7ELnNDiA==", - "dependencies": { - "@types/base64-js": "^1.3.0", - "@types/jquery": "^3.5.5", - "base64-js": "^1.5.1", - "follow-redirects": "^1.13.3", - "form-data": "^4.0.0", - "opener": "^1.5.2" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3382,52 +3369,60 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.1.tgz", + "integrity": "sha512-QSXujx4d4ogDamQA8ckkkRieFzDgZEuZuGiey9G7CuDcbnX4iINKWxTPC5Br2AEzY9ICAvcndqgAUFMMKnS/Tw==", "dev": true, "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", + "proxy-agent": "6.4.0", + "semver": "7.6.0", + "tar-fs": "3.0.5", "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "yargs": "17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "node_modules/@puppeteer/browsers/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" } }, + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@radix-ui/number": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", @@ -4236,9 +4231,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", "cpu": [ "arm" ], @@ -4249,9 +4244,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", "cpu": [ "arm64" ], @@ -4262,9 +4257,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", "cpu": [ "arm64" ], @@ -4275,9 +4270,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", "cpu": [ "x64" ], @@ -4288,9 +4283,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", "cpu": [ "arm" ], @@ -4301,9 +4296,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", "cpu": [ "arm64" ], @@ -4314,9 +4309,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", "cpu": [ "arm64" ], @@ -4326,10 +4321,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", "cpu": [ "riscv64" ], @@ -4339,10 +4347,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", "cpu": [ "x64" ], @@ -4353,9 +4374,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", "cpu": [ "x64" ], @@ -4366,9 +4387,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", "cpu": [ "arm64" ], @@ -4379,9 +4400,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", "cpu": [ "ia32" ], @@ -4392,9 +4413,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", "cpu": [ "x64" ], @@ -5905,11 +5926,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/base64-js": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/base64-js/-/base64-js-1.3.2.tgz", - "integrity": "sha512-Q2Xn2/vQHRGLRXhQ5+BSLwhHkR3JVflxVKywH0Q6fVoAiUE8fFYL2pE5/l2ZiOiBDfA8qUqRnSxln4G/NFz1Sg==" - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -6127,14 +6143,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jquery": { - "version": "3.5.29", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.29.tgz", - "integrity": "sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==", - "dependencies": { - "@types/sizzle": "*" - } - }, "node_modules/@types/js-levenshtein": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.3.tgz", @@ -6368,11 +6376,6 @@ "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", "dev": true }, - "node_modules/@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==" - }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -8213,15 +8216,15 @@ } }, "node_modules/@web/test-runner": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.18.0.tgz", - "integrity": "sha512-aAlQrdSqwCie1mxuSK5kM0RYDJZL4Q0Hd5LeXn1on3OtHLtgztL4dZzzNSuAWablR2/Vuve3ChwDDxmYSTqXRg==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.18.1.tgz", + "integrity": "sha512-jB/9vrpGVtcLY6/7sPpKpSheQ3wWY9P5aQcz2SK2gMHTq3gNpa51NAyec0Al7EFpHvJ1wKYTGRLB2gPyEoJeDg==", "dev": true, "dependencies": { "@web/browser-logs": "^0.4.0", "@web/config-loader": "^0.3.0", "@web/dev-server": "^0.4.0", - "@web/test-runner-chrome": "^0.15.0", + "@web/test-runner-chrome": "^0.16.0", "@web/test-runner-commands": "^0.9.0", "@web/test-runner-core": "^0.13.0", "@web/test-runner-mocha": "^0.9.0", @@ -8244,16 +8247,16 @@ } }, "node_modules/@web/test-runner-chrome": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@web/test-runner-chrome/-/test-runner-chrome-0.15.0.tgz", - "integrity": "sha512-ZqkTJGQ57FDz3lWw+9CKfHuTV64S9GzBy5+0siSQulEVPfGiTzpksx9DohtA3BCLXdbEq4OHg40/XIQJomlc9w==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@web/test-runner-chrome/-/test-runner-chrome-0.16.0.tgz", + "integrity": "sha512-Edc6Y49aVB6k18S5IOj9OCX3rEf8F3jptIu0p95+imqxmcutFEh1GNmlAk2bQGnXS0U6uVY7Xbf61fiaXUQqhg==", "dev": true, "dependencies": { "@web/test-runner-core": "^0.13.0", "@web/test-runner-coverage-v8": "^0.8.0", "async-mutex": "0.4.0", "chrome-launcher": "^0.15.0", - "puppeteer-core": "^20.0.0" + "puppeteer-core": "^22.0.0" }, "engines": { "node": ">=18.0.0" @@ -8488,9 +8491,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -8916,7 +8919,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/available-typed-arrays": { "version": "1.0.6", @@ -8946,9 +8950,9 @@ "dev": true }, "node_modules/b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "dev": true }, "node_modules/babel-core": { @@ -9161,6 +9165,42 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", + "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "dev": true, + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.3.tgz", + "integrity": "sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==", + "dev": true, + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "streamx": "^2.13.0" + } + }, + "node_modules/bare-os": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.2.1.tgz", + "integrity": "sha512-OwPyHgBBMkhC29Hl3O4/YfxW9n7mdTr2+SsO29XBWKKJsbgj3mnorDB80r5TiCQgQstgE5ga1qNYrpes6NvX2w==", + "dev": true, + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.1.tgz", + "integrity": "sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==", + "dev": true, + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -9181,9 +9221,9 @@ ] }, "node_modules/basic-ftp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", - "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -9704,12 +9744,14 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.16.tgz", + "integrity": "sha512-IT5lnR44h/qZQ4GaCHvBxYIl4cQL2i9UvFyYeRyVdcpY04hx5H720HQfe/7Oz7ndxaYVLQFGpCO71J4X2Ye/Gw==", "dev": true, "dependencies": { - "mitt": "3.0.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.22.4" }, "peerDependencies": { "devtools-protocol": "*" @@ -9914,6 +9956,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -10195,15 +10238,6 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10234,9 +10268,9 @@ "dev": true }, "node_modules/data-uri-to-buffer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", - "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, "engines": { "node": ">= 14" @@ -10423,6 +10457,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -10512,9 +10547,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "version": "0.0.1262051", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1262051.tgz", + "integrity": "sha512-YJe4CT5SA8on3Spa+UDtNhEqtuV6Epwz3OZ4HQVLhlRccpZ9/PAYk0/cy/oKxFKRrZPBUPyxympQci4yWNWZ9g==", "dev": true }, "node_modules/diff": { @@ -12236,25 +12271,6 @@ "node": ">=0.4.0" } }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -12296,6 +12312,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -12546,52 +12563,20 @@ } }, "node_modules/get-uri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", - "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "dev": true, "dependencies": { "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.0", + "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4", - "fs-extra": "^8.1.0" + "fs-extra": "^11.2.0" }, "engines": { "node": ">= 14" } }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/get-uri/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/get-uri/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/giget": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.1.tgz", @@ -12956,9 +12941,9 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { "agent-base": "^7.1.0", @@ -12969,9 +12954,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -13206,6 +13191,25 @@ "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", "dev": true }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -14154,6 +14158,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "node_modules/jscodeshift": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.2.tgz", @@ -15775,6 +15785,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { "node": ">= 0.6" } @@ -15783,6 +15794,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -15864,9 +15876,9 @@ "dev": true }, "node_modules/mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, "node_modules/mkdirp": { @@ -16570,14 +16582,6 @@ "openapi": "bin/index.js" } }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "bin": { - "opener": "bin/opener-bin.js" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -16759,25 +16763,18 @@ } }, "node_modules/pac-resolver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "dependencies": { "degenerator": "^5.0.0", - "ip": "^1.1.8", "netmask": "^2.0.2" }, "engines": { "node": ">= 14" } }, - "node_modules/pac-resolver/node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", - "dev": true - }, "node_modules/pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -17244,19 +17241,19 @@ } }, "node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dev": true, "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", + "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.2" }, "engines": { "node": ">= 14" @@ -17318,49 +17315,19 @@ } }, "node_modules/puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "version": "22.6.3", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.6.3.tgz", + "integrity": "sha512-YrTAak5zCTWVTnVaCK1b7FD1qFCCT9bSvQhLzamnIsJ57/tfuXiT8ZvPJR2SBfahyFTYFCcaZAd/Npow3lmDGA==", "dev": true, "dependencies": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", - "cross-fetch": "4.0.0", + "@puppeteer/browsers": "2.2.1", + "chromium-bidi": "0.5.16", "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" + "devtools-protocol": "0.0.1262051", + "ws": "8.16.0" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=18" } }, "node_modules/qs": { @@ -18073,9 +18040,9 @@ } }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -18088,19 +18055,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", "fsevents": "~2.3.2" } }, @@ -18130,9 +18099,9 @@ "dev": true }, "node_modules/rollup-plugin-import-css": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.4.0.tgz", - "integrity": "sha512-997dJi7M7yYFn7tZer/UVt72mh4GH/hHBv48j3V4jsGSg+1DdYUXn+QB9SMMCNKF99pSv6QXmIOLTyeuijIsgg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.5.0.tgz", + "integrity": "sha512-JOVow6n00qt2C/NnsqPmIjFOfxIAudwWqC5SaC84CodMGiMFaP1gPAdgnJ8g8hcG+P85TCYp2kI98grYCEt5pg==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.4" @@ -18559,26 +18528,26 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", + "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", "dev": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", "socks": "^2.7.1" }, @@ -18719,13 +18688,16 @@ "dev": true }, "node_modules/streamx": { - "version": "2.15.7", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.7.tgz", - "integrity": "sha512-NPEKS5+yjyo597eafGbKW5ujh7Sm6lDLHZQd/lRSz6S0VarpADBJItqfB4PnwpS+472oob1GX5cCY9vzfJpHUA==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", + "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", "dev": true, "dependencies": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" } }, "node_modules/strict-event-emitter": { @@ -19000,14 +18972,17 @@ } }, "node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", + "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", "dev": true, "dependencies": { - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/tar-stream": { @@ -20080,6 +20055,12 @@ "punycode": "^2.1.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true + }, "node_modules/use-callback-ref": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz", @@ -21133,6 +21114,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 929d6ea64c..f6339dca28 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -88,6 +88,7 @@ "./webhook": "./dist-cms/packages/webhook/index.js", "./workspace": "./dist-cms/packages/core/workspace/index.js", "./external/backend-api": "./dist-cms/external/backend-api/index.js", + "./external/base64-js": "./dist-cms/external/base64-js/index.js", "./external/dompurify": "./dist-cms/external/dompurify/index.js", "./external/lit": "./dist-cms/external/lit/index.js", "./external/marked": "./dist-cms/external/marked/index.js", @@ -162,11 +163,11 @@ "npm": ">=10.1 < 11" }, "dependencies": { - "@openid/appauth": "^1.3.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^9.0.8", "@umbraco-ui/uui": "1.7.2", "@umbraco-ui/uui-css": "1.7.2", + "base64-js": "^1.5.1", "dompurify": "^3.0.9", "element-internals-polyfill": "^1.3.10", "lit": "^3.1.2", @@ -197,10 +198,10 @@ "@types/mocha": "^10.0.1", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^7.1.0", - "@web/dev-server-esbuild": "^1.0.1", + "@web/dev-server-esbuild": "^1.0.2", "@web/dev-server-import-maps": "^0.2.0", "@web/dev-server-rollup": "^0.6.1", - "@web/test-runner": "^0.18.0", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.11.0", "babel-loader": "^9.1.3", "eslint": "^8.56.0", @@ -221,12 +222,12 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "remark-gfm": "^3.0.1", - "rollup": "^4.12.0", - "rollup-plugin-esbuild": "^6.1.0", - "rollup-plugin-import-css": "^3.4.0", + "rollup": "^4.14.1", + "rollup-plugin-esbuild": "^6.1.1", + "rollup-plugin-import-css": "^3.5.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "storybook": "^7.6.17", "simple-icons": "^11.11.0", + "storybook": "^7.6.17", "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.8", "typedoc": "^0.25.10", diff --git a/src/Umbraco.Web.UI.Client/src/external/base64-js/index.ts b/src/Umbraco.Web.UI.Client/src/external/base64-js/index.ts new file mode 100644 index 0000000000..d8a192ebba --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/base64-js/index.ts @@ -0,0 +1 @@ +export { fromByteArray } from 'base64-js'; diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/LICENSE b/src/Umbraco.Web.UI.Client/src/external/openid/LICENSE new file mode 100644 index 0000000000..d9a10c0d8e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request.ts b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request.ts new file mode 100644 index 0000000000..668082c472 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request.ts @@ -0,0 +1,122 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DefaultCrypto } from './crypto_utils.js'; +import type { Crypto } from './crypto_utils.js'; +import { log } from './logger.js'; +import type { StringMap } from './types.js'; + +/** + * Represents an AuthorizationRequest as JSON. + */ +export interface AuthorizationRequestJson { + response_type: string; + client_id: string; + redirect_uri: string; + scope: string; + state?: string; + extras?: StringMap; + internal?: StringMap; +} + +/** + * Generates a cryptographically random new state. Useful for CSRF protection. + */ +const SIZE = 10; // 10 bytes +const newState = function (crypto: Crypto): string { + return crypto.generateRandom(SIZE); +}; + +/** + * Represents the AuthorizationRequest. + * For more information look at + * https://tools.ietf.org/html/rfc6749#section-4.1.1 + */ +export class AuthorizationRequest { + static RESPONSE_TYPE_TOKEN = 'token'; + static RESPONSE_TYPE_CODE = 'code'; + + // NOTE: + // Both redirect_uri and state are actually optional. + // However AppAuth is more opionionated, and requires you to use both. + + clientId: string; + redirectUri: string; + scope: string; + responseType: string; + state: string; + extras?: StringMap; + internal?: StringMap; + /** + * Constructs a new AuthorizationRequest. + * Use a `undefined` value for the `state` parameter, to generate a random + * state for CSRF protection. + */ + constructor( + request: AuthorizationRequestJson, + private crypto: Crypto = new DefaultCrypto(), + private usePkce: boolean = true, + ) { + this.clientId = request.client_id; + this.redirectUri = request.redirect_uri; + this.scope = request.scope; + this.responseType = request.response_type || AuthorizationRequest.RESPONSE_TYPE_CODE; + this.state = request.state || newState(crypto); + this.extras = request.extras; + // read internal properties if available + this.internal = request.internal; + } + + setupCodeVerifier(): Promise { + if (!this.usePkce) { + return Promise.resolve(); + } else { + const codeVerifier = this.crypto.generateRandom(128); + const challenge: Promise = this.crypto.deriveChallenge(codeVerifier).catch((error) => { + log('Unable to generate PKCE challenge. Not using PKCE', error); + return undefined; + }); + return challenge.then((result) => { + if (result) { + // keep track of the code used. + this.internal = this.internal || {}; + this.internal['code_verifier'] = codeVerifier; + this.extras = this.extras || {}; + this.extras['code_challenge'] = result; + // We always use S256. Plain is not good enough. + this.extras['code_challenge_method'] = 'S256'; + } + }); + } + } + + /** + * Serializes the AuthorizationRequest to a JavaScript Object. + */ + toJson(): Promise { + // Always make sure that the code verifier is setup when toJson() is called. + return this.setupCodeVerifier().then(() => { + return { + response_type: this.responseType, + client_id: this.clientId, + redirect_uri: this.redirectUri, + scope: this.scope, + state: this.state, + extras: this.extras, + internal: this.internal, + }; + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request_handler.ts b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request_handler.ts new file mode 100644 index 0000000000..90eb72b171 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_request_handler.ts @@ -0,0 +1,161 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { AuthorizationRequest } from './authorization_request.js'; +import type { AuthorizationError, AuthorizationResponse } from './authorization_response.js'; +import type { AuthorizationServiceConfiguration } from './authorization_service_configuration.js'; +import type { Crypto } from './crypto_utils.js'; +import { log } from './logger.js'; +import type { QueryStringUtils } from './query_string_utils.js'; +import type { StringMap } from './types.js'; + +/** + * This type represents a lambda that can take an AuthorizationRequest, + * and an AuthorizationResponse as arguments. + */ +export type AuthorizationListener = ( + request: AuthorizationRequest, + response: AuthorizationResponse | null, + error: AuthorizationError | null, +) => void; + +/** + * Represents a structural type holding both authorization request and response. + */ +export interface AuthorizationRequestResponse { + request: AuthorizationRequest; + response: AuthorizationResponse | null; + error: AuthorizationError | null; +} + +/** + * Authorization Service notifier. + * This manages the communication of the AuthorizationResponse to the 3p client. + */ +export class AuthorizationNotifier { + private listener: AuthorizationListener | null = null; + + setAuthorizationListener(listener: AuthorizationListener) { + this.listener = listener; + } + + /** + * The authorization complete callback. + */ + onAuthorizationComplete( + request: AuthorizationRequest, + response: AuthorizationResponse | null, + error: AuthorizationError | null, + ): void { + if (this.listener) { + // complete authorization request + this.listener(request, response, error); + } + } +} + +// TODO(rahulrav@): add more built in parameters. +/* built in parameters. */ +export const BUILT_IN_PARAMETERS = ['redirect_uri', 'client_id', 'response_type', 'state', 'scope']; + +/** + * Defines the interface which is capable of handling an authorization request + * using various methods (iframe / popup / different process etc.). + */ +export abstract class AuthorizationRequestHandler { + constructor( + public utils: QueryStringUtils, + protected crypto: Crypto, + ) {} + + // notifier send the response back to the client. + protected notifier: AuthorizationNotifier | null = null; + + /** + * A utility method to be able to build the authorization request URL. + */ + protected buildRequestUrl(configuration: AuthorizationServiceConfiguration, request: AuthorizationRequest) { + // build the query string + // coerce to any type for convenience + const requestMap: StringMap = { + redirect_uri: request.redirectUri, + client_id: request.clientId, + response_type: request.responseType, + state: request.state, + scope: request.scope, + }; + + // copy over extras + if (request.extras) { + for (const extra in request.extras) { + if (Object.prototype.hasOwnProperty.call(request.extras, extra)) { + // check before inserting to requestMap + if (BUILT_IN_PARAMETERS.indexOf(extra) < 0) { + requestMap[extra] = request.extras[extra]; + } + } + } + } + + const query = this.utils.stringify(requestMap); + const baseUrl = configuration.authorizationEndpoint; + const url = `${baseUrl}?${query}`; + return url; + } + + /** + * Completes the authorization request if necessary & when possible. + */ + completeAuthorizationRequestIfPossible(): Promise { + // call complete authorization if possible to see there might + // be a response that needs to be delivered. + log(`Checking to see if there is an authorization response to be delivered.`); + if (!this.notifier) { + log(`Notifier is not present on AuthorizationRequest handler. + No delivery of result will be possible`); + } + return this.completeAuthorizationRequest().then((result) => { + if (!result) { + log(`No result is available yet.`); + } + if (result && this.notifier) { + this.notifier.onAuthorizationComplete(result.request, result.response, result.error); + } + }); + } + + /** + * Sets the default Authorization Service notifier. + */ + setAuthorizationNotifier(notifier: AuthorizationNotifier): AuthorizationRequestHandler { + this.notifier = notifier; + return this; + } + + /** + * Makes an authorization request. + */ + abstract performAuthorizationRequest( + configuration: AuthorizationServiceConfiguration, + request: AuthorizationRequest, + ): void; + + /** + * Checks if an authorization flow can be completed, and completes it. + * The handler returns a `Promise` if ready, or a `Promise` + * if not ready. + */ + protected abstract completeAuthorizationRequest(): Promise; +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/authorization_response.ts b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_response.ts new file mode 100644 index 0000000000..ac074fca30 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_response.ts @@ -0,0 +1,79 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents the AuthorizationResponse as a JSON object. + */ +export interface AuthorizationResponseJson { + code: string; + state: string; +} + +/** + * Represents the AuthorizationError as a JSON object. + */ +export interface AuthorizationErrorJson { + error: string; + error_description?: string; + error_uri?: string; + state?: string; +} + +/** + * Represents the Authorization Response type. + * For more information look at + * https://tools.ietf.org/html/rfc6749#section-4.1.2 + */ +export class AuthorizationResponse { + code: string; + state: string; + + constructor(response: AuthorizationResponseJson) { + this.code = response.code; + this.state = response.state; + } + + toJson(): AuthorizationResponseJson { + return { code: this.code, state: this.state }; + } +} + +/** + * Represents the Authorization error response. + * For more information look at: + * https://tools.ietf.org/html/rfc6749#section-4.1.2.1 + */ +export class AuthorizationError { + error: string; + errorDescription?: string; + errorUri?: string; + state?: string; + + constructor(error: AuthorizationErrorJson) { + this.error = error.error; + this.errorDescription = error.error_description; + this.errorUri = error.error_uri; + this.state = error.state; + } + + toJson(): AuthorizationErrorJson { + return { + error: this.error, + error_description: this.errorDescription, + error_uri: this.errorUri, + state: this.state, + }; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/authorization_service_configuration.ts b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_service_configuration.ts new file mode 100644 index 0000000000..b532d7f2a4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/authorization_service_configuration.ts @@ -0,0 +1,81 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Requestor } from './xhr.js'; +import { JQueryRequestor } from './xhr.js'; + +/** + * Represents AuthorizationServiceConfiguration as a JSON object. + */ +export interface AuthorizationServiceConfigurationJson { + authorization_endpoint: string; + token_endpoint: string; + revocation_endpoint: string; + end_session_endpoint?: string; + userinfo_endpoint?: string; +} + +/** + * The standard base path for well-known resources on domains. + * See https://tools.ietf.org/html/rfc5785 for more information. + */ +const WELL_KNOWN_PATH = '.well-known'; + +/** + * The standard resource under the well known path at which an OpenID Connect + * discovery document can be found under an issuer's base URI. + */ +const OPENID_CONFIGURATION = 'openid-configuration'; + +/** + * Configuration details required to interact with an authorization service. + * + * More information at https://openid.net/specs/openid-connect-discovery-1_0-17.html + */ +export class AuthorizationServiceConfiguration { + authorizationEndpoint: string; + tokenEndpoint: string; + revocationEndpoint: string; + userInfoEndpoint?: string; + endSessionEndpoint?: string; + + constructor(request: AuthorizationServiceConfigurationJson) { + this.authorizationEndpoint = request.authorization_endpoint; + this.tokenEndpoint = request.token_endpoint; + this.revocationEndpoint = request.revocation_endpoint; + this.userInfoEndpoint = request.userinfo_endpoint; + this.endSessionEndpoint = request.end_session_endpoint; + } + + toJson() { + return { + authorization_endpoint: this.authorizationEndpoint, + token_endpoint: this.tokenEndpoint, + revocation_endpoint: this.revocationEndpoint, + end_session_endpoint: this.endSessionEndpoint, + userinfo_endpoint: this.userInfoEndpoint, + }; + } + + static fetchFromIssuer(openIdIssuerUrl: string, requestor?: Requestor): Promise { + const fullUrl = `${openIdIssuerUrl}/${WELL_KNOWN_PATH}/${OPENID_CONFIGURATION}`; + + const requestorToUse = requestor || new JQueryRequestor(); + + return requestorToUse + .xhr({ url: fullUrl, dataType: 'json', method: 'GET' }) + .then((json) => new AuthorizationServiceConfiguration(json)); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/crypto_utils.ts b/src/Umbraco.Web.UI.Client/src/external/openid/crypto_utils.ts new file mode 100644 index 0000000000..9ee6c84ac1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/crypto_utils.ts @@ -0,0 +1,98 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as base64 from '@umbraco-cms/backoffice/external/base64-js'; + +import { AppAuthError } from './errors.js'; + +const HAS_CRYPTO = typeof window !== 'undefined' && !!(window.crypto as any); +const HAS_SUBTLE_CRYPTO = HAS_CRYPTO && !!(window.crypto.subtle as any); +const CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + +export function bufferToString(buffer: Uint8Array) { + const state = []; + for (let i = 0; i < buffer.byteLength; i += 1) { + const index = buffer[i] % CHARSET.length; + state.push(CHARSET[index]); + } + return state.join(''); +} + +export function urlSafe(buffer: Uint8Array): string { + const encoded = base64.fromByteArray(new Uint8Array(buffer)); + return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); +} + +// adapted from source: http://stackoverflow.com/a/11058858 +// this is used in place of TextEncode as the api is not yet +// well supported: https://caniuse.com/#search=TextEncoder +export function textEncodeLite(str: string) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + + for (let i = 0; i < str.length; i++) { + bufView[i] = str.charCodeAt(i); + } + return bufView; +} + +export interface Crypto { + /** + * Generate a random string + */ + generateRandom(size: number): string; + /** + * Compute the SHA256 of a given code. + * This is useful when using PKCE. + */ + deriveChallenge(code: string): Promise; +} + +/** + * The default implementation of the `Crypto` interface. + * This uses the capabilities of the browser. + */ +export class DefaultCrypto implements Crypto { + generateRandom(size: number) { + const buffer = new Uint8Array(size); + if (HAS_CRYPTO) { + window.crypto.getRandomValues(buffer); + } else { + // fall back to Math.random() if nothing else is available + for (let i = 0; i < size; i += 1) { + buffer[i] = (Math.random() * CHARSET.length) | 0; + } + } + return bufferToString(buffer); + } + + deriveChallenge(code: string): Promise { + if (code.length < 43 || code.length > 128) { + return Promise.reject(new AppAuthError('Invalid code length.')); + } + if (!HAS_SUBTLE_CRYPTO) { + return Promise.reject(new AppAuthError('window.crypto.subtle is unavailable.')); + } + + return new Promise((resolve, reject) => { + crypto.subtle.digest('SHA-256', textEncodeLite(code)).then( + (buffer) => { + return resolve(urlSafe(new Uint8Array(buffer))); + }, + (error) => reject(error), + ); + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/errors.ts b/src/Umbraco.Web.UI.Client/src/external/openid/errors.ts new file mode 100644 index 0000000000..a37305b800 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/errors.ts @@ -0,0 +1,24 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents the AppAuthError type. + */ +export class AppAuthError { + constructor( + public message: string, + public extras?: any, + ) {} +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/flags.ts b/src/Umbraco.Web.UI.Client/src/external/openid/flags.ts new file mode 100644 index 0000000000..8a278c6c06 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/flags.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Global flags that control the behavior of App Auth JS. */ + +/* Logging turned on ? */ +export const IS_LOG = true; + +/* Profiling turned on ? */ +export const IS_PROFILE = false; diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/index.ts b/src/Umbraco.Web.UI.Client/src/external/openid/index.ts index 5d92bc1f8c..108f5e5134 100644 --- a/src/Umbraco.Web.UI.Client/src/external/openid/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/openid/index.ts @@ -1,17 +1,17 @@ -export { - AuthorizationNotifier, - AuthorizationRequest, - AuthorizationServiceConfiguration, - BaseTokenRequestHandler, - FetchRequestor, - BasicQueryStringUtils, - LocalStorageBackend, - RedirectRequestHandler, - TokenRequest, - TokenResponse, - RevokeTokenRequest, - GRANT_TYPE_AUTHORIZATION_CODE, - GRANT_TYPE_REFRESH_TOKEN, -} from '@openid/appauth'; - -export type { LocationLike, StringMap } from '@openid/appauth'; +export * from './authorization_request.js'; +export * from './authorization_request_handler.js'; +export * from './authorization_response.js'; +export * from './authorization_service_configuration.js'; +export * from './crypto_utils.js'; +export * from './errors.js'; +export * from './flags.js'; +export * from './logger.js'; +export * from './query_string_utils.js'; +export * from './redirect_based_handler.js'; +export * from './revoke_token_request.js'; +export * from './storage.js'; +export * from './token_request.js'; +export * from './token_request_handler.js'; +export * from './token_response.js'; +export type * from './types.js'; +export * from './xhr.js'; diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/logger.ts b/src/Umbraco.Web.UI.Client/src/external/openid/logger.ts new file mode 100644 index 0000000000..9f31c83d79 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/logger.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IS_LOG, IS_PROFILE } from './flags.js'; + +export function log(message: string, ...args: any[]) { + if (IS_LOG) { + const length = args ? args.length : 0; + if (length > 0) { + console.log(message, ...args); + } else { + console.log(message); + } + } +} + +// check to see if native support for profiling is available. +const NATIVE_PROFILE_SUPPORT = typeof window !== 'undefined' && !!window.performance && !!console.profile; + +/** + * A decorator that can profile a function. + */ +export function profile(target: any, propertyKey: string, descriptor: PropertyDescriptor) { + if (IS_PROFILE) { + return performProfile(target, propertyKey, descriptor); + } else { + // return as-is + return descriptor; + } +} + +function performProfile(target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor { + const originalCallable = descriptor.value; + // name must exist + let name = originalCallable.name; + if (!name) { + name = 'anonymous function'; + } + if (NATIVE_PROFILE_SUPPORT) { + descriptor.value = function (args: any[]) { + console.profile(name); + const startTime = window.performance.now(); + const result = originalCallable.call(this || window, ...args); + const duration = window.performance.now() - startTime; + console.log(`${name} took ${duration} ms`); + console.profileEnd(); + return result; + }; + } else { + descriptor.value = function (args: any[]) { + log(`Profile start ${name}`); + const start = Date.now(); + const result = originalCallable.call(this || window, ...args); + const duration = Date.now() - start; + log(`Profile end ${name} took ${duration} ms.`); + return result; + }; + } + return descriptor; +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/query_string_utils.ts b/src/Umbraco.Web.UI.Client/src/external/openid/query_string_utils.ts new file mode 100644 index 0000000000..eb88ce5c13 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/query_string_utils.ts @@ -0,0 +1,64 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { LocationLike, StringMap } from './types.js'; + +/** + * Query String Utilities. + */ +export interface QueryStringUtils { + stringify(input: StringMap): string; + parse(query: LocationLike, useHash?: boolean): StringMap; + parseQueryString(query: string): StringMap; +} + +export class BasicQueryStringUtils implements QueryStringUtils { + parse(input: LocationLike, useHash?: boolean) { + if (useHash) { + return this.parseQueryString(input.hash); + } else { + return this.parseQueryString(input.search); + } + } + + parseQueryString(query: string): StringMap { + const result: StringMap = {}; + // if anything starts with ?, # or & remove it + query = query.trim().replace(/^(\?|#|&)/, ''); + const params = query.split('&'); + for (let i = 0; i < params.length; i += 1) { + const param = params[i]; // looks something like a=b + const parts = param.split('='); + if (parts.length >= 2) { + const key = decodeURIComponent(parts.shift()!); + const value = parts.length > 0 ? parts.join('=') : null; + if (value) { + result[key] = decodeURIComponent(value); + } + } + } + return result; + } + + stringify(input: StringMap) { + const encoded: string[] = []; + for (const key in input) { + if (Object.prototype.hasOwnProperty.call(input, key) && input[key]) { + encoded.push(`${encodeURIComponent(key)}=${encodeURIComponent(input[key])}`); + } + } + return encoded.join('&'); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/redirect_based_handler.ts b/src/Umbraco.Web.UI.Client/src/external/openid/redirect_based_handler.ts new file mode 100644 index 0000000000..7a5f7eafc9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/redirect_based_handler.ts @@ -0,0 +1,147 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {AuthorizationRequest} from './authorization_request'; +import {AuthorizationRequestHandler, AuthorizationRequestResponse} from './authorization_request_handler'; +import {AuthorizationError, AuthorizationResponse} from './authorization_response' +import {AuthorizationServiceConfiguration} from './authorization_service_configuration'; +import {Crypto, DefaultCrypto} from './crypto_utils'; +import {log} from './logger'; +import {BasicQueryStringUtils} from './query_string_utils'; +import {LocalStorageBackend, StorageBackend} from './storage'; +import {LocationLike} from './types'; + + +/** key for authorization request. */ +const authorizationRequestKey = + (handle: string) => { + return `${handle}_appauth_authorization_request`; + } + +/** key for authorization service configuration */ +const authorizationServiceConfigurationKey = + (handle: string) => { + return `${handle}_appauth_authorization_service_configuration`; + } + +/** key in local storage which represents the current authorization request. */ +const AUTHORIZATION_REQUEST_HANDLE_KEY = 'appauth_current_authorization_request'; + +/** + * Represents an AuthorizationRequestHandler which uses a standard + * redirect based code flow. + */ +export class RedirectRequestHandler extends AuthorizationRequestHandler { + constructor( + // use the provided storage backend + // or initialize local storage with the default storage backend which + // uses window.localStorage + public storageBackend: StorageBackend = new LocalStorageBackend(), + utils = new BasicQueryStringUtils(), + public locationLike: LocationLike = window.location, + crypto: Crypto = new DefaultCrypto()) { + super(utils, crypto); + } + + performAuthorizationRequest( + configuration: AuthorizationServiceConfiguration, + request: AuthorizationRequest) { + const handle = this.crypto.generateRandom(10); + + // before you make request, persist all request related data in local storage. + const persisted = Promise.all([ + this.storageBackend.setItem(AUTHORIZATION_REQUEST_HANDLE_KEY, handle), + // Calling toJson() adds in the code & challenge when possible + request.toJson().then( + result => + this.storageBackend.setItem(authorizationRequestKey(handle), JSON.stringify(result))), + this.storageBackend.setItem( + authorizationServiceConfigurationKey(handle), JSON.stringify(configuration.toJson())), + ]); + + persisted.then(() => { + // make the redirect request + let url = this.buildRequestUrl(configuration, request); + log('Making a request to ', request, url); + this.locationLike.assign(url); + }); + } + + /** + * Attempts to introspect the contents of storage backend and completes the + * request. + */ + protected completeAuthorizationRequest(): Promise { + // TODO(rahulrav@): handle authorization errors. + return this.storageBackend.getItem(AUTHORIZATION_REQUEST_HANDLE_KEY).then(handle => { + if (handle) { + // we have a pending request. + // fetch authorization request, and check state + return this.storageBackend + .getItem(authorizationRequestKey(handle)) + // requires a corresponding instance of result + // TODO(rahulrav@): check for inconsitent state here + .then(result => JSON.parse(result!)) + .then(json => new AuthorizationRequest(json)) + .then(request => { + // check redirect_uri and state + let currentUri = `${this.locationLike.origin}${this.locationLike.pathname}`; + let queryParams = this.utils.parse(this.locationLike, true /* use hash */); + let state: string|undefined = queryParams['state']; + let code: string|undefined = queryParams['code']; + let error: string|undefined = queryParams['error']; + log('Potential authorization request ', currentUri, queryParams, state, code, error); + let shouldNotify = state === request.state; + let authorizationResponse: AuthorizationResponse|null = null; + let authorizationError: AuthorizationError|null = null; + if (shouldNotify) { + if (error) { + // get additional optional info. + let errorUri = queryParams['error_uri']; + let errorDescription = queryParams['error_description']; + authorizationError = new AuthorizationError({ + error: error, + error_description: errorDescription, + error_uri: errorUri, + state: state + }); + } else { + authorizationResponse = new AuthorizationResponse({code: code, state: state}); + } + // cleanup state + return Promise + .all([ + this.storageBackend.removeItem(AUTHORIZATION_REQUEST_HANDLE_KEY), + this.storageBackend.removeItem(authorizationRequestKey(handle)), + this.storageBackend.removeItem(authorizationServiceConfigurationKey(handle)) + ]) + .then(() => { + log('Delivering authorization response'); + return { + request: request, + response: authorizationResponse, + error: authorizationError + } as AuthorizationRequestResponse; + }); + } else { + log('Mismatched request (state and request_uri) dont match.'); + return Promise.resolve(null); + } + }); + } else { + return null; + } + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/revoke_token_request.ts b/src/Umbraco.Web.UI.Client/src/external/openid/revoke_token_request.ts new file mode 100644 index 0000000000..97e21f1bec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/revoke_token_request.ts @@ -0,0 +1,73 @@ +/* eslint-disable local-rules/umb-class-prefix */ +import type { StringMap } from './types.js'; + +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Supported token types + */ +export type TokenTypeHint = 'refresh_token' | 'access_token'; + +/** + * Represents the Token Request as JSON. + */ +export interface RevokeTokenRequestJson { + token: string; + token_type_hint?: TokenTypeHint; + client_id?: string; + client_secret?: string; +} + +/** + * Represents a revoke token request. + * For more information look at: + * https://tools.ietf.org/html/rfc7009#section-2.1 + */ +export class RevokeTokenRequest { + token: string; + tokenTypeHint: TokenTypeHint | undefined; + clientId: string | undefined; + clientSecret: string | undefined; + + constructor(request: RevokeTokenRequestJson) { + this.token = request.token; + this.tokenTypeHint = request.token_type_hint; + this.clientId = request.client_id; + this.clientSecret = request.client_secret; + } + + /** + * Serializes a TokenRequest to a JavaScript object. + */ + toJson(): RevokeTokenRequestJson { + const json: RevokeTokenRequestJson = { token: this.token }; + if (this.tokenTypeHint) { + json['token_type_hint'] = this.tokenTypeHint; + } + if (this.clientId) { + json['client_id'] = this.clientId; + } + if (this.clientSecret) { + json['client_secret'] = this.clientSecret; + } + return json; + } + + toStringMap(): StringMap { + const json = this.toJson(); + // :( + return json as any; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/storage.ts b/src/Umbraco.Web.UI.Client/src/external/openid/storage.ts new file mode 100644 index 0000000000..a33c5dfcad --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/storage.ts @@ -0,0 +1,101 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A subset of the `Storage` interface which we need for the backends to work. + * + * Essentially removes the indexable properties and readonly properties from + * `Storage` in lib.dom.d.ts. This is so that a custom type can extend it for + * testing. + */ +export interface UnderlyingStorage { + readonly length: number; + clear(): void; + getItem(key: string): string | null; + removeItem(key: string): void; + setItem(key: string, data: string): void; +} + +/** + * Asynchronous storage APIs. All methods return a `Promise`. + * All methods take the `DOMString` + * IDL type (as it is the lowest common denominator). + */ +export abstract class StorageBackend { + /** + * When passed a key `name`, will return that key's value. + */ + public abstract getItem(name: string): Promise; + + /** + * When passed a key `name`, will remove that key from the storage. + */ + public abstract removeItem(name: string): Promise; + + /** + * When invoked, will empty all keys out of the storage. + */ + public abstract clear(): Promise; + + /** + * The setItem() method of the `StorageBackend` interface, + * when passed a key name and value, will add that key to the storage, + * or update that key's value if it already exists. + */ + public abstract setItem(name: string, value: string): Promise; +} + +/** + * A `StorageBackend` backed by `localstorage`. + */ +export class LocalStorageBackend extends StorageBackend { + private storage: UnderlyingStorage; + constructor(storage?: UnderlyingStorage) { + super(); + this.storage = storage || window.localStorage; + } + + public getItem(name: string): Promise { + return new Promise((resolve, reject) => { + const value = this.storage.getItem(name); + if (value) { + resolve(value); + } else { + resolve(null); + } + }); + } + + public removeItem(name: string): Promise { + return new Promise((resolve, reject) => { + this.storage.removeItem(name); + resolve(); + }); + } + + public clear(): Promise { + return new Promise((resolve, reject) => { + this.storage.clear(); + resolve(); + }); + } + + public setItem(name: string, value: string): Promise { + return new Promise((resolve, reject) => { + this.storage.setItem(name, value); + resolve(); + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/token_request.ts b/src/Umbraco.Web.UI.Client/src/external/openid/token_request.ts new file mode 100644 index 0000000000..9ee6a209ce --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/token_request.ts @@ -0,0 +1,96 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* eslint-disable local-rules/exported-string-constant-naming */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { StringMap } from './types.js'; + +export const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code'; +export const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token'; + +/** + * Represents the Token Request as JSON. + */ +export interface TokenRequestJson { + grant_type: string; + code?: string; + refresh_token?: string; + redirect_uri: string; + client_id: string; + extras?: StringMap; +} + +/** + * Represents an Access Token request. + * For more information look at: + * https://tools.ietf.org/html/rfc6749#section-4.1.3 + */ +export class TokenRequest { + clientId: string; + redirectUri: string; + grantType: string; + code: string | undefined; + refreshToken: string | undefined; + extras: StringMap | undefined; + + constructor(request: TokenRequestJson) { + this.clientId = request.client_id; + this.redirectUri = request.redirect_uri; + this.grantType = request.grant_type; + this.code = request.code; + this.refreshToken = request.refresh_token; + this.extras = request.extras; + } + + /** + * Serializes a TokenRequest to a JavaScript object. + */ + toJson(): TokenRequestJson { + return { + grant_type: this.grantType, + code: this.code, + refresh_token: this.refreshToken, + redirect_uri: this.redirectUri, + client_id: this.clientId, + extras: this.extras, + }; + } + + toStringMap(): StringMap { + const map: StringMap = { + grant_type: this.grantType, + client_id: this.clientId, + redirect_uri: this.redirectUri, + }; + + if (this.code) { + map['code'] = this.code; + } + + if (this.refreshToken) { + map['refresh_token'] = this.refreshToken; + } + + // copy over extras + if (this.extras) { + for (const extra in this.extras) { + if (Object.prototype.hasOwnProperty.call(this.extras, extra) && !map.hasOwnProperty(extra)) { + // check before inserting to requestMap + map[extra] = this.extras[extra]; + } + } + } + return map; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/token_request_handler.ts b/src/Umbraco.Web.UI.Client/src/external/openid/token_request_handler.ts new file mode 100644 index 0000000000..99ae687a39 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/token_request_handler.ts @@ -0,0 +1,88 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { AuthorizationServiceConfiguration } from './authorization_service_configuration.js'; +import { AppAuthError } from './errors.js'; +import { BasicQueryStringUtils } from './query_string_utils.js'; +import type { QueryStringUtils } from './query_string_utils.js'; +import type { RevokeTokenRequest } from './revoke_token_request.js'; +import type { TokenRequest } from './token_request.js'; +import { TokenError, TokenResponse } from './token_response.js'; +import type { TokenErrorJson, TokenResponseJson } from './token_response.js'; +import { JQueryRequestor } from './xhr.js'; +import type { Requestor } from './xhr.js'; + +/** + * Represents an interface which can make a token request. + */ +export interface TokenRequestHandler { + /** + * Performs the token request, given the service configuration. + */ + performTokenRequest(configuration: AuthorizationServiceConfiguration, request: TokenRequest): Promise; + + performRevokeTokenRequest( + configuration: AuthorizationServiceConfiguration, + request: RevokeTokenRequest, + ): Promise; +} + +/** + * The default token request handler. + */ +export class BaseTokenRequestHandler implements TokenRequestHandler { + constructor( + public readonly requestor: Requestor = new JQueryRequestor(), + public readonly utils: QueryStringUtils = new BasicQueryStringUtils(), + ) {} + + private isTokenResponse(response: TokenResponseJson | TokenErrorJson): response is TokenResponseJson { + return (response as TokenErrorJson).error === undefined; + } + + performRevokeTokenRequest( + configuration: AuthorizationServiceConfiguration, + request: RevokeTokenRequest, + ): Promise { + const revokeTokenResponse = this.requestor.xhr({ + url: configuration.revocationEndpoint, + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: this.utils.stringify(request.toStringMap()), + }); + + return revokeTokenResponse.then((response) => { + return true; + }); + } + + performTokenRequest(configuration: AuthorizationServiceConfiguration, request: TokenRequest): Promise { + const tokenResponse = this.requestor.xhr({ + url: configuration.tokenEndpoint, + method: 'POST', + dataType: 'json', // adding implicit dataType + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: this.utils.stringify(request.toStringMap()), + }); + + return tokenResponse.then((response) => { + if (this.isTokenResponse(response)) { + return new TokenResponse(response); + } else { + return Promise.reject(new AppAuthError(response.error, new TokenError(response))); + } + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/token_response.ts b/src/Umbraco.Web.UI.Client/src/external/openid/token_response.ts new file mode 100644 index 0000000000..3afbf101d7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/token_response.ts @@ -0,0 +1,137 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents the access token types. + * For more information see: + * https://tools.ietf.org/html/rfc6749#section-7.1 + */ +export type TokenType = 'bearer' | 'mac'; + +/** + * Represents the TokenResponse as a JSON Object. + */ +export interface TokenResponseJson { + access_token: string; + token_type?: TokenType /* treating token type as optional, as its going to be inferred. */; + expires_in?: string /* lifetime in seconds. */; + refresh_token?: string; + scope?: string; + id_token?: string /* https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse */; + issued_at?: number /* when was it issued ? */; +} + +/** + * Represents the possible error codes from the token endpoint. + * For more information look at: + * https://tools.ietf.org/html/rfc6749#section-5.2 + */ +export type ErrorType = + | 'invalid_request' + | 'invalid_client' + | 'invalid_grant' + | 'unauthorized_client' + | 'unsupported_grant_type' + | 'invalid_scope'; + +/** + * Represents the TokenError as a JSON Object. + */ +export interface TokenErrorJson { + error: ErrorType; + error_description?: string; + error_uri?: string; +} + +// constants +const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds + +/** + * Returns the instant of time in seconds. + */ +export const nowInSeconds = () => Math.round(new Date().getTime() / 1000); + +/** + * Represents the Token Response type. + * For more information look at: + * https://tools.ietf.org/html/rfc6749#section-5.1 + */ +export class TokenResponse { + accessToken: string; + tokenType: TokenType; + expiresIn: number | undefined; + refreshToken: string | undefined; + scope: string | undefined; + idToken: string | undefined; + issuedAt: number; + + constructor(response: TokenResponseJson) { + this.accessToken = response.access_token; + this.tokenType = response.token_type || 'bearer'; + if (response.expires_in) { + this.expiresIn = parseInt(response.expires_in, 10); + } + this.refreshToken = response.refresh_token; + this.scope = response.scope; + this.idToken = response.id_token; + this.issuedAt = response.issued_at || nowInSeconds(); + } + + toJson(): TokenResponseJson { + return { + access_token: this.accessToken, + id_token: this.idToken, + refresh_token: this.refreshToken, + scope: this.scope, + token_type: this.tokenType, + issued_at: this.issuedAt, + expires_in: this.expiresIn?.toString(), + }; + } + + isValid(buffer: number = AUTH_EXPIRY_BUFFER): boolean { + if (this.expiresIn) { + const now = nowInSeconds(); + return now < this.issuedAt + this.expiresIn + buffer; + } else { + return true; + } + } +} + +/** + * Represents the Token Error type. + * For more information look at: + * https://tools.ietf.org/html/rfc6749#section-5.2 + */ +export class TokenError { + error: ErrorType; + errorDescription: string | undefined; + errorUri: string | undefined; + + constructor(tokenError: TokenErrorJson) { + this.error = tokenError.error; + this.errorDescription = tokenError.error_description; + this.errorUri = tokenError.error_uri; + } + + toJson(): TokenErrorJson { + return { + error: this.error, + error_description: this.errorDescription, + error_uri: this.errorUri, + }; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/types.ts b/src/Umbraco.Web.UI.Client/src/external/openid/types.ts new file mode 100644 index 0000000000..3af235a085 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/types.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface StringMap { + [key: string]: string; +} + +/** + * Represents a window.location like object. + */ +export interface LocationLike { + hash: string; + host: string; + origin: string; + hostname: string; + pathname: string; + port: string; + protocol: string; + search: string; + assign(url: string): void; +} diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/xhr.ts b/src/Umbraco.Web.UI.Client/src/external/openid/xhr.ts new file mode 100644 index 0000000000..96bd2c82c8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/openid/xhr.ts @@ -0,0 +1,115 @@ +/* eslint-disable local-rules/umb-class-prefix */ +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppAuthError } from './errors.js'; + +/** + * An class that abstracts away the ability to make an XMLHttpRequest. + */ +export abstract class Requestor { + abstract xhr(settings: JQueryAjaxSettings): Promise; +} + +/** + * Uses $.ajax to makes the Ajax requests. + */ +export class JQueryRequestor extends Requestor { + xhr(settings: JQueryAjaxSettings): Promise { + // NOTE: using jquery to make XHR's as whatwg-fetch requires + // that I target ES6. + const xhr = $.ajax(settings); + return new Promise((resolve, reject) => { + xhr.then( + (data, textStatus, jqXhr) => { + resolve(data as T); + }, + (jqXhr, textStatus, error) => { + reject(new AppAuthError(error)); + }, + ); + }); + } +} + +/** + * Uses fetch API to make Ajax requests + */ +export class FetchRequestor extends Requestor { + xhr(settings: JQueryAjaxSettings): Promise { + if (!settings.url) { + return Promise.reject(new AppAuthError('A URL must be provided.')); + } + const url: URL = new URL(settings.url); + const requestInit: RequestInit = {}; + requestInit.method = settings.method; + requestInit.mode = 'cors'; + + if (settings.data) { + if (settings.method && settings.method.toUpperCase() === 'POST') { + requestInit.body = settings.data; + } else { + const searchParams = new URLSearchParams(settings.data); + searchParams.forEach((value, key) => { + url.searchParams.append(key, value); + }); + } + } + + // Set the request headers + requestInit.headers = {}; + if (settings.headers) { + for (const i in settings.headers) { + if (Object.prototype.hasOwnProperty.call(settings.headers, i)) { + requestInit.headers[i] = settings.headers[i]; + } + } + } + + const isJsonDataType = settings.dataType && settings.dataType.toLowerCase() === 'json'; + + // Set 'Accept' header value for json requests (Taken from + // https://github.com/jquery/jquery/blob/e0d941156900a6bff7c098c8ea7290528e468cf8/src/ajax.js#L644 + // ) + if (isJsonDataType) { + requestInit.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'; + } + + return fetch(url.toString(), requestInit).then((response) => { + if (response.status >= 200 && response.status < 300) { + const contentType = response.headers.get('content-type'); + if (isJsonDataType || (contentType && contentType.indexOf('application/json') !== -1)) { + return response.json(); + } else { + return response.text(); + } + } else { + return Promise.reject(new AppAuthError(response.status.toString(), response.statusText)); + } + }); + } +} + +/** + * Should be used only in the context of testing. Just uses the underlying + * Promise to mock the behavior of the Requestor. + */ +export class TestRequestor extends Requestor { + constructor(public promise: Promise) { + super(); + } + xhr(settings: JQueryAjaxSettings): Promise { + return this.promise; // unsafe cast + } +} diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 33bf53145e..a3a4e7e517 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -106,6 +106,7 @@ "@umbraco-cms/backoffice/webhook": ["./src/packages/webhook/index.ts"], "@umbraco-cms/backoffice/workspace": ["./src/packages/core/workspace/index.ts"], "@umbraco-cms/backoffice/external/backend-api": ["./src/external/backend-api/index.ts"], + "@umbraco-cms/backoffice/external/base64-js": ["./src/external/base64-js/index.ts"], "@umbraco-cms/backoffice/external/dompurify": ["./src/external/dompurify/index.ts"], "@umbraco-cms/backoffice/external/lit": ["./src/external/lit/index.ts"], "@umbraco-cms/backoffice/external/marked": ["./src/external/marked/index.ts"],