From c9ed90aeebaabca957ae1980738f732e5472ee9d Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Tue, 15 Feb 2022 16:58:36 +0100 Subject: [PATCH] fix: Update OIDC provider dependency to v7 The biggest resulting change is that the consent page always appears after logging in. Some minor fixes to be closer to the spec are included together with some minor structural refactors. --- .../identity/handler/interaction/routes.json | 4 +- .../{existing-login.json => consent.json} | 10 +- .../handler/interaction/routes/login.json | 3 +- .../handler/interaction/routes/prompt.json | 2 +- .../handler/interaction/views/html.json | 2 +- .../handler/provider-factory/identity.json | 16 +- package-lock.json | 894 +++++++++++++----- package.json | 3 +- src/identity/OidcHttpHandler.ts | 4 +- .../configuration/IdentityProviderFactory.ts | 74 +- .../CompletingInteractionHandler.ts | 48 - src/identity/interaction/ConsentHandler.ts | 111 +++ .../interaction/ExistingLoginHandler.ts | 25 - .../handler/ForgotPasswordHandler.ts | 2 +- .../email-password/handler/LoginHandler.ts | 35 +- .../util/BaseEmailSender.ts | 0 .../{ => email-password}/util/EmailSender.ts | 2 +- .../util/BaseInteractionCompleter.ts | 37 - .../interaction/util/InteractionCompleter.ts | 16 - src/index.ts | 11 +- test/integration/Identity.test.ts | 26 +- test/integration/IdentityTestState.ts | 24 +- test/integration/RestrictedIdentity.test.ts | 5 +- test/unit/identity/OidcHttpHandler.test.ts | 5 +- .../IdentityProviderFactory.test.ts | 31 +- .../CompletingInteractionHandler.test.ts | 76 -- .../interaction/ConsentHandler.test.ts | 142 +++ .../interaction/ExistingLoginHandler.test.ts | 38 - .../handler/ForgotPasswordHandler.test.ts | 2 +- .../handler/LoginHandler.test.ts | 32 +- .../util/BaseEmailSender.test.ts | 6 +- .../util/BaseInteractionCompleter.test.ts | 56 -- 32 files changed, 1081 insertions(+), 661 deletions(-) rename config/identity/handler/interaction/routes/{existing-login.json => consent.json} (61%) delete mode 100644 src/identity/interaction/CompletingInteractionHandler.ts create mode 100644 src/identity/interaction/ConsentHandler.ts delete mode 100644 src/identity/interaction/ExistingLoginHandler.ts rename src/identity/interaction/{ => email-password}/util/BaseEmailSender.ts (100%) rename src/identity/interaction/{ => email-password}/util/EmailSender.ts (75%) delete mode 100644 src/identity/interaction/util/BaseInteractionCompleter.ts delete mode 100644 src/identity/interaction/util/InteractionCompleter.ts delete mode 100644 test/unit/identity/interaction/CompletingInteractionHandler.test.ts create mode 100644 test/unit/identity/interaction/ConsentHandler.test.ts delete mode 100644 test/unit/identity/interaction/ExistingLoginHandler.test.ts rename test/unit/identity/interaction/{ => email-password}/util/BaseEmailSender.test.ts (82%) delete mode 100644 test/unit/identity/interaction/util/BaseInteractionCompleter.test.ts diff --git a/config/identity/handler/interaction/routes.json b/config/identity/handler/interaction/routes.json index 07d40db70..a15a177f9 100644 --- a/config/identity/handler/interaction/routes.json +++ b/config/identity/handler/interaction/routes.json @@ -1,7 +1,7 @@ { "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld", "import": [ - "files-scs:config/identity/handler/interaction/routes/existing-login.json", + "files-scs:config/identity/handler/interaction/routes/consent.json", "files-scs:config/identity/handler/interaction/routes/forgot-password.json", "files-scs:config/identity/handler/interaction/routes/index.json", "files-scs:config/identity/handler/interaction/routes/login.json", @@ -47,7 +47,7 @@ { "@id": "urn:solid-server:auth:password:IndexRouteHandler" }, { "@id": "urn:solid-server:auth:password:PromptRouteHandler" }, { "@id": "urn:solid-server:auth:password:LoginRouteHandler" }, - { "@id": "urn:solid-server:auth:password:ExistingLoginRouteHandler" }, + { "@id": "urn:solid-server:auth:password:ConsentRouteHandler" }, { "@id": "urn:solid-server:auth:password:ForgotPasswordRouteHandler" }, { "@id": "urn:solid-server:auth:password:ResetPasswordRouteHandler" } ] diff --git a/config/identity/handler/interaction/routes/existing-login.json b/config/identity/handler/interaction/routes/consent.json similarity index 61% rename from config/identity/handler/interaction/routes/existing-login.json rename to config/identity/handler/interaction/routes/consent.json index 915373ff0..e2fcfb60a 100644 --- a/config/identity/handler/interaction/routes/existing-login.json +++ b/config/identity/handler/interaction/routes/consent.json @@ -3,18 +3,18 @@ "@graph": [ { "comment": "Handles the interaction that occurs when a logged in user wants to authenticate with a new app.", - "@id": "urn:solid-server:auth:password:ExistingLoginRouteHandler", + "@id": "urn:solid-server:auth:password:ConsentRouteHandler", "@type":"InteractionRouteHandler", "route": { - "@id": "urn:solid-server:auth:password:ExistingLoginRoute", + "@id": "urn:solid-server:auth:password:ConsentRoute", "@type": "RelativePathInteractionRoute", "base": { "@id": "urn:solid-server:auth:password:IndexRoute" }, "relativePath": "/consent/" }, "source": { - "@id": "urn:solid-server:auth:password:ExistingLoginHandler", - "@type": "ExistingLoginHandler", - "interactionCompleter": { "@type": "BaseInteractionCompleter" } + "@id": "urn:solid-server:auth:password:ConsentHandler", + "@type": "ConsentHandler", + "providerFactory": { "@id": "urn:solid-server:default:IdentityProviderFactory" } } } ] diff --git a/config/identity/handler/interaction/routes/login.json b/config/identity/handler/interaction/routes/login.json index b3bf1b108..ecab17bda 100644 --- a/config/identity/handler/interaction/routes/login.json +++ b/config/identity/handler/interaction/routes/login.json @@ -14,8 +14,7 @@ "source": { "@id": "urn:solid-server:auth:password:LoginHandler", "@type": "LoginHandler", - "accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" }, - "interactionCompleter": { "@type": "BaseInteractionCompleter" } + "accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" } } } ] diff --git a/config/identity/handler/interaction/routes/prompt.json b/config/identity/handler/interaction/routes/prompt.json index 98cecae96..cea3ce87e 100644 --- a/config/identity/handler/interaction/routes/prompt.json +++ b/config/identity/handler/interaction/routes/prompt.json @@ -21,7 +21,7 @@ }, { "PromptHandler:_promptRoutes_key": "consent", - "PromptHandler:_promptRoutes_value": { "@id": "urn:solid-server:auth:password:ExistingLoginRoute" } + "PromptHandler:_promptRoutes_value": { "@id": "urn:solid-server:auth:password:ConsentRoute" } } ] } diff --git a/config/identity/handler/interaction/views/html.json b/config/identity/handler/interaction/views/html.json index ba6e568c9..d70f0170c 100644 --- a/config/identity/handler/interaction/views/html.json +++ b/config/identity/handler/interaction/views/html.json @@ -28,7 +28,7 @@ }, { "HtmlViewHandler:_templates_key": "@css:templates/identity/email-password/consent.html.ejs", - "HtmlViewHandler:_templates_value": { "@id": "urn:solid-server:auth:password:ExistingLoginRoute" } + "HtmlViewHandler:_templates_value": { "@id": "urn:solid-server:auth:password:ConsentRoute" } }, { "HtmlViewHandler:_templates_key": "@css:templates/identity/email-password/forgot-password.html.ejs", diff --git a/config/identity/handler/provider-factory/identity.json b/config/identity/handler/provider-factory/identity.json index 7efe13956..ed9c505f2 100644 --- a/config/identity/handler/provider-factory/identity.json +++ b/config/identity/handler/provider-factory/identity.json @@ -17,7 +17,7 @@ "args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" }, "config": { "claims": { - "openid": [ "client_id" ], + "openid": [ "azp" ], "webid": [ "webid" ] }, "cookies": { @@ -27,22 +27,24 @@ "features": { "claimsParameter": { "enabled": true }, "devInteractions": { "enabled": false }, - "dPoP": { "enabled": true, "ack": "draft-01" }, + "dPoP": { "enabled": true, "ack": "draft-03" }, "introspection": { "enabled": true }, "registration": { "enabled": true }, - "revocation": { "enabled": true } - }, - "formats": { - "AccessToken": "jwt" + "revocation": { "enabled": true }, + "userinfo": { "enabled": false } }, "scopes": [ "openid", "profile", "offline_access", "webid" ], "subjectTypes": [ "public" ], "ttl": { "AccessToken": 3600, "AuthorizationCode": 600, + "BackchannelAuthenticationRequest": 600, "DeviceCode": 600, + "Grant": 1209600, "IdToken": 3600, - "RefreshToken": 86400 + "Interaction": 3600, + "RefreshToken": 86400, + "Session": 1209600 } } } diff --git a/package-lock.json b/package-lock.json index b1e825606..34e7d8db0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@types/n3": "^1.10.4", "@types/node": "^14.18.0", "@types/nodemailer": "^6.4.4", + "@types/oidc-provider": "^7.8.1", "@types/pump": "^1.1.1", "@types/punycode": "^2.1.0", "@types/redis": "^2.8.30", @@ -48,7 +49,7 @@ "mime-types": "^2.1.34", "n3": "^1.13.0", "nodemailer": "^6.7.2", - "oidc-provider": "^6.31.1", + "oidc-provider": "^7.10.6", "pump": "^3.0.0", "punycode": "^2.1.1", "rdf-dereference": "^1.9.0", @@ -4177,14 +4178,6 @@ "node": ">= 8" } }, - "node_modules/@panva/asn1.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", - "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@rdfjs/types": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.0.1.tgz", @@ -4197,6 +4190,7 @@ "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, "engines": { "node": ">=6" } @@ -4236,6 +4230,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, "dependencies": { "defer-to-connect": "^1.0.1" }, @@ -4341,6 +4336,17 @@ "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "node_modules/@types/cheerio": { "version": "0.22.30", "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz", @@ -4443,6 +4449,11 @@ "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "node_modules/@types/http-errors": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", @@ -4507,6 +4518,14 @@ "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/koa": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.1.tgz", @@ -4605,6 +4624,14 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "node_modules/@types/oidc-provider": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-7.8.1.tgz", + "integrity": "sha512-MsmVKYFN9i27kfJh3hqj7F6aQNue6A/1aBKVJH07I3WYMriUDqMtYU0MWtheFPI1Tm9kwa0JHheaOdNRjuxboA==", + "dependencies": { + "@types/koa": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -4679,6 +4706,14 @@ "@types/bluebird": "*" } }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.3.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", @@ -5423,11 +5458,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -5898,10 +5928,19 @@ "node": ">= 6.0.0" } }, + "node_modules/cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "engines": { + "node": ">=10.6.0" + } + }, "node_modules/cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -5919,6 +5958,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "dependencies": { "pump": "^3.0.0" }, @@ -5933,6 +5973,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, "engines": { "node": ">=8" } @@ -6681,6 +6722,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, "dependencies": { "mimic-response": "^1.0.0" }, @@ -6726,7 +6768,8 @@ "node_modules/defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true }, "node_modules/define-properties": { "version": "1.1.3", @@ -6771,9 +6814,13 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.1.0.tgz", + "integrity": "sha512-R5QZrOXxSs0JDUIU/VANvRJlQVMts9C0L76HToQdPdlftfZCE7W6dyH0G4GZ5UW9fRqUOhAoCE2aGekuu+3HjQ==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detect-libc": { "version": "1.0.3", @@ -6939,7 +6986,8 @@ "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true }, "node_modules/ee-first": { "version": "1.1.1", @@ -8443,6 +8491,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "dependencies": { "pump": "^3.0.0" }, @@ -8485,12 +8534,6 @@ "node": ">=10" } }, - "node_modules/git-raw-commits/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/git-raw-commits/node_modules/through2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", @@ -8843,6 +8886,7 @@ "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, "dependencies": { "@sindresorhus/is": "^0.14.0", "@szmarczak/http-timer": "^1.1.2", @@ -9009,7 +9053,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9021,7 +9064,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -9121,67 +9163,50 @@ } }, "node_modules/http-assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz", - "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "dependencies": { "deep-equal": "~1.0.1", - "http-errors": "~1.7.2" + "http-errors": "~1.8.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/http-assert/node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-assert/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "node_modules/http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/http-errors/node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/http-errors/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/http-link-header": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.0.3.tgz", @@ -9203,6 +9228,29 @@ "node": ">= 6" } }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/http2-wrapper/node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -9440,9 +9488,9 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "2.0.0", @@ -9602,9 +9650,12 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", - "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -10755,7 +10806,8 @@ "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -10919,6 +10971,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, "dependencies": { "json-buffer": "3.0.0" } @@ -10942,16 +10995,16 @@ } }, "node_modules/koa": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.1.tgz", - "integrity": "sha512-Lb2Dloc72auj5vK4X4qqL7B5jyDPQaZucc9sR/71byg7ryoD1NCaCm63CShk9ID9quQvDEi1bGR/iGjCG7As3w==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz", + "integrity": "sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==", "dependencies": { "accepts": "^1.3.5", "cache-content-type": "^1.0.0", "content-disposition": "~0.5.2", "content-type": "^1.0.4", "cookies": "~0.8.0", - "debug": "~3.1.0", + "debug": "^4.3.2", "delegates": "^1.0.0", "depd": "^2.0.0", "destroy": "^1.0.4", @@ -10962,7 +11015,7 @@ "http-errors": "^1.6.3", "is-generator-function": "^1.0.7", "koa-compose": "^4.1.0", - "koa-convert": "^1.2.0", + "koa-convert": "^2.0.0", "on-finished": "^2.3.0", "only": "~0.0.2", "parseurl": "^1.3.2", @@ -10980,31 +11033,15 @@ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==" }, "node_modules/koa-convert": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", - "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", "dependencies": { "co": "^4.6.0", - "koa-compose": "^3.0.0" + "koa-compose": "^4.1.0" }, "engines": { - "node": ">= 4" - } - }, - "node_modules/koa-convert/node_modules/koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", - "dependencies": { - "any-promise": "^1.1.0" - } - }, - "node_modules/koa/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" + "node": ">= 10" } }, "node_modules/koa/node_modules/depd": { @@ -11206,6 +11243,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -11565,9 +11603,9 @@ } }, "node_modules/nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", + "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -11806,6 +11844,7 @@ "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, "engines": { "node": ">=8" } @@ -11868,9 +11907,9 @@ } }, "node_modules/object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { "node": ">= 6" } @@ -11976,44 +12015,141 @@ } }, "node_modules/oidc-provider": { - "version": "6.31.1", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-6.31.1.tgz", - "integrity": "sha512-YYfcJKGrobdaW2v7bx5crd4yfxFTKAoOTHdQs/gsu6fwzc/yD/qjforQX2VgbTX+ySK8nm7EaAwJuJJSFRo1MA==", + "version": "7.10.6", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-7.10.6.tgz", + "integrity": "sha512-7fbnormUyTLP34dmR5WXoJtTWtfj6MsFNzIMKVRKv21e18NIXggn14EBUFC5rrMMtmeExb03+lJI/v+opD+0oQ==", "dependencies": { "@koa/cors": "^3.1.0", - "@types/koa": "^2.11.4", - "debug": "^4.1.1", - "ejs": "^3.1.5", - "got": "^9.6.0", - "jose": "^2.0.4", - "jsesc": "^3.0.1", - "koa": "^2.13.0", + "cacheable-lookup": "^6.0.1", + "debug": "^4.3.2", + "ejs": "^3.1.6", + "got": "^11.8.2", + "jose": "^4.1.4", + "jsesc": "^3.0.2", + "koa": "^2.13.3", "koa-compose": "^4.1.0", - "lru-cache": "^6.0.0", - "nanoid": "^3.1.10", - "object-hash": "^2.0.3", + "nanoid": "^3.1.28", + "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.1", + "paseto2": "npm:paseto@^2.1.3", + "quick-lru": "^5.1.1", "raw-body": "^2.4.1" }, "engines": { - "node": "^10.13.0 || >=12.0.0" + "node": "^12.19.0 || ^14.15.0 || ^16.13.0" }, "funding": { "url": "https://github.com/sponsors/panva" + }, + "optionalDependencies": { + "paseto3": "npm:paseto@^3.0.0" } }, - "node_modules/oidc-provider/node_modules/jose": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", - "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", - "dependencies": { - "@panva/asn1.js": "^1.0.0" - }, + "node_modules/oidc-provider/node_modules/@sindresorhus/is": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", "engines": { - "node": ">=10.13.0 < 13 || >=13.7.0" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/panva" + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/oidc-provider/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/oidc-provider/node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/oidc-provider/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oidc-provider/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/oidc-provider/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oidc-provider/node_modules/got": { + "version": "11.8.3", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", + "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/oidc-provider/node_modules/got/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" } }, "node_modules/oidc-provider/node_modules/jsesc": { @@ -12027,6 +12163,76 @@ "node": ">=6" } }, + "node_modules/oidc-provider/node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/oidc-provider/node_modules/keyv": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/oidc-provider/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/oidc-provider/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oidc-provider/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oidc-provider/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/oidc-provider/node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oidc-provider/node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, "node_modules/oidc-token-hash": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", @@ -12130,6 +12336,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, "engines": { "node": ">=6" } @@ -12255,6 +12462,31 @@ "node": ">= 0.8" } }, + "node_modules/paseto2": { + "name": "paseto", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/paseto/-/paseto-2.1.3.tgz", + "integrity": "sha512-BNkbvr0ZFDbh3oV13QzT5jXIu8xpFc9r0o5mvWBhDU1GBkVt1IzHK1N6dcYmN7XImrUmPQ0HCUXmoe2WPo8xsg==", + "engines": { + "node": "^12.19.0 || >=14.15.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/paseto3": { + "name": "paseto", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/paseto/-/paseto-3.1.0.tgz", + "integrity": "sha512-oVSKoCH89M0WU3I+13NoCP9wGRel0BlQumwxsDZPk1yJtqS76PWKRM7vM9D4bz4PcScT0aIiAipC7lW6hSgkBQ==", + "optional": true, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -12370,6 +12602,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, "engines": { "node": ">=4" } @@ -12571,11 +12804,6 @@ "node": ">= 0.6" } }, - "node_modules/raw-body/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -13047,6 +13275,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -13090,6 +13323,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, "dependencies": { "lowercase-keys": "^1.0.0" } @@ -13972,6 +14206,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true, "engines": { "node": ">=6" } @@ -14349,6 +14584,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, "dependencies": { "prepend-http": "^2.0.0" }, @@ -18080,11 +18316,6 @@ "fastq": "^1.6.0" } }, - "@panva/asn1.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", - "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==" - }, "@rdfjs/types": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.0.1.tgz", @@ -18096,7 +18327,8 @@ "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true }, "@sinonjs/commons": { "version": "1.8.3", @@ -18133,6 +18365,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, "requires": { "defer-to-connect": "^1.0.1" } @@ -18232,6 +18465,17 @@ "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/cheerio": { "version": "0.22.30", "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz", @@ -18334,6 +18578,11 @@ "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "@types/http-errors": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", @@ -18398,6 +18647,14 @@ "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" }, + "@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "requires": { + "@types/node": "*" + } + }, "@types/koa": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.1.tgz", @@ -18496,6 +18753,14 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/oidc-provider": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-7.8.1.tgz", + "integrity": "sha512-MsmVKYFN9i27kfJh3hqj7F6aQNue6A/1aBKVJH07I3WYMriUDqMtYU0MWtheFPI1Tm9kwa0JHheaOdNRjuxboA==", + "requires": { + "@types/koa": "*" + } + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -18569,6 +18834,14 @@ "@types/bluebird": "*" } }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/semver": { "version": "7.3.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", @@ -19071,11 +19344,6 @@ } } }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -19446,10 +19714,16 @@ "ylru": "^1.2.0" } }, + "cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==" + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -19464,6 +19738,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -19471,7 +19746,8 @@ "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true } } }, @@ -20071,6 +20347,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, "requires": { "mimic-response": "^1.0.0" } @@ -20107,7 +20384,8 @@ "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true }, "define-properties": { "version": "1.1.3", @@ -20140,9 +20418,9 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.1.0.tgz", + "integrity": "sha512-R5QZrOXxSs0JDUIU/VANvRJlQVMts9C0L76HToQdPdlftfZCE7W6dyH0G4GZ5UW9fRqUOhAoCE2aGekuu+3HjQ==" }, "detect-libc": { "version": "1.0.3", @@ -20261,7 +20539,8 @@ "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true }, "ee-first": { "version": "1.1.1", @@ -21402,6 +21681,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -21429,12 +21709,6 @@ "through2": "^3.0.0" }, "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "through2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", @@ -21694,6 +21968,7 @@ "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, "requires": { "@sindresorhus/is": "^0.14.0", "@szmarczak/http-timer": "^1.1.2", @@ -21827,14 +22102,12 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -21908,31 +22181,12 @@ } }, "http-assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz", - "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "requires": { "deep-equal": "~1.0.1", - "http-errors": "~1.7.2" - }, - "dependencies": { - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } + "http-errors": "~1.8.0" } }, "http-cache-semantics": { @@ -21941,26 +22195,26 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "requires": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "toidentifier": "1.0.1" }, "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" } } }, @@ -21979,6 +22233,22 @@ "debug": "4" } }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "dependencies": { + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + } + } + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -22140,9 +22410,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "2.0.0", @@ -22256,9 +22526,12 @@ "dev": true }, "is-generator-function": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", - "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-glob": { "version": "4.0.3", @@ -23126,7 +23399,8 @@ "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true }, "json-parse-better-errors": { "version": "1.0.2", @@ -23265,6 +23539,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, "requires": { "json-buffer": "3.0.0" } @@ -23282,16 +23557,16 @@ "dev": true }, "koa": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.1.tgz", - "integrity": "sha512-Lb2Dloc72auj5vK4X4qqL7B5jyDPQaZucc9sR/71byg7ryoD1NCaCm63CShk9ID9quQvDEi1bGR/iGjCG7As3w==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz", + "integrity": "sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==", "requires": { "accepts": "^1.3.5", "cache-content-type": "^1.0.0", "content-disposition": "~0.5.2", "content-type": "^1.0.4", "cookies": "~0.8.0", - "debug": "~3.1.0", + "debug": "^4.3.2", "delegates": "^1.0.0", "depd": "^2.0.0", "destroy": "^1.0.4", @@ -23302,7 +23577,7 @@ "http-errors": "^1.6.3", "is-generator-function": "^1.0.7", "koa-compose": "^4.1.0", - "koa-convert": "^1.2.0", + "koa-convert": "^2.0.0", "on-finished": "^2.3.0", "only": "~0.0.2", "parseurl": "^1.3.2", @@ -23311,14 +23586,6 @@ "vary": "^1.1.2" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -23332,22 +23599,12 @@ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==" }, "koa-convert": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", - "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", "requires": { "co": "^4.6.0", - "koa-compose": "^3.0.0" - }, - "dependencies": { - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", - "requires": { - "any-promise": "^1.1.0" - } - } + "koa-compose": "^4.1.0" } }, "kuler": { @@ -23520,7 +23777,8 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true }, "lru-cache": { "version": "6.0.0", @@ -23788,9 +24046,9 @@ } }, "nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.0.tgz", + "integrity": "sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==" }, "natural-compare": { "version": "1.4.0", @@ -23976,7 +24234,8 @@ "normalize-url": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true }, "npm-run-path": { "version": "4.0.1", @@ -24024,9 +24283,9 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" }, "object-inspect": { "version": "1.11.1", @@ -24099,38 +24358,151 @@ } }, "oidc-provider": { - "version": "6.31.1", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-6.31.1.tgz", - "integrity": "sha512-YYfcJKGrobdaW2v7bx5crd4yfxFTKAoOTHdQs/gsu6fwzc/yD/qjforQX2VgbTX+ySK8nm7EaAwJuJJSFRo1MA==", + "version": "7.10.6", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-7.10.6.tgz", + "integrity": "sha512-7fbnormUyTLP34dmR5WXoJtTWtfj6MsFNzIMKVRKv21e18NIXggn14EBUFC5rrMMtmeExb03+lJI/v+opD+0oQ==", "requires": { "@koa/cors": "^3.1.0", - "@types/koa": "^2.11.4", - "debug": "^4.1.1", - "ejs": "^3.1.5", - "got": "^9.6.0", - "jose": "^2.0.4", - "jsesc": "^3.0.1", - "koa": "^2.13.0", + "cacheable-lookup": "^6.0.1", + "debug": "^4.3.2", + "ejs": "^3.1.6", + "got": "^11.8.2", + "jose": "^4.1.4", + "jsesc": "^3.0.2", + "koa": "^2.13.3", "koa-compose": "^4.1.0", - "lru-cache": "^6.0.0", - "nanoid": "^3.1.10", - "object-hash": "^2.0.3", + "nanoid": "^3.1.28", + "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.1", + "paseto2": "npm:paseto@^2.1.3", + "paseto3": "npm:paseto@^3.0.0", + "quick-lru": "^5.1.1", "raw-body": "^2.4.1" }, "dependencies": { - "jose": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", - "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", + "@sindresorhus/is": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==" + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "requires": { - "@panva/asn1.js": "^1.0.0" + "defer-to-connect": "^2.0.0" + } + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "11.8.3", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", + "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + } } }, "jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "keyv": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } } } }, @@ -24212,7 +24584,8 @@ "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true }, "p-limit": { "version": "2.3.0", @@ -24307,6 +24680,17 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "paseto2": { + "version": "npm:paseto@2.1.3", + "resolved": "https://registry.npmjs.org/paseto/-/paseto-2.1.3.tgz", + "integrity": "sha512-BNkbvr0ZFDbh3oV13QzT5jXIu8xpFc9r0o5mvWBhDU1GBkVt1IzHK1N6dcYmN7XImrUmPQ0HCUXmoe2WPo8xsg==" + }, + "paseto3": { + "version": "npm:paseto@3.1.0", + "resolved": "https://registry.npmjs.org/paseto/-/paseto-3.1.0.tgz", + "integrity": "sha512-oVSKoCH89M0WU3I+13NoCP9wGRel0BlQumwxsDZPk1yJtqS76PWKRM7vM9D4bz4PcScT0aIiAipC7lW6hSgkBQ==", + "optional": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -24388,7 +24772,8 @@ "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true }, "pretty-format": { "version": "27.4.6", @@ -24540,11 +24925,6 @@ "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" } } }, @@ -24943,6 +25323,11 @@ "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -24976,6 +25361,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, "requires": { "lowercase-keys": "^1.0.0" } @@ -25693,7 +26079,8 @@ "to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true }, "to-regex-range": { "version": "5.0.1", @@ -25960,6 +26347,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, "requires": { "prepend-http": "^2.0.0" } diff --git a/package.json b/package.json index 1ddbc435d..814bbc0ce 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@types/n3": "^1.10.4", "@types/node": "^14.18.0", "@types/nodemailer": "^6.4.4", + "@types/oidc-provider": "^7.8.1", "@types/pump": "^1.1.1", "@types/punycode": "^2.1.0", "@types/redis": "^2.8.30", @@ -114,7 +115,7 @@ "mime-types": "^2.1.34", "n3": "^1.13.0", "nodemailer": "^6.7.2", - "oidc-provider": "^6.31.1", + "oidc-provider": "^7.10.6", "pump": "^3.0.0", "punycode": "^2.1.1", "rdf-dereference": "^1.9.0", diff --git a/src/identity/OidcHttpHandler.ts b/src/identity/OidcHttpHandler.ts index a8a3fe924..03fb340d6 100644 --- a/src/identity/OidcHttpHandler.ts +++ b/src/identity/OidcHttpHandler.ts @@ -20,8 +20,8 @@ export class OidcHttpHandler extends HttpHandler { const provider = await this.providerFactory.getProvider(); this.logger.debug(`Sending request to oidc-provider: ${request.url}`); // Even though the typings do not indicate this, this is a Promise that needs to be awaited. - // Otherwise the `BaseHttpServerFactory` will write a 404 before the OIDC library could handle the response. + // Otherwise, the `BaseHttpServerFactory` will write a 404 before the OIDC library could handle the response. // eslint-disable-next-line @typescript-eslint/await-thenable - await provider.callback(request, response); + await provider.callback()(request, response); } } diff --git a/src/identity/configuration/IdentityProviderFactory.ts b/src/identity/configuration/IdentityProviderFactory.ts index fc3125422..3f094e459 100644 --- a/src/identity/configuration/IdentityProviderFactory.ts +++ b/src/identity/configuration/IdentityProviderFactory.ts @@ -4,13 +4,14 @@ import { randomBytes } from 'crypto'; import type { JWK } from 'jose'; import { exportJWK, generateKeyPair } from 'jose'; -import type { AnyObject, +import type { Account, + Adapter, CanBePromise, - KoaContextWithOIDC, Configuration, - Account, ErrorOut, - Adapter } from 'oidc-provider'; + KoaContextWithOIDC, + ResourceServer, + UnknownObject } from 'oidc-provider'; import { Provider } from 'oidc-provider'; import type { Operation } from '../../http/Operation'; import type { ErrorHandler } from '../../http/output/error/ErrorHandler'; @@ -75,6 +76,7 @@ export class IdentityProviderFactory implements ProviderFactory { private readonly errorHandler!: ErrorHandler; private readonly responseWriter!: ResponseWriter; + private readonly jwtAlg = 'ES256'; private provider?: Provider; /** @@ -139,11 +141,23 @@ export class IdentityProviderFactory implements ProviderFactory { keys: await this.generateCookieKeys(), }; + // Solid OIDC requires pkce https://solid.github.io/solid-oidc/#concepts + config.pkce = { + methods: [ 'S256' ], + required: (): true => true, + }; + + // Default client settings that might not be defined. + // Mostly relevant for WebID clients. + config.clientDefaults = { + id_token_signed_response_alg: this.jwtAlg, + }; + return config; } /** - * Generates a JWKS using a single RS256 JWK.. + * Generates a JWKS using a single JWK. * The JWKS will be cached so subsequent calls return the same key. */ private async generateJwks(): Promise<{ keys: JWK[] }> { @@ -153,10 +167,10 @@ export class IdentityProviderFactory implements ProviderFactory { return jwks; } // If they are not, generate and save them - const { privateKey } = await generateKeyPair('RS256'); + const { privateKey } = await generateKeyPair(this.jwtAlg); const jwk = await exportJWK(privateKey); // Required for Solid authn client - jwk.alg = 'RS256'; + jwk.alg = this.jwtAlg; // In node v15.12.0 the JWKS does not get accepted because the JWK is not a plain object, // which is why we convert it into a plain object here. // Potentially this can be changed at a later point in time to `{ keys: [ jwk ]}`. @@ -190,28 +204,51 @@ export class IdentityProviderFactory implements ProviderFactory { } /** - * Adds the necessary claims the to id token and access token based on the Solid OIDC spec. + * Adds the necessary claims the to id and access tokens based on the Solid OIDC spec. */ private configureClaims(config: Configuration): void { - // Access token audience is 'solid', ID token audience is the client_id - config.audiences = (ctx, sub, token, use): string => - use === 'access_token' ? 'solid' : token.clientId!; - // Returns the id_token // See https://solid.github.io/authentication-panel/solid-oidc/#tokens-id + // Some fields are still missing, see https://github.com/solid/community-server/issues/1154#issuecomment-1040233385 config.findAccount = async(ctx: KoaContextWithOIDC, sub: string): Promise => ({ accountId: sub, - claims: async(): Promise<{ sub: string; [key: string]: any }> => - ({ sub, webid: sub }), + async claims(): Promise<{ sub: string; [key: string]: any }> { + return { sub, webid: sub, azp: ctx.oidc.client?.clientId }; + }, }); // Add extra claims in case an AccessToken is being issued. // Specifically this sets the required webid and client_id claims for the access token - // See https://solid.github.io/authentication-panel/solid-oidc/#tokens-access - config.extraAccessTokenClaims = (ctx, token): CanBePromise => + // See https://solid.github.io/solid-oidc/#resource-access-validation + config.extraTokenClaims = (ctx, token): CanBePromise => this.isAccessToken(token) ? - { webid: token.accountId, client_id: token.clientId } : + { webid: token.accountId } : {}; + + config.features = { + ...config.features, + resourceIndicators: { + defaultResource(): string { + // This value is irrelevant, but is necessary to trigger the `getResourceServerInfo` call below, + // where it will be an input parameter in case the client provided no value. + // Note that an empty string is not a valid value. + return 'http://example.com/'; + }, + enabled: true, + // This call is necessary to force the OIDC library to return a JWT access token. + // See https://github.com/panva/node-oidc-provider/discussions/959#discussioncomment-524757 + getResourceServerInfo: (): ResourceServer => ({ + // The scopes of the Resource Server. + // Since this is irrelevant at the moment, an empty string is fine. + scope: '', + audience: 'solid', + accessTokenFormat: 'jwt', + jwt: { + sign: { alg: this.jwtAlg }, + }, + }), + }, + }; } /** @@ -230,6 +267,7 @@ export class IdentityProviderFactory implements ProviderFactory { // When oidc-provider cannot fulfill the authorization request for any of the possible reasons // (missing user session, requested ACR not fulfilled, prompt requested, ...) // it will resolve the interactions.url helper function and redirect the User-Agent to that url. + // Another requirement is that `features.userinfo` is disabled in the configuration. config.interactions = { url: async(ctx, oidcInteraction): Promise => { const operation: Operation = { @@ -255,7 +293,7 @@ export class IdentityProviderFactory implements ProviderFactory { config.routes = { authorization: this.createRoute('auth'), - check_session: this.createRoute('session/check'), + backchannel_authentication: this.createRoute('backchannel'), code_verification: this.createRoute('device'), device_authorization: this.createRoute('device/auth'), end_session: this.createRoute('session/end'), diff --git a/src/identity/interaction/CompletingInteractionHandler.ts b/src/identity/interaction/CompletingInteractionHandler.ts deleted file mode 100644 index 3a460011e..000000000 --- a/src/identity/interaction/CompletingInteractionHandler.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError'; -import { FoundHttpError } from '../../util/errors/FoundHttpError'; -import { BaseInteractionHandler } from './BaseInteractionHandler'; -import type { InteractionHandlerInput } from './InteractionHandler'; -import type { InteractionCompleterInput, InteractionCompleter } from './util/InteractionCompleter'; - -/** - * Abstract extension of {@link BaseInteractionHandler} for handlers that need to call an {@link InteractionCompleter}. - * This is required by handlers that handle IDP behaviour - * and need to complete an OIDC interaction by redirecting back to the client, - * such as when logging in. - * - * Calls the InteractionCompleter with the results returned by the helper function - * and throw a corresponding {@link FoundHttpError}. - */ -export abstract class CompletingInteractionHandler extends BaseInteractionHandler { - protected readonly interactionCompleter: InteractionCompleter; - - protected constructor(view: Record, interactionCompleter: InteractionCompleter) { - super(view); - this.interactionCompleter = interactionCompleter; - } - - public async canHandle(input: InteractionHandlerInput): Promise { - await super.canHandle(input); - if (input.operation.method === 'POST' && !input.oidcInteraction) { - throw new BadRequestHttpError( - 'This action can only be performed as part of an OIDC authentication flow.', - { errorCode: 'E0002' }, - ); - } - } - - public async handlePost(input: InteractionHandlerInput): Promise { - // Interaction is defined due to canHandle call - const parameters = await this.getCompletionParameters(input as Required); - const location = await this.interactionCompleter.handleSafe(parameters); - throw new FoundHttpError(location); - } - - /** - * Generates the parameters necessary to call an InteractionCompleter. - * The input parameters are the same that the `handlePost` function was called with. - * @param input - The original input parameters to the `handle` function. - */ - protected abstract getCompletionParameters(input: Required): - Promise; -} diff --git a/src/identity/interaction/ConsentHandler.ts b/src/identity/interaction/ConsentHandler.ts new file mode 100644 index 000000000..a7cf11144 --- /dev/null +++ b/src/identity/interaction/ConsentHandler.ts @@ -0,0 +1,111 @@ +import type { InteractionResults, KoaContextWithOIDC, UnknownObject } from 'oidc-provider'; +import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError'; +import { FoundHttpError } from '../../util/errors/FoundHttpError'; +import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError'; +import { readJsonStream } from '../../util/StreamUtil'; +import type { ProviderFactory } from '../configuration/ProviderFactory'; +import { BaseInteractionHandler } from './BaseInteractionHandler'; +import type { Interaction, InteractionHandlerInput } from './InteractionHandler'; + +type Grant = NonNullable; + +/** + * Handles the OIDC consent prompts where the user confirms they want to log in for the given client. + */ +export class ConsentHandler extends BaseInteractionHandler { + private readonly providerFactory: ProviderFactory; + + public constructor(providerFactory: ProviderFactory) { + super({}); + this.providerFactory = providerFactory; + } + + public async canHandle(input: InteractionHandlerInput): Promise { + await super.canHandle(input); + if (input.operation.method === 'POST' && !input.oidcInteraction) { + throw new BadRequestHttpError( + 'This action can only be performed as part of an OIDC authentication flow.', + { errorCode: 'E0002' }, + ); + } + } + + protected async handlePost({ operation, oidcInteraction }: InteractionHandlerInput): Promise { + const { remember } = await readJsonStream(operation.body.data); + + const grant = await this.getGrant(oidcInteraction!); + this.updateGrant(grant, oidcInteraction!.prompt.details, remember); + + const location = await this.updateInteraction(oidcInteraction!, grant); + + throw new FoundHttpError(location); + } + + /** + * Either returns the grant associated with the given interaction or creates a new one if it does not exist yet. + */ + private async getGrant(oidcInteraction: Interaction): Promise { + if (!oidcInteraction.session) { + throw new NotImplementedHttpError('Only interactions with a valid session are supported.'); + } + + const { params, session: { accountId }, grantId } = oidcInteraction; + const provider = await this.providerFactory.getProvider(); + let grant: Grant; + if (grantId) { + grant = (await provider.Grant.find(grantId))!; + } else { + grant = new provider.Grant({ + accountId, + clientId: params.client_id as string, + }); + } + return grant; + } + + /** + * Updates the grant with all the missing scopes and claims requested by the interaction. + * + * Will reject the `offline_access` scope if `remember` is false. + */ + private updateGrant(grant: Grant, details: UnknownObject, remember: boolean): void { + // Reject the offline_access scope if the user does not want to be remembered + if (!remember) { + grant.rejectOIDCScope('offline_access'); + } + + // Grant all the requested scopes and claims + if (details.missingOIDCScope) { + grant.addOIDCScope((details.missingOIDCScope as string[]).join(' ')); + } + if (details.missingOIDCClaims) { + grant.addOIDCClaims(details.missingOIDCClaims as string[]); + } + if (details.missingResourceScopes) { + for (const [ indicator, scopes ] of Object.entries(details.missingResourceScopes as Record)) { + grant.addResourceScope(indicator, scopes.join(' ')); + } + } + } + + /** + * Updates the interaction with the new grant and returns the resulting redirect URL. + */ + private async updateInteraction(oidcInteraction: Interaction, grant: Grant): Promise { + const grantId = await grant.save(); + + const consent: InteractionResults['consent'] = {}; + // Only need to update the grantId if it is new + if (!oidcInteraction.grantId) { + consent.grantId = grantId; + } + + const result: InteractionResults = { consent }; + + // Need to merge with previous submission + oidcInteraction.result = { ...oidcInteraction.lastSubmission, ...result }; + await oidcInteraction.save(oidcInteraction.exp - Math.floor(Date.now() / 1000)); + + return oidcInteraction.returnTo; + } +} diff --git a/src/identity/interaction/ExistingLoginHandler.ts b/src/identity/interaction/ExistingLoginHandler.ts deleted file mode 100644 index 94755405f..000000000 --- a/src/identity/interaction/ExistingLoginHandler.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError'; -import { readJsonStream } from '../../util/StreamUtil'; -import { CompletingInteractionHandler } from './CompletingInteractionHandler'; -import type { InteractionHandlerInput } from './InteractionHandler'; -import type { InteractionCompleter, InteractionCompleterInput } from './util/InteractionCompleter'; - -/** - * Simple CompletingInteractionRoute that returns the session accountId as webId. - * This is relevant when a client already logged in this session and tries logging in again. - */ -export class ExistingLoginHandler extends CompletingInteractionHandler { - public constructor(interactionCompleter: InteractionCompleter) { - super({}, interactionCompleter); - } - - protected async getCompletionParameters({ operation, oidcInteraction }: Required): - Promise { - if (!oidcInteraction.session) { - throw new NotImplementedHttpError('Only interactions with a valid session are supported.'); - } - - const { remember } = await readJsonStream(operation.body.data); - return { oidcInteraction, webId: oidcInteraction.session.accountId, shouldRemember: Boolean(remember) }; - } -} diff --git a/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts b/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts index a4ad43ebc..500691273 100644 --- a/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts +++ b/src/identity/interaction/email-password/handler/ForgotPasswordHandler.ts @@ -8,8 +8,8 @@ import type { TemplateEngine } from '../../../../util/templates/TemplateEngine'; import { BaseInteractionHandler } from '../../BaseInteractionHandler'; import type { InteractionHandlerInput } from '../../InteractionHandler'; import type { InteractionRoute } from '../../routing/InteractionRoute'; -import type { EmailSender } from '../../util/EmailSender'; import type { AccountStore } from '../storage/AccountStore'; +import type { EmailSender } from '../util/EmailSender'; const forgotPasswordView = { required: { diff --git a/src/identity/interaction/email-password/handler/LoginHandler.ts b/src/identity/interaction/email-password/handler/LoginHandler.ts index 0f8e9f04c..6b6a9c1c3 100644 --- a/src/identity/interaction/email-password/handler/LoginHandler.ts +++ b/src/identity/interaction/email-password/handler/LoginHandler.ts @@ -1,11 +1,12 @@ import assert from 'assert'; +import type { InteractionResults } from 'oidc-provider'; import type { Operation } from '../../../../http/Operation'; import { getLoggerFor } from '../../../../logging/LogUtil'; import { BadRequestHttpError } from '../../../../util/errors/BadRequestHttpError'; +import { FoundHttpError } from '../../../../util/errors/FoundHttpError'; import { readJsonStream } from '../../../../util/StreamUtil'; -import { CompletingInteractionHandler } from '../../CompletingInteractionHandler'; +import { BaseInteractionHandler } from '../../BaseInteractionHandler'; import type { InteractionHandlerInput } from '../../InteractionHandler'; -import type { InteractionCompleterInput, InteractionCompleter } from '../../util/InteractionCompleter'; import type { AccountStore } from '../storage/AccountStore'; const loginView = { @@ -26,19 +27,27 @@ interface LoginInput { * Handles the submission of the Login Form and logs the user in. * Will throw a RedirectHttpError on success. */ -export class LoginHandler extends CompletingInteractionHandler { +export class LoginHandler extends BaseInteractionHandler { protected readonly logger = getLoggerFor(this); private readonly accountStore: AccountStore; - public constructor(accountStore: AccountStore, interactionCompleter: InteractionCompleter) { - super(loginView, interactionCompleter); + public constructor(accountStore: AccountStore) { + super(loginView); this.accountStore = accountStore; } - protected async getCompletionParameters(input: Required): - Promise { - const { operation, oidcInteraction } = input; + public async canHandle(input: InteractionHandlerInput): Promise { + await super.canHandle(input); + if (input.operation.method === 'POST' && !input.oidcInteraction) { + throw new BadRequestHttpError( + 'This action can only be performed as part of an OIDC authentication flow.', + { errorCode: 'E0002' }, + ); + } + } + + public async handlePost({ operation, oidcInteraction }: InteractionHandlerInput): Promise { const { email, password, remember } = await this.parseInput(operation); // Try to log in, will error if email/password combination is invalid const webId = await this.accountStore.authenticate(email, password); @@ -49,7 +58,15 @@ export class LoginHandler extends CompletingInteractionHandler { } this.logger.debug(`Logging in user ${email}`); - return { oidcInteraction, webId, shouldRemember: remember }; + // Update the interaction to get the redirect URL + const login: InteractionResults['login'] = { + accountId: webId, + remember, + }; + oidcInteraction!.result = { login }; + await oidcInteraction!.save(oidcInteraction!.exp - Math.floor(Date.now() / 1000)); + + throw new FoundHttpError(oidcInteraction!.returnTo); } /** diff --git a/src/identity/interaction/util/BaseEmailSender.ts b/src/identity/interaction/email-password/util/BaseEmailSender.ts similarity index 100% rename from src/identity/interaction/util/BaseEmailSender.ts rename to src/identity/interaction/email-password/util/BaseEmailSender.ts diff --git a/src/identity/interaction/util/EmailSender.ts b/src/identity/interaction/email-password/util/EmailSender.ts similarity index 75% rename from src/identity/interaction/util/EmailSender.ts rename to src/identity/interaction/email-password/util/EmailSender.ts index 30a9a748d..ac6198e54 100644 --- a/src/identity/interaction/util/EmailSender.ts +++ b/src/identity/interaction/email-password/util/EmailSender.ts @@ -1,4 +1,4 @@ -import { AsyncHandler } from '../../../util/handlers/AsyncHandler'; +import { AsyncHandler } from '../../../../util/handlers/AsyncHandler'; export interface EmailArgs { recipient: string; diff --git a/src/identity/interaction/util/BaseInteractionCompleter.ts b/src/identity/interaction/util/BaseInteractionCompleter.ts deleted file mode 100644 index f70cc24a9..000000000 --- a/src/identity/interaction/util/BaseInteractionCompleter.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { InteractionResults } from 'oidc-provider'; -import type { InteractionCompleterInput } from './InteractionCompleter'; -import { InteractionCompleter } from './InteractionCompleter'; - -/** - * Creates a simple InteractionResults object based on the input parameters and injects it in the Interaction. - */ -export class BaseInteractionCompleter extends InteractionCompleter { - public async handle(input: InteractionCompleterInput): Promise { - const now = Math.floor(Date.now() / 1000); - const result: InteractionResults = { - login: { - account: input.webId, - // Indicates if a persistent cookie should be used instead of a session cookie. - remember: input.shouldRemember, - ts: now, - }, - consent: { - // When OIDC clients want a refresh token, they need to request the 'offline_access' scope. - // This indicates that this scope is not granted to the client in case they do not want to be remembered. - rejectedScopes: input.shouldRemember ? [] : [ 'offline_access' ], - }, - }; - - // Generates the URL a client needs to be redirected to - // after a successful interaction completion (such as logging in). - // Identical behaviour to calling `provider.interactionResult`. - // We use the code below instead of calling that function - // since that function also uses Request/Response objects to generate the Interaction object, - // which we already have here. - const { oidcInteraction } = input; - oidcInteraction.result = { ...oidcInteraction.lastSubmission, ...result }; - await oidcInteraction.save(oidcInteraction.exp - now); - - return oidcInteraction.returnTo; - } -} diff --git a/src/identity/interaction/util/InteractionCompleter.ts b/src/identity/interaction/util/InteractionCompleter.ts deleted file mode 100644 index 20dd4ecef..000000000 --- a/src/identity/interaction/util/InteractionCompleter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AsyncHandler } from '../../../util/handlers/AsyncHandler'; -import type { Interaction } from '../InteractionHandler'; - -/** - * Parameters required to specify how the interaction should be completed. - */ -export interface InteractionCompleterInput { - oidcInteraction: Interaction; - webId: string; - shouldRemember?: boolean; -} - -/** - * Class responsible for completing the interaction based on the parameters provided. - */ -export abstract class InteractionCompleter extends AsyncHandler {} diff --git a/src/index.ts b/src/index.ts index 12dd3b8e9..47587d80e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -139,6 +139,8 @@ export * from './identity/interaction/email-password/storage/AccountStore'; export * from './identity/interaction/email-password/storage/BaseAccountStore'; // Identity/Interaction/Email-Password/Util +export * from './identity/interaction/email-password/util/BaseEmailSender'; +export * from './identity/interaction/email-password/util/EmailSender'; export * from './identity/interaction/email-password/util/RegistrationManager'; // Identity/Interaction/Email-Password @@ -150,16 +152,9 @@ export * from './identity/interaction/routing/InteractionRoute'; export * from './identity/interaction/routing/InteractionRouteHandler'; export * from './identity/interaction/routing/RelativePathInteractionRoute'; -// Identity/Interaction/Util -export * from './identity/interaction/util/BaseEmailSender'; -export * from './identity/interaction/util/BaseInteractionCompleter'; -export * from './identity/interaction/util/EmailSender'; -export * from './identity/interaction/util/InteractionCompleter'; - // Identity/Interaction export * from './identity/interaction/BaseInteractionHandler'; -export * from './identity/interaction/CompletingInteractionHandler'; -export * from './identity/interaction/ExistingLoginHandler'; +export * from './identity/interaction/ConsentHandler'; export * from './identity/interaction/ControlHandler'; export * from './identity/interaction/FixedInteractionHandler'; export * from './identity/interaction/HtmlViewHandler'; diff --git a/test/integration/Identity.test.ts b/test/integration/Identity.test.ts index 3be14c145..5e3ddacf0 100644 --- a/test/integration/Identity.test.ts +++ b/test/integration/Identity.test.ts @@ -138,10 +138,11 @@ describe('A Solid server with IDP', (): void => { }); it('initializes the session and logs in.', async(): Promise => { - const url = await state.startSession(); + let url = await state.startSession(); const res = await state.fetchIdp(url); expect(res.status).toBe(200); - await state.login(url, email, password); + url = await state.login(url, email, password); + await state.consent(url); expect(state.session.info?.webId).toBe(webId); }); @@ -162,16 +163,12 @@ describe('A Solid server with IDP', (): void => { it('can log in again.', async(): Promise => { const url = await state.startSession(); - let res = await state.fetchIdp(url); + const res = await state.fetchIdp(url); expect(res.status).toBe(200); // Will receive confirm screen here instead of login screen - res = await state.fetchIdp(url, 'POST', '', APPLICATION_X_WWW_FORM_URLENCODED); - const json = await res.json(); - const nextUrl = json.location; - expect(typeof nextUrl).toBe('string'); + await state.consent(url); - await state.handleLoginRedirect(nextUrl); expect(state.session.info?.webId).toBe(webId); }); }); @@ -223,10 +220,11 @@ describe('A Solid server with IDP', (): void => { }); it('initializes the session and logs in.', async(): Promise => { - const url = await state.startSession(clientId); + let url = await state.startSession(clientId); const res = await state.fetchIdp(url); expect(res.status).toBe(200); - await state.login(url, email, password); + url = await state.login(url, email, password); + await state.consent(url); expect(state.session.info?.webId).toBe(webId); }); @@ -318,7 +316,8 @@ describe('A Solid server with IDP', (): void => { }); it('can log in with the new password.', async(): Promise => { - await state.login(nextUrl, email, password2); + const url = await state.login(nextUrl, email, password2); + await state.consent(url); expect(state.session.info?.webId).toBe(webId); }); }); @@ -397,10 +396,11 @@ describe('A Solid server with IDP', (): void => { it('initializes the session and logs in.', async(): Promise => { state = new IdentityTestState(baseUrl, redirectUrl, oidcIssuer); - const url = await state.startSession(); + let url = await state.startSession(); const res = await state.fetchIdp(url); expect(res.status).toBe(200); - await state.login(url, newMail, password); + url = await state.login(url, newMail, password); + await state.consent(url); expect(state.session.info?.webId).toBe(newWebId); }); diff --git a/test/integration/IdentityTestState.ts b/test/integration/IdentityTestState.ts index 17f4364bb..836fad1ad 100644 --- a/test/integration/IdentityTestState.ts +++ b/test/integration/IdentityTestState.ts @@ -89,7 +89,7 @@ export class IdentityTestState { // Need to catch the redirect so we can copy the cookies let res = await this.fetchIdp(nextUrl); - expect(res.status).toBe(302); + expect(res.status).toBe(303); nextUrl = res.headers.get('location')!; // Handle redirect @@ -109,22 +109,26 @@ export class IdentityTestState { * Logs in by sending the corresponding email and password to the given form action. * The URL should be extracted from the login page. */ - public async login(url: string, email: string, password: string): Promise { + public async login(url: string, email: string, password: string): Promise { const formData = stringify({ email, password }); - const res = await this.fetchIdp(url, 'POST', formData, APPLICATION_X_WWW_FORM_URLENCODED); + let res = await this.fetchIdp(url, 'POST', formData, APPLICATION_X_WWW_FORM_URLENCODED); expect(res.status).toBe(200); const json = await res.json(); - const nextUrl = json.location; - - return this.handleLoginRedirect(nextUrl); + res = await this.fetchIdp(json.location); + expect(res.status).toBe(303); + return res.headers.get('location')!; } /** - * Handles the redirect that happens after logging in. + * Handles the consent screen at the given URL and the followup redirect back to the client. */ - public async handleLoginRedirect(url: string): Promise { - const res = await this.fetchIdp(url); - expect(res.status).toBe(302); + public async consent(url: string): Promise { + let res = await this.fetchIdp(url, 'POST', '', APPLICATION_X_WWW_FORM_URLENCODED); + expect(res.status).toBe(200); + const json = await res.json(); + + res = await this.fetchIdp(json.location); + expect(res.status).toBe(303); const mockUrl = res.headers.get('location')!; expect(mockUrl.startsWith(this.redirectUrl)).toBeTruthy(); diff --git a/test/integration/RestrictedIdentity.test.ts b/test/integration/RestrictedIdentity.test.ts index c70581a44..6295d937f 100644 --- a/test/integration/RestrictedIdentity.test.ts +++ b/test/integration/RestrictedIdentity.test.ts @@ -94,10 +94,11 @@ describe('A server with restricted IDP access', (): void => { it('can still access registration with the correct credentials.', async(): Promise => { // Logging into session const state = new IdentityTestState(baseUrl, 'http://mockedredirect/', baseUrl); - const url = await state.startSession(); + let url = await state.startSession(); let res = await state.fetchIdp(url); expect(res.status).toBe(200); - await state.login(url, settings.email, settings.password); + url = await state.login(url, settings.email, settings.password); + await state.consent(url); expect(state.session.info?.webId).toBe(webId); // Registration still works for this WebID diff --git a/test/unit/identity/OidcHttpHandler.test.ts b/test/unit/identity/OidcHttpHandler.test.ts index 61be21c83..7ceba4473 100644 --- a/test/unit/identity/OidcHttpHandler.test.ts +++ b/test/unit/identity/OidcHttpHandler.test.ts @@ -13,7 +13,7 @@ describe('An OidcHttpHandler', (): void => { beforeEach(async(): Promise => { provider = { - callback: jest.fn(), + callback: jest.fn().mockReturnValue(jest.fn()), } as any; providerFactory = { @@ -26,6 +26,7 @@ describe('An OidcHttpHandler', (): void => { it('sends all requests to the OIDC library.', async(): Promise => { await expect(handler.handle({ request, response })).resolves.toBeUndefined(); expect(provider.callback).toHaveBeenCalledTimes(1); - expect(provider.callback).toHaveBeenLastCalledWith(request, response); + expect(provider.callback.mock.results[0].value).toHaveBeenCalledTimes(1); + expect(provider.callback.mock.results[0].value).toHaveBeenLastCalledWith(request, response); }); }); diff --git a/test/unit/identity/configuration/IdentityProviderFactory.test.ts b/test/unit/identity/configuration/IdentityProviderFactory.test.ts index bffb4787e..ceffb5a6d 100644 --- a/test/unit/identity/configuration/IdentityProviderFactory.test.ts +++ b/test/unit/identity/configuration/IdentityProviderFactory.test.ts @@ -16,7 +16,7 @@ jest.mock('oidc-provider', (): any => ({ const routes = { authorization: '/foo/oidc/auth', - check_session: '/foo/oidc/session/check', + backchannel_authentication: '/foo/oidc/backchannel', code_verification: '/foo/oidc/device', device_authorization: '/foo/oidc/device/auth', end_session: '/foo/oidc/session/end', @@ -100,23 +100,32 @@ describe('An IdentityProviderFactory', (): void => { expect(adapterFactory.createStorageAdapter).toHaveBeenLastCalledWith('test!'); expect(config.cookies?.keys).toEqual([ expect.any(String) ]); - expect(config.jwks).toEqual({ keys: [ expect.objectContaining({ kty: 'RSA' }) ]}); + expect(config.jwks).toEqual({ keys: [ expect.objectContaining({ alg: 'ES256' }) ]}); expect(config.routes).toEqual(routes); + expect(config.pkce?.methods).toEqual([ 'S256' ]); + expect((config.pkce!.required as any)()).toBe(true); + expect(config.clientDefaults?.id_token_signed_response_alg).toBe('ES256'); await expect((config.interactions?.url as any)(ctx, oidcInteraction)).resolves.toBe(redirectUrl); - expect((config.audiences as any)(null, null, {}, 'access_token')).toBe('solid'); - expect((config.audiences as any)(null, null, { clientId: 'clientId' }, 'client_credentials')).toBe('clientId'); - const findResult = await config.findAccount?.({ oidc: { client: { clientId: 'clientId' }}} as any, webId); + let findResult = await config.findAccount?.({ oidc: { client: { clientId: 'clientId' }}} as any, webId); expect(findResult?.accountId).toBe(webId); + await expect((findResult?.claims as any)()).resolves.toEqual({ sub: webId, webid: webId, azp: 'clientId' }); + findResult = await config.findAccount?.({ oidc: {}} as any, webId); await expect((findResult?.claims as any)()).resolves.toEqual({ sub: webId, webid: webId }); - expect((config.extraAccessTokenClaims as any)({}, {})).toEqual({}); - expect((config.extraAccessTokenClaims as any)({}, { kind: 'AccessToken', accountId: webId, clientId: 'clientId' })) - .toEqual({ - webid: webId, - client_id: 'clientId', - }); + expect((config.extraTokenClaims as any)({}, {})).toEqual({}); + expect((config.extraTokenClaims as any)({}, { kind: 'AccessToken', accountId: webId, clientId: 'clientId' })) + .toEqual({ webid: webId }); + + expect(config.features?.resourceIndicators?.enabled).toBe(true); + expect((config.features?.resourceIndicators?.defaultResource as any)()).toBe('http://example.com/'); + expect((config.features?.resourceIndicators?.getResourceServerInfo as any)()).toEqual({ + scope: '', + audience: 'solid', + accessTokenFormat: 'jwt', + jwt: { sign: { alg: 'ES256' }}, + }); // Test the renderError function const response = { } as HttpResponse; diff --git a/test/unit/identity/interaction/CompletingInteractionHandler.test.ts b/test/unit/identity/interaction/CompletingInteractionHandler.test.ts deleted file mode 100644 index 34a221d4f..000000000 --- a/test/unit/identity/interaction/CompletingInteractionHandler.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { Operation } from '../../../../src/http/Operation'; -import { BasicRepresentation } from '../../../../src/http/representation/BasicRepresentation'; -import { CompletingInteractionHandler } from '../../../../src/identity/interaction/CompletingInteractionHandler'; -import type { - Interaction, - InteractionHandlerInput, -} from '../../../../src/identity/interaction/InteractionHandler'; -import type { - InteractionCompleter, - InteractionCompleterInput, -} from '../../../../src/identity/interaction/util/InteractionCompleter'; -import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; - -const webId = 'http://alice.test.com/card#me'; -class DummyCompletingInteractionHandler extends CompletingInteractionHandler { - public constructor(interactionCompleter: InteractionCompleter) { - super({}, interactionCompleter); - } - - public async getCompletionParameters(input: Required): Promise { - return { webId, oidcInteraction: input.oidcInteraction }; - } -} - -describe('A CompletingInteractionHandler', (): void => { - const oidcInteraction: Interaction = {} as any; - const location = 'http://test.com/redirect'; - let operation: Operation; - let interactionCompleter: jest.Mocked; - let handler: DummyCompletingInteractionHandler; - - beforeEach(async(): Promise => { - const representation = new BasicRepresentation('', 'application/json'); - operation = { - method: 'POST', - body: representation, - } as any; - - interactionCompleter = { - handleSafe: jest.fn().mockResolvedValue(location), - } as any; - - handler = new DummyCompletingInteractionHandler(interactionCompleter); - }); - - it('calls the parent JSON canHandle check.', async(): Promise => { - operation.body.metadata.contentType = 'application/x-www-form-urlencoded'; - await expect(handler.canHandle({ operation, oidcInteraction } as any)).rejects.toThrow(NotImplementedHttpError); - }); - - it('can handle GET requests without interaction.', async(): Promise => { - operation.method = 'GET'; - await expect(handler.canHandle({ operation } as any)).resolves.toBeUndefined(); - }); - - it('errors if no OidcInteraction is defined on POST requests.', async(): Promise => { - const error = expect.objectContaining({ - statusCode: 400, - message: 'This action can only be performed as part of an OIDC authentication flow.', - errorCode: 'E0002', - }); - await expect(handler.canHandle({ operation })).rejects.toThrow(error); - - await expect(handler.canHandle({ operation, oidcInteraction })).resolves.toBeUndefined(); - }); - - it('throws a redirect error with the completer location.', async(): Promise => { - const error = expect.objectContaining({ - statusCode: 302, - location, - }); - await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(error); - expect(interactionCompleter.handleSafe).toHaveBeenCalledTimes(1); - expect(interactionCompleter.handleSafe).toHaveBeenLastCalledWith({ oidcInteraction, webId }); - }); -}); diff --git a/test/unit/identity/interaction/ConsentHandler.test.ts b/test/unit/identity/interaction/ConsentHandler.test.ts new file mode 100644 index 000000000..28cf92890 --- /dev/null +++ b/test/unit/identity/interaction/ConsentHandler.test.ts @@ -0,0 +1,142 @@ +import type { Provider } from 'oidc-provider'; +import type { ProviderFactory } from '../../../../src/identity/configuration/ProviderFactory'; +import { ConsentHandler } from '../../../../src/identity/interaction/ConsentHandler'; +import type { Interaction } from '../../../../src/identity/interaction/InteractionHandler'; +import { FoundHttpError } from '../../../../src/util/errors/FoundHttpError'; +import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; +import { createPostJsonOperation } from './email-password/handler/Util'; + +const newGrantId = 'newGrantId'; +class DummyGrant { + public accountId: string; + public clientId: string; + + public readonly scopes: string[] = []; + public claims: string[] = []; + public readonly rejectedScopes: string[] = []; + public readonly resourceScopes: Record = {}; + + public constructor(props: { accountId: string; clientId: string }) { + this.accountId = props.accountId; + this.clientId = props.clientId; + } + + public rejectOIDCScope(scope: string): void { + this.rejectedScopes.push(scope); + } + + public addOIDCScope(scope: string): void { + this.scopes.push(scope); + } + + public addOIDCClaims(claims: string[]): void { + this.claims = claims; + } + + public addResourceScope(resource: string, scope: string): void { + this.resourceScopes[resource] = scope; + } + + public async save(): Promise { + return newGrantId; + } +} + +describe('A ConsentHandler', (): void => { + const accountId = 'http://example.com/id#me'; + const clientId = 'clientId'; + let grantFn: jest.Mock & { find: jest.Mock }; + let knownGrant: DummyGrant; + let oidcInteraction: Interaction; + let provider: jest.Mocked; + let providerFactory: jest.Mocked; + let handler: ConsentHandler; + + beforeEach(async(): Promise => { + oidcInteraction = { + session: { accountId }, + // eslint-disable-next-line @typescript-eslint/naming-convention + params: { client_id: clientId }, + prompt: { details: {}}, + save: jest.fn(), + } as any; + + knownGrant = new DummyGrant({ accountId, clientId }); + + grantFn = jest.fn((props): DummyGrant => new DummyGrant(props)) as any; + grantFn.find = jest.fn((grantId: string): any => grantId ? knownGrant : undefined); + provider = { + // eslint-disable-next-line @typescript-eslint/naming-convention + Grant: grantFn, + } as any; + + providerFactory = { + getProvider: jest.fn().mockResolvedValue(provider), + }; + + handler = new ConsentHandler(providerFactory); + }); + + it('errors if no oidcInteraction is defined on POST requests.', async(): Promise => { + const error = expect.objectContaining({ + statusCode: 400, + message: 'This action can only be performed as part of an OIDC authentication flow.', + errorCode: 'E0002', + }); + await expect(handler.canHandle({ operation: createPostJsonOperation({}) })).rejects.toThrow(error); + + await expect(handler.canHandle({ operation: createPostJsonOperation({}), oidcInteraction })) + .resolves.toBeUndefined(); + }); + + it('requires an oidcInteraction with a defined session.', async(): Promise => { + oidcInteraction.session = undefined; + await expect(handler.handle({ operation: createPostJsonOperation({}), oidcInteraction })) + .rejects.toThrow(NotImplementedHttpError); + }); + + it('throws a redirect error.', async(): Promise => { + const operation = createPostJsonOperation({}); + await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); + }); + + it('stores the requested scopes and claims in the grant.', async(): Promise => { + oidcInteraction.prompt.details = { + missingOIDCScope: [ 'scope1', 'scope2' ], + missingOIDCClaims: [ 'claim1', 'claim2' ], + missingResourceScopes: { resource: [ 'scope1', 'scope2' ]}, + }; + + const operation = createPostJsonOperation({ remember: true }); + await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); + expect(grantFn.mock.results).toHaveLength(1); + expect(grantFn.mock.results[0].value.scopes).toEqual([ 'scope1 scope2' ]); + expect(grantFn.mock.results[0].value.claims).toEqual([ 'claim1', 'claim2' ]); + expect(grantFn.mock.results[0].value.resourceScopes).toEqual({ resource: 'scope1 scope2' }); + expect(grantFn.mock.results[0].value.rejectedScopes).toEqual([]); + }); + + it('creates a new Grant when needed.', async(): Promise => { + const operation = createPostJsonOperation({}); + await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); + expect(grantFn).toHaveBeenCalledTimes(1); + expect(grantFn).toHaveBeenLastCalledWith({ accountId, clientId }); + expect(grantFn.find).toHaveBeenCalledTimes(0); + }); + + it('reuses existing Grant objects.', async(): Promise => { + const operation = createPostJsonOperation({}); + oidcInteraction.grantId = '123456'; + await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); + expect(grantFn).toHaveBeenCalledTimes(0); + expect(grantFn.find).toHaveBeenCalledTimes(1); + expect(grantFn.find).toHaveBeenLastCalledWith('123456'); + }); + + it('rejectes offline_access as scope if a user does not want to be remembered.', async(): Promise => { + const operation = createPostJsonOperation({}); + await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); + expect(grantFn.mock.results).toHaveLength(1); + expect(grantFn.mock.results[0].value.rejectedScopes).toEqual([ 'offline_access' ]); + }); +}); diff --git a/test/unit/identity/interaction/ExistingLoginHandler.test.ts b/test/unit/identity/interaction/ExistingLoginHandler.test.ts deleted file mode 100644 index c649e0a23..000000000 --- a/test/unit/identity/interaction/ExistingLoginHandler.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ExistingLoginHandler } from '../../../../src/identity/interaction/ExistingLoginHandler'; -import type { Interaction } from '../../../../src/identity/interaction/InteractionHandler'; -import type { - InteractionCompleter, -} from '../../../../src/identity/interaction/util/InteractionCompleter'; -import { FoundHttpError } from '../../../../src/util/errors/FoundHttpError'; -import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; -import { createPostJsonOperation } from './email-password/handler/Util'; - -describe('An ExistingLoginHandler', (): void => { - const webId = 'http://test.com/id#me'; - let oidcInteraction: Interaction; - let interactionCompleter: jest.Mocked; - let handler: ExistingLoginHandler; - - beforeEach(async(): Promise => { - oidcInteraction = { session: { accountId: webId }} as any; - - interactionCompleter = { - handleSafe: jest.fn().mockResolvedValue('http://test.com/redirect'), - } as any; - - handler = new ExistingLoginHandler(interactionCompleter); - }); - - it('requires an oidcInteraction with a defined session.', async(): Promise => { - oidcInteraction.session = undefined; - await expect(handler.handle({ operation: createPostJsonOperation({}), oidcInteraction })) - .rejects.toThrow(NotImplementedHttpError); - }); - - it('returns the correct completion parameters.', async(): Promise => { - const operation = createPostJsonOperation({ remember: true }); - await expect(handler.handle({ operation, oidcInteraction })).rejects.toThrow(FoundHttpError); - expect(interactionCompleter.handleSafe).toHaveBeenCalledTimes(1); - expect(interactionCompleter.handleSafe).toHaveBeenLastCalledWith({ oidcInteraction, webId, shouldRemember: true }); - }); -}); diff --git a/test/unit/identity/interaction/email-password/handler/ForgotPasswordHandler.test.ts b/test/unit/identity/interaction/email-password/handler/ForgotPasswordHandler.test.ts index 92701c0a3..75a5a7640 100644 --- a/test/unit/identity/interaction/email-password/handler/ForgotPasswordHandler.test.ts +++ b/test/unit/identity/interaction/email-password/handler/ForgotPasswordHandler.test.ts @@ -3,8 +3,8 @@ import { ForgotPasswordHandler, } from '../../../../../../src/identity/interaction/email-password/handler/ForgotPasswordHandler'; import type { AccountStore } from '../../../../../../src/identity/interaction/email-password/storage/AccountStore'; +import type { EmailSender } from '../../../../../../src/identity/interaction/email-password/util/EmailSender'; import type { InteractionRoute } from '../../../../../../src/identity/interaction/routing/InteractionRoute'; -import type { EmailSender } from '../../../../../../src/identity/interaction/util/EmailSender'; import { readJsonStream } from '../../../../../../src/util/StreamUtil'; import type { TemplateEngine } from '../../../../../../src/util/templates/TemplateEngine'; import { createPostJsonOperation } from './Util'; diff --git a/test/unit/identity/interaction/email-password/handler/LoginHandler.test.ts b/test/unit/identity/interaction/email-password/handler/LoginHandler.test.ts index c4a9392e7..2ea04b774 100644 --- a/test/unit/identity/interaction/email-password/handler/LoginHandler.test.ts +++ b/test/unit/identity/interaction/email-password/handler/LoginHandler.test.ts @@ -4,22 +4,23 @@ import type { Interaction, InteractionHandlerInput, } from '../../../../../../src/identity/interaction/InteractionHandler'; -import type { - InteractionCompleter, -} from '../../../../../../src/identity/interaction/util/InteractionCompleter'; import { FoundHttpError } from '../../../../../../src/util/errors/FoundHttpError'; import { createPostJsonOperation } from './Util'; describe('A LoginHandler', (): void => { const webId = 'http://alice.test.com/card#me'; const email = 'alice@test.email'; - const oidcInteraction: Interaction = {} as any; + let oidcInteraction: jest.Mocked; let input: Required; let accountStore: jest.Mocked; - let interactionCompleter: jest.Mocked; let handler: LoginHandler; beforeEach(async(): Promise => { + oidcInteraction = { + exp: 123456, + save: jest.fn(), + } as any; + input = { oidcInteraction } as any; accountStore = { @@ -27,11 +28,18 @@ describe('A LoginHandler', (): void => { getSettings: jest.fn().mockResolvedValue({ useIdp: true }), } as any; - interactionCompleter = { - handleSafe: jest.fn().mockResolvedValue('http://test.com/redirect'), - } as any; + handler = new LoginHandler(accountStore); + }); + it('errors if no oidcInteraction is defined on POST requests.', async(): Promise => { + const error = expect.objectContaining({ + statusCode: 400, + message: 'This action can only be performed as part of an OIDC authentication flow.', + errorCode: 'E0002', + }); + await expect(handler.canHandle({ operation: createPostJsonOperation({}) })).rejects.toThrow(error); - handler = new LoginHandler(accountStore, interactionCompleter); + await expect(handler.canHandle({ operation: createPostJsonOperation({}), oidcInteraction })) + .resolves.toBeUndefined(); }); it('errors on invalid emails.', async(): Promise => { @@ -61,13 +69,13 @@ describe('A LoginHandler', (): void => { .rejects.toThrow('This server is not an identity provider for this account.'); }); - it('returns the correct completion parameters.', async(): Promise => { + it('returns the generated redirect URL.', async(): Promise => { input.operation = createPostJsonOperation({ email, password: 'password!' }); await expect(handler.handle(input)).rejects.toThrow(FoundHttpError); expect(accountStore.authenticate).toHaveBeenCalledTimes(1); expect(accountStore.authenticate).toHaveBeenLastCalledWith(email, 'password!'); - expect(interactionCompleter.handleSafe).toHaveBeenCalledTimes(1); - expect(interactionCompleter.handleSafe).toHaveBeenLastCalledWith({ oidcInteraction, webId, shouldRemember: false }); + expect(oidcInteraction.save).toHaveBeenCalledTimes(1); + expect(oidcInteraction.result).toEqual({ login: { accountId: webId, remember: false }}); }); }); diff --git a/test/unit/identity/interaction/util/BaseEmailSender.test.ts b/test/unit/identity/interaction/email-password/util/BaseEmailSender.test.ts similarity index 82% rename from test/unit/identity/interaction/util/BaseEmailSender.test.ts rename to test/unit/identity/interaction/email-password/util/BaseEmailSender.test.ts index a04fa4424..4a2839f29 100644 --- a/test/unit/identity/interaction/util/BaseEmailSender.test.ts +++ b/test/unit/identity/interaction/email-password/util/BaseEmailSender.test.ts @@ -1,6 +1,6 @@ -import type { EmailSenderArgs } from '../../../../../src/identity/interaction/util/BaseEmailSender'; -import { BaseEmailSender } from '../../../../../src/identity/interaction/util/BaseEmailSender'; -import type { EmailArgs } from '../../../../../src/identity/interaction/util/EmailSender'; +import type { EmailSenderArgs } from '../../../../../../src/identity/interaction/email-password/util/BaseEmailSender'; +import { BaseEmailSender } from '../../../../../../src/identity/interaction/email-password/util/BaseEmailSender'; +import type { EmailArgs } from '../../../../../../src/identity/interaction/email-password/util/EmailSender'; jest.mock('nodemailer'); describe('A BaseEmailSender', (): void => { diff --git a/test/unit/identity/interaction/util/BaseInteractionCompleter.test.ts b/test/unit/identity/interaction/util/BaseInteractionCompleter.test.ts deleted file mode 100644 index 4973e6354..000000000 --- a/test/unit/identity/interaction/util/BaseInteractionCompleter.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { Interaction } from '../../../../../src/identity/interaction/InteractionHandler'; -import { BaseInteractionCompleter } from '../../../../../src/identity/interaction/util/BaseInteractionCompleter'; - -jest.useFakeTimers(); - -describe('A BaseInteractionCompleter', (): void => { - const now = Math.floor(Date.now() / 1000); - const webId = 'http://alice.test.com/#me'; - let oidcInteraction: jest.Mocked; - let completer: BaseInteractionCompleter; - - beforeEach(async(): Promise => { - oidcInteraction = { - lastSubmission: {}, - exp: now + 500, - returnTo: 'http://test.com/redirect', - save: jest.fn(), - } as any; - - completer = new BaseInteractionCompleter(); - }); - - it('stores the correct data in the interaction.', async(): Promise => { - await expect(completer.handle({ oidcInteraction, webId, shouldRemember: true })) - .resolves.toBe(oidcInteraction.returnTo); - expect(oidcInteraction.result).toEqual({ - login: { - account: webId, - remember: true, - ts: now, - }, - consent: { - rejectedScopes: [], - }, - }); - expect(oidcInteraction.save).toHaveBeenCalledTimes(1); - expect(oidcInteraction.save).toHaveBeenLastCalledWith(500); - }); - - it('rejects offline access if shouldRemember is false.', async(): Promise => { - await expect(completer.handle({ oidcInteraction, webId, shouldRemember: false })) - .resolves.toBe(oidcInteraction.returnTo); - expect(oidcInteraction.result).toEqual({ - login: { - account: webId, - remember: false, - ts: now, - }, - consent: { - rejectedScopes: [ 'offline_access' ], - }, - }); - expect(oidcInteraction.save).toHaveBeenCalledTimes(1); - expect(oidcInteraction.save).toHaveBeenLastCalledWith(500); - }); -});