From 8667d5341c50515e07cacf9e993c7f1a7fede875 Mon Sep 17 00:00:00 2001 From: NextFire Date: Tue, 9 Apr 2024 20:01:42 -0400 Subject: [PATCH] readd wildcard support --- package-lock.json | 96 +++++++++++++++++++++++++--------------- package.json | 5 ++- server/utils/provider.ts | 58 +++++++++++++++++++++++- 3 files changed, 121 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef2fda0..a1be660 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,14 @@ "@discordjs/rest": "2.3.0", "@types/lodash-es": "4.17.12", "@types/oidc-provider": "8.5.2", + "@types/psl": "1.1.3", "discord-api-types": "0.37.97", "ioredis": "5.4.1", "lodash-es": "4.17.21", "nitropack": "2.9.7", - "oidc-provider": "8.5.1" + "oidc-provider": "8.5.1", + "psl": "1.9.0", + "wildcard": "2.0.1" } }, "node_modules/@cloudflare/kv-asset-handler": { @@ -1783,9 +1786,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.1.tgz", - "integrity": "sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==", + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", "dev": true, "license": "MIT", "dependencies": { @@ -1803,6 +1806,13 @@ "@types/node": "*" } }, + "node_modules/@types/psl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/psl/-/psl-1.1.3.tgz", + "integrity": "sha512-Iu174JHfLd7i/XkXY6VDrqSlPvTDQOtQI7wNAXKKOAADJ9TduRLkNdMgjGiMxSttUIZnomv81JAbAbC0DhggxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", @@ -1935,9 +1945,9 @@ } }, "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.5.tgz", - "integrity": "sha512-J7T3gUr3Wz0l7Ni1f9upgBZ7+J22/Q1B7dl0X6fG+fTsD+H+31DIosMHj4Um1dWQwqbcQ3oQf+YS2foYkDc9cQ==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==", "dev": true, "license": "MIT", "engines": { @@ -2195,9 +2205,9 @@ "license": "Python-2.0" }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, @@ -3888,9 +3898,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4100,9 +4110,9 @@ } }, "node_modules/jose": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.6.3.tgz", - "integrity": "sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.7.0.tgz", + "integrity": "sha512-3P9qfTYDVnNn642LCAqIKbTGb9a1TBxZ9ti5zEVEr48aDdflgRjhspWFb6WM4PzAfFbGMJYC4+803v8riCRAKw==", "dev": true, "license": "MIT", "funding": { @@ -4518,9 +4528,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -5284,9 +5294,9 @@ } }, "node_modules/pkg-types": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", - "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", "dev": true, "license": "MIT", "dependencies": { @@ -5325,6 +5335,13 @@ "dev": true, "license": "MIT" }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5977,9 +5994,9 @@ "license": "MIT" }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.19.0.tgz", + "integrity": "sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg==", "dev": true, "license": "MIT", "dependencies": { @@ -6214,9 +6231,9 @@ "license": "MIT" }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "dev": true, "license": "0BSD" }, @@ -6305,9 +6322,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz", - "integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, "license": "MIT" }, @@ -6352,9 +6369,9 @@ } }, "node_modules/unimport": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.10.0.tgz", - "integrity": "sha512-/UvKRfWx3mNDWwWQhR62HsoM3wxHwYdTq8ellZzMOHnnw4Dp8tovgthyW7DjTrbjDL+i4idOp06voz2VKlvrLw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.11.1.tgz", + "integrity": "sha512-DuB1Uoq01LrrXTScxnwOoMSlTXxyKcULguFxbLrMDFcE/CO0ZWHpEiyhovN0mycPt7K6luAHe8laqvwvuoeUPg==", "dev": true, "license": "MIT", "dependencies": { @@ -6367,10 +6384,10 @@ "magic-string": "^0.30.11", "mlly": "^1.7.1", "pathe": "^1.1.2", - "pkg-types": "^1.1.3", + "pkg-types": "^1.2.0", "scule": "^1.3.0", "strip-literal": "^2.1.0", - "unplugin": "^1.12.0" + "unplugin": "^1.12.2" } }, "node_modules/unimport/node_modules/estree-walker": { @@ -6616,6 +6633,13 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index c02db58..b4af6fb 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,13 @@ "@discordjs/rest": "2.3.0", "@types/lodash-es": "4.17.12", "@types/oidc-provider": "8.5.2", + "@types/psl": "1.1.3", "discord-api-types": "0.37.97", "ioredis": "5.4.1", "lodash-es": "4.17.21", "nitropack": "2.9.7", - "oidc-provider": "8.5.1" + "oidc-provider": "8.5.1", + "psl": "1.9.0", + "wildcard": "2.0.1" } } diff --git a/server/utils/provider.ts b/server/utils/provider.ts index 0704295..fb7d45b 100644 --- a/server/utils/provider.ts +++ b/server/utils/provider.ts @@ -1,4 +1,7 @@ -import Provider, { type Configuration } from "oidc-provider"; +import { URL } from "node:url"; +import Provider, { errors, type Configuration } from "oidc-provider"; +import psl from "psl"; +import wildcard from "wildcard"; const config: Configuration = { adapter: RedisAdapter, @@ -47,6 +50,42 @@ const config: Configuration = { expiresWithSession: () => false, + extraClientMetadata: { + // https://github.com/panva/node-oidc-provider/blob/87cd3c5c335cb30074612b405bd581c6bc76a98d/recipes/redirect_uri_wildcards.md + properties: ["redirect_uris"], + validator: (ctx, key, value: string[], metadata) => { + if (key === "redirect_uris") { + for (const redirectUri of value) { + if (redirectUri.includes("*")) { + const { hostname, href } = new URL(redirectUri); + if (href.split("*").length !== 2) { + throw new errors.InvalidClientMetadata( + "redirect_uris with a wildcard may only contain a single one" + ); + } + if (!hostname.includes("*")) { + throw new errors.InvalidClientMetadata( + "redirect_uris may only have a wildcard in the hostname" + ); + } + const test = hostname.replace("*", "test"); + // checks that the wildcard is for a full subdomain e.g. *.panva.cz, not *suffix.panva.cz + if (!wildcard(hostname, test)) { + throw new errors.InvalidClientMetadata( + "redirect_uris with a wildcard must only match the whole subdomain" + ); + } + if (!psl.get(hostname.split("*.")[1])) { + throw new errors.InvalidClientMetadata( + "redirect_uris with a wildcard must not match an eTLD+1 of a known public suffix domain" + ); + } + } + } + } + }, + }, + pkce: { required: () => false, }, @@ -98,3 +137,20 @@ const config: Configuration = { export const provider = new Provider(userConfig.publicUrl, config); provider.proxy = true; + +// https://github.com/panva/node-oidc-provider/blob/87cd3c5c335cb30074612b405bd581c6bc76a98d/recipes/redirect_uri_wildcards.md +// redirectUriAllowed on a client prototype checks whether a redirect_uri is allowed or not +const { redirectUriAllowed } = provider.Client.prototype; +const hasWildcardHost = (redirectUri: string) => { + const { hostname } = new URL(redirectUri); + return hostname.includes("*"); +}; +const wildcardMatches = (redirectUri: string, wildcardUri: string) => + !!wildcard(wildcardUri, redirectUri); +provider.Client.prototype.redirectUriAllowed = function (redirectUri) { + if (!redirectUri.includes("*")) { + return redirectUriAllowed.call(this, redirectUri); + } + const wildcardUris = this.redirectUris.filter(hasWildcardHost); + return wildcardUris.some(wildcardMatches.bind(undefined, redirectUri)); +};