diff --git a/.eslintrc.json b/.eslintrc.json index 82bd56b2d..7d35e7657 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,6 +2,14 @@ "env": { "es6": true }, + "overrides": [ + { + "files": ["src/server/middleware/auth/verifyAADToken.js"], + "parserOptions": { + "ecmaVersion": 2022 + } + } + ], "parserOptions": { "ecmaVersion": 2018 }, diff --git a/package-lock.json b/package-lock.json index 07acdb700..01fb9fb90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,14 +10,13 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@azure/msal-node": "^2.9.2", + "@azure/msal-node": "^5.1.5", "@redocly/cli": "^2.30.3", "@socket.io/redis-adapter": "^8.3.0", "adm-zip": "^0.5.10", "agentkeepalive": "^4.5.0", "archiver": "^7", "aws-sdk": "^2.1443.0", - "azure-ad-verify-token-commonjs": "^2.0.2", "bcryptjs": "^3", "body-parser": "^2", "browserify": "^17.0.0", @@ -32,6 +31,7 @@ "ejs": "^5", "express": "^4.18.2", "import-fresh": "^3.3.0", + "jose": "^6.2.3", "jsdoc": "^4.0.2", "jsonwebtoken": "^9.0.2", "method-override": "^3.0.0", @@ -68,26 +68,25 @@ } }, "node_modules/@azure/msal-common": { - "version": "14.16.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.1.tgz", - "integrity": "sha512-nyxsA6NA4SVKh5YyRpbSXiMr7oQbwark7JU9LMeg6tJYTSPyAGkdx61wPT4gyxZfxlSxMMEyAsWaubBlNyIa1w==", + "version": "16.5.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.5.2.tgz", + "integrity": "sha512-GkDEL6TYo3HgT3UuqakdgE9PZfc1hMki6+Hwgy1uddb/EauvAKfu85vVhuofRSo22D1xTnWt8Ucwfg4vSCVwvA==", "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.3.tgz", - "integrity": "sha512-CO+SE4weOsfJf+C5LM8argzvotrXw252/ZU6SM2Tz63fEblhH1uuVaaO4ISYFuN4Q6BhTo7I3qIdi8ydUQCqhw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.5.tgz", + "integrity": "sha512-ObTeMoNPmq19X3z40et9Xvs4ZoWVeJg43PZMRLG5iwVL+2nCtAerG3YTDItqPp1CfXNwmCXBbg8jn1DOx65c3g==", "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.1", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" + "@azure/msal-common": "16.5.2", + "jsonwebtoken": "^9.0.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@babel/code-frame": { @@ -1671,72 +1670,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/azure-ad-verify-token-commonjs": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/azure-ad-verify-token-commonjs/-/azure-ad-verify-token-commonjs-2.0.2.tgz", - "integrity": "sha512-UAaZkQ+CW8vVDJBEgrEICNw+GkT4mjj0QC9du7KsVwi1C/odomClWCeeCO2NB/JeyM5UkOr1KTecAoGUOtT52g==", - "license": "MIT", - "dependencies": { - "jsonwebtoken": "^8.5.1", - "node-fetch": "^2.6.5", - "rsa-pem-from-mod-exp": "^0.8.4" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/azure-ad-verify-token-commonjs/node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/azure-ad-verify-token-commonjs/node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/azure-ad-verify-token-commonjs/node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.2", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/azure-ad-verify-token-commonjs/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/b4a": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", @@ -4996,6 +4929,15 @@ "node": ">= 0.6.0" } }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -7538,12 +7480,6 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, - "node_modules/rsa-pem-from-mod-exp": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.6.tgz", - "integrity": "sha512-c5ouQkOvGHF1qomUUDJGFcXsomeSO2gbEs6hVhMAtlkE1CuaZase/WzoaKFG/EZQuNmq6pw/EMCeEnDvOgCJYQ==", - "license": "MIT" - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8996,16 +8932,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 7614e462f..eaa877118 100644 --- a/package.json +++ b/package.json @@ -44,14 +44,13 @@ }, "homepage": "https://github.com/webgme/webgme-engine#README.md", "dependencies": { - "@azure/msal-node": "^2.9.2", + "@azure/msal-node": "^5.1.5", "@redocly/cli": "^2.30.3", "@socket.io/redis-adapter": "^8.3.0", "adm-zip": "^0.5.10", "agentkeepalive": "^4.5.0", "archiver": "^7", "aws-sdk": "^2.1443.0", - "azure-ad-verify-token-commonjs": "^2.0.2", "bcryptjs": "^3", "body-parser": "^2", "browserify": "^17.0.0", @@ -66,6 +65,7 @@ "ejs": "^5", "express": "^4.18.2", "import-fresh": "^3.3.0", + "jose": "^6.2.3", "jsdoc": "^4.0.2", "jsonwebtoken": "^9.0.2", "method-override": "^3.0.0", diff --git a/src/server/middleware/auth/WebgmeAADClient.js b/src/server/middleware/auth/WebgmeAADClient.js index 8057fdddd..a5719536b 100644 --- a/src/server/middleware/auth/WebgmeAADClient.js +++ b/src/server/middleware/auth/WebgmeAADClient.js @@ -5,7 +5,7 @@ const msal = require('@azure/msal-node'); const jwt = require('jsonwebtoken'); const GUID = requireJS('common/util/guid'); const Q = require('q'); -const aadVerify = require('azure-ad-verify-token-commonjs').verify; +const aadVerify = require('./verifyAADToken').verify; class WebGMEAADClient { constructor(gmeConfig, gmeAuth, logger) { diff --git a/src/server/middleware/auth/verifyAADToken.js b/src/server/middleware/auth/verifyAADToken.js new file mode 100644 index 000000000..09d92e672 --- /dev/null +++ b/src/server/middleware/auth/verifyAADToken.js @@ -0,0 +1,42 @@ +/*eslint-env node*/ +'use strict'; + +const {URL} = require('url'); + +let joseModulePromise = null; +const jwksByUri = new Map(); + +function getJoseModule() { + if (!joseModulePromise) { + joseModulePromise = import('jose'); + } + + return joseModulePromise; +} + +async function getJwks(jwksUri) { + const key = String(jwksUri); + + if (!jwksByUri.has(key)) { + const jose = await getJoseModule(); + jwksByUri.set(key, jose.createRemoteJWKSet(new URL(key))); + } + + return jwksByUri.get(key); +} + +async function verify(token, options) { + const jose = await getJoseModule(); + const jwks = await getJwks(options.jwksUri); + const result = await jose.jwtVerify(token, jwks, { + issuer: options.issuer, + audience: options.audience, + algorithms: ['RS256'], + }); + + return result.payload; +} + +module.exports = { + verify, +}; diff --git a/src/server/standalone.js b/src/server/standalone.js index 96d867fef..da9d55693 100644 --- a/src/server/standalone.js +++ b/src/server/standalone.js @@ -39,6 +39,7 @@ const getClientConfig = require('../../config/getclientconfig'); const GmeAuth = require('./middleware/auth/gmeauth'); const Logger = require('./logger'); const AADClient = require('./middleware/auth/WebgmeAADClient'); +const aadVerify = require('./middleware/auth/verifyAADToken'); const AddOnEventPropagator = require('../addon/addoneventpropagator'); const webgmeUtils = require('../utils'); @@ -690,7 +691,6 @@ class StandAloneServer { //device access to use webgme related services - only available when access Scope is used if (__gmeConfig.authentication.azureActiveDirectory.accessScope) { - const aadVerify = require('azure-ad-verify-token-commonjs'); const verify = aadVerify.verify; const voptions = { jwksUri: __gmeConfig.authentication.azureActiveDirectory.jwksUri,