diff --git a/client/package-lock.json b/client/package-lock.json index 63bb66c1..4e3b67db 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -18,8 +18,8 @@ "js-cookie": "^3.0.1", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", + "nanoid": "^5.0.2", "node-sass": "^8.0.0", - "oidc-client-ts": "^2.3.0", "photoswipe": "^5.3.3", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -7190,11 +7190,6 @@ "node": ">= 8" } }, - "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" - }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -16413,14 +16408,20 @@ "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.2.tgz", + "integrity": "sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -17041,18 +17042,6 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, - "node_modules/oidc-client-ts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.3.0.tgz", - "integrity": "sha512-7RUKU+TJFQo+4X9R50IGJAIDF18uRBaFXyZn4VVCfwmwbSUhKcdDnw4zgeut3uEXkiD3NqURq+d88sDPxjf1FA==", - "dependencies": { - "crypto-js": "^4.1.1", - "jwt-decode": "^3.1.2" - }, - "engines": { - "node": ">=12.13.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -18676,6 +18665,23 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -28785,11 +28791,6 @@ "which": "^2.0.1" } }, - "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" - }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -35494,9 +35495,9 @@ "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.2.tgz", + "integrity": "sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==" }, "natural-compare": { "version": "1.4.0", @@ -35956,15 +35957,6 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, - "oidc-client-ts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.3.0.tgz", - "integrity": "sha512-7RUKU+TJFQo+4X9R50IGJAIDF18uRBaFXyZn4VVCfwmwbSUhKcdDnw4zgeut3uEXkiD3NqURq+d88sDPxjf1FA==", - "requires": { - "crypto-js": "^4.1.1", - "jwt-decode": "^3.1.2" - } - }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -36284,6 +36276,13 @@ "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" + }, + "dependencies": { + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + } } }, "postcss-attribute-case-insensitive": { diff --git a/client/package.json b/client/package.json index 1a970362..7d247abc 100755 --- a/client/package.json +++ b/client/package.json @@ -65,8 +65,8 @@ "js-cookie": "^3.0.1", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", + "nanoid": "^5.0.2", "node-sass": "^8.0.0", - "oidc-client-ts": "^2.3.0", "photoswipe": "^5.3.3", "prop-types": "^15.8.1", "react": "^18.2.0", diff --git a/client/src/sagas/core/services/core.js b/client/src/sagas/core/services/core.js index 30389d6c..2d7c96db 100644 --- a/client/src/sagas/core/services/core.js +++ b/client/src/sagas/core/services/core.js @@ -1,4 +1,4 @@ -import { apply, call, put, select, take } from 'redux-saga/effects'; +import { call, put, select, take } from 'redux-saga/effects'; import request from '../request'; import requests from '../requests'; @@ -6,7 +6,6 @@ import selectors from '../../../selectors'; import actions from '../../../actions'; import api from '../../../api'; import i18n from '../../../i18n'; -import { createOidcManager } from '../../../utils/oidc-manager'; import { removeAccessToken } from '../../../utils/access-token-storage'; export function* initializeCore() { @@ -86,15 +85,13 @@ export function* logout(invalidateAccessToken = true) { const oidcConfig = yield select(selectors.selectOidcConfig); - if (oidcConfig) { - const oidcManager = createOidcManager(oidcConfig); + yield put(actions.logout()); - try { - yield apply(oidcManager, oidcManager.logout); - } catch (error) {} // eslint-disable-line no-empty + if (oidcConfig && oidcConfig.endSessionUrl !== null) { + // Redirect the user to the IDP to log out. + window.location.replace(oidcConfig.endSessionUrl); } - yield put(actions.logout()); yield take(); } diff --git a/client/src/sagas/login/services/login.js b/client/src/sagas/login/services/login.js index 04c029ad..74574f18 100644 --- a/client/src/sagas/login/services/login.js +++ b/client/src/sagas/login/services/login.js @@ -1,10 +1,10 @@ -import { apply, call, put, select } from 'redux-saga/effects'; +import { nanoid } from 'nanoid'; +import { call, put, select } from 'redux-saga/effects'; import { replace } from '../../../lib/redux-router'; import selectors from '../../../selectors'; import actions from '../../../actions'; import api from '../../../api'; -import { createOidcManager } from '../../../utils/oidc-manager'; import { setAccessToken } from '../../../utils/access-token-storage'; import Paths from '../../../constants/Paths'; @@ -31,29 +31,53 @@ export function* authenticate(data) { export function* authenticateWithOidc() { const oidcConfig = yield select(selectors.selectOidcConfig); - const oidcManager = createOidcManager(oidcConfig); - yield apply(oidcManager, oidcManager.login); + const nonce = nanoid(); + window.sessionStorage.setItem('oidc-nonce', nonce); + window.location.replace(`${oidcConfig.authorizationUrl}&nonce=${encodeURIComponent(nonce)}`); } export function* authenticateWithOidcCallback() { - const oidcConfig = yield select(selectors.selectOidcConfig); - const oidcManager = createOidcManager(oidcConfig); - - let oidcToken; - try { - ({ access_token: oidcToken } = yield apply(oidcManager, oidcManager.loginCallback)); - } catch (error) { - yield put(actions.authenticateWithOidc.failure(error)); + const params = new URLSearchParams(window.location.hash.substring(1)); + if (params.get('error') !== null) { + yield put( + actions.authenticateWithOidc.failure( + new Error( + `OIDC Authorization error: ${params.get('error')}: ${params.get('error_description')}`, + ), + ), + ); + return; } + const nonce = window.sessionStorage.getItem('oidc-nonce'); + if (nonce === null) { + yield put( + actions.authenticateWithOidc.failure( + new Error('Unable to process OIDC response: no nonce issued'), + ), + ); + return; + } + + const code = params.get('code'); + if (code === null) { + yield put( + actions.authenticateWithOidc.failure(new Error('Invalid OIDC response: no code parameter')), + ); + return; + } + + window.sessionStorage.removeItem('oidc-nonce'); + yield put(replace(Paths.LOGIN)); - if (oidcToken) { + if (code !== null) { let accessToken; try { ({ item: accessToken } = yield call(api.exchangeToAccessToken, { - token: oidcToken, + code, + nonce, })); } catch (error) { yield put(actions.authenticateWithOidc.failure(error)); diff --git a/client/src/utils/oidc-manager.js b/client/src/utils/oidc-manager.js deleted file mode 100644 index c7812052..00000000 --- a/client/src/utils/oidc-manager.js +++ /dev/null @@ -1,27 +0,0 @@ -import { UserManager } from 'oidc-client-ts'; - -export default class OidcManager { - constructor(config) { - this.userManager = new UserManager({ - ...config, - authority: config.issuer, - client_id: config.clientId, - redirect_uri: config.redirectUri, - scope: config.scopes, - }); - } - - login() { - return this.userManager.signinRedirect(); - } - - loginCallback() { - return this.userManager.signinRedirectCallback(); - } - - logout() { - return this.userManager.signoutSilent(); - } -} - -export const createOidcManager = (config) => new OidcManager(config); diff --git a/docker-compose.yml b/docker-compose.yml index e0d030a5..0559fc42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,13 +40,10 @@ services: # - OIDC_ISSUER= # - OIDC_CLIENT_ID= - # - OIDC_REDIRECT_URI=http://localhost:3000/oidc-callback + # - OIDC_CLIENT_SECRET= # - OIDC_SCOPES=openid email profile - # - OIDC_JWKS_URI= - # - OIDC_AUDIENCE= # - OIDC_ADMIN_ROLES=admin # - OIDC_ROLES_ATTRIBUTE=groups - # - OIDC_SKIP_USER_INFO=true depends_on: - postgres diff --git a/server/.env.sample b/server/.env.sample index 76b30480..2f61e7ce 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -26,13 +26,10 @@ DEFAULT_ADMIN_USERNAME=demo # OIDC_ISSUER= # OIDC_CLIENT_ID= -# OIDC_REDIRECT_URI=http://localhost:1337/oidc-callback +# OIDC_CLIENT_SECRET= # OIDC_SCOPES=openid email profile -# OIDC_JWKS_URI= -# OIDC_AUDIENCE= # OIDC_ADMIN_ROLES=admin # OIDC_ROLES_ATTRIBUTE=groups -# OIDC_SKIP_USER_INFO=true ## Do not edit this diff --git a/server/api/controllers/access-tokens/exchange.js b/server/api/controllers/access-tokens/exchange.js index bd77c2cb..582cd296 100644 --- a/server/api/controllers/access-tokens/exchange.js +++ b/server/api/controllers/access-tokens/exchange.js @@ -17,7 +17,11 @@ const Errors = { module.exports = { inputs: { - token: { + code: { + type: 'string', + required: true, + }, + nonce: { type: 'string', required: true, }, @@ -42,7 +46,7 @@ module.exports = { const remoteAddress = getRemoteAddress(this.req); const user = await sails.helpers.users - .getOrCreateOneByOidcToken(inputs.token) + .getOrCreateOneByOidcToken(inputs.code, inputs.nonce) .intercept('invalidToken', () => { sails.log.warn(`Invalid token! (IP: ${remoteAddress})`); return Errors.INVALID_TOKEN; diff --git a/server/api/controllers/show-config.js b/server/api/controllers/show-config.js index 0273f93b..e41bb0fb 100644 --- a/server/api/controllers/show-config.js +++ b/server/api/controllers/show-config.js @@ -1,15 +1,20 @@ module.exports = { fn() { + const oidcClient = sails.hooks.oidc.isActive() ? sails.hooks.oidc.getClient() : null; return { item: { - oidc: sails.config.custom.oidcIssuer - ? { - issuer: sails.config.custom.oidcIssuer, - clientId: sails.config.custom.oidcClientId, - redirectUri: sails.config.custom.oidcRedirectUri, - scopes: sails.config.custom.oidcScopes, - } - : null, + oidc: + sails.config.custom.oidcIssuer !== '' + ? { + authorizationUrl: oidcClient.authorizationUrl({ + scope: sails.config.custom.oidcScopes, + response_mode: 'fragment', + }), + endSessionUrl: oidcClient.issuer.end_session_endpoint + ? oidcClient.endSessionUrl({}) + : null, + } + : null, }, }; }, diff --git a/server/api/helpers/users/get-or-create-one-by-oidc-token.js b/server/api/helpers/users/get-or-create-one-by-oidc-token.js index c9e38e07..50588dba 100644 --- a/server/api/helpers/users/get-or-create-one-by-oidc-token.js +++ b/server/api/helpers/users/get-or-create-one-by-oidc-token.js @@ -1,50 +1,10 @@ -const jwt = require('jsonwebtoken'); -const jwksClient = require('jwks-rsa'); -const openidClient = require('openid-client'); - -const jwks = jwksClient({ - jwksUri: sails.config.custom.oidcJwksUri, -}); - -const verifyOidcToken = async (oidcToken) => { - const signingKeys = await jwks.getSigningKeys(); - - const options = { - issuer: sails.config.custom.oidcIssuer, - }; - - if (sails.config.custom.oidcAudience) { - options.audience = sails.config.custom.oidcAudience; - } - - let payload = null; - signingKeys.some((signingKey) => { - try { - const publicKey = signingKey.getPublicKey(); - payload = jwt.verify(oidcToken, publicKey, options); - } catch (error) { - sails.log.error(error); - } - - return !!payload; - }); - - return payload; -}; - -const getUserInfo = async (oidcToken) => { - const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); - - const client = new issuer.Client({ - client_id: 'irrelevant', - }); - - return client.userinfo(oidcToken); -}; - module.exports = { inputs: { - token: { + code: { + type: 'string', + required: true, + }, + nonce: { type: 'string', required: true, }, @@ -58,18 +18,22 @@ module.exports = { }, async fn(inputs) { - const oidcUser = await verifyOidcToken(inputs.token); + const client = sails.hooks.oidc.getClient(); - if (!oidcUser) { + let userInfo; + try { + const tokenSet = await client.callback( + `${sails.config.custom.baseUrl}/oidc-callback`, + { code: inputs.code }, + { nonce: inputs.nonce }, + ); + userInfo = await client.userinfo(tokenSet); + } catch (e) { + sails.log.warn(`Error while exchanging OIDC code: ${e}`); throw 'invalidToken'; } - if (!sails.config.custom.oidcSkipUserInfo) { - const userInfo = await getUserInfo(inputs.token); - Object.assign(oidcUser, userInfo); - } - - if (!oidcUser.email || !oidcUser.name) { + if (!userInfo.email || !userInfo.name) { throw 'missingValues'; } @@ -77,75 +41,62 @@ module.exports = { if (sails.config.custom.oidcAdminRoles.includes('*')) { isAdmin = true; } else { - const roles = oidcUser[sails.config.custom.oidcRolesAttribute]; - + const roles = userInfo[sails.config.custom.oidcRolesAttribute]; if (Array.isArray(roles)) { - isAdmin = sails.config.custom.oidcAdminRoles.some((role) => roles.includes(role)); + // Use a Set here to avoid quadratic time complexity + const userRoles = new Set(userInfo[sails.config.custom.oidcRolesAttribute]); + isAdmin = sails.config.custom.oidcAdminRoles.findIndex((role) => userRoles.has(role)) > -1; } } const values = { isAdmin, - email: oidcUser.email, + email: userInfo.email, isSso: true, - name: oidcUser.name, - username: oidcUser.preferred_username, + name: userInfo.name, + username: userInfo.preferred_username, subscribeToOwnCards: false, }; let user; - /* eslint-disable no-constant-condition, no-await-in-loop */ - while (true) { - let identityProviderUser = await IdentityProviderUser.findOne({ - issuer: oidcUser.iss, - sub: oidcUser.sub, + // This whole block technically needs to be executed in a transaction + // with SERIALIZABLE isolation level (but Waterline does not support + // that), so this will result in errors if for example users are deleted + // concurrently with logging in via OIDC. + let identityProviderUser = await IdentityProviderUser.findOne({ + issuer: sails.config.custom.oidcIssuer, + sub: userInfo.sub, + }); + + if (identityProviderUser) { + user = await sails.helpers.users.getOne(identityProviderUser.userId); + } else { + // If no IDP/User mapping exists, search for the user by email. + user = await sails.helpers.users.getOne({ + email: values.email, }); - if (identityProviderUser) { - user = await sails.helpers.users.getOne(identityProviderUser.userId); - } else { - while (true) { - user = await sails.helpers.users.getOne({ - email: values.email, - }); - - if (!user) { - user = await sails.helpers.users - .createOne(values) - .tolerate('emailAlreadyInUse') - .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); - } - - if (user) { - break; - } - } - - identityProviderUser = await IdentityProviderUser.create({ - userId: user.id, - issuer: oidcUser.iss, - sub: oidcUser.sub, - }).tolerate('E_UNIQUE'); + // Otherwise, create a new user. + if (!user) { + user = await sails.helpers.users + .createOne(values) + .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); } - if (identityProviderUser) { - break; - } + identityProviderUser = await IdentityProviderUser.create({ + userId: user.id, + issuer: sails.config.custom.oidcIssuer, + sub: userInfo.sub, + }); } - /* eslint-enable no-constant-condition, no-await-in-loop */ const updateFieldKeys = ['email', 'isAdmin', 'isSso', 'name', 'username']; - const updateValues = updateFieldKeys.reduce((result, fieldKey) => { - if (values[fieldKey] === user[fieldKey]) { - return result; - } - - return { - ...result, - [fieldKey]: values[fieldKey], - }; - }, {}); + const updateValues = {}; + // eslint-disable-next-line no-restricted-syntax + for (const k of updateFieldKeys) { + if (values[k] !== user[k]) updateValues[k] = values[k]; + } if (Object.keys(updateValues).length > 0) { user = await sails.helpers.users diff --git a/server/api/hooks/oidc/index.js b/server/api/hooks/oidc/index.js new file mode 100644 index 00000000..9ed9b0a9 --- /dev/null +++ b/server/api/hooks/oidc/index.js @@ -0,0 +1,33 @@ +const openidClient = require('openid-client'); + +module.exports = function oidcServiceHook(sails) { + let client = null; + + return { + /** + * Runs when this Sails app loads/lifts. + */ + + async initialize() { + if (sails.config.custom.oidcIssuer) { + const issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer); + + client = new issuer.Client({ + client_id: sails.config.custom.oidcClientId, + client_secret: sails.config.custom.oidcClientSecret, + redirect_uris: [`${sails.config.custom.baseUrl}/oidc-callback`], + response_types: ['code'], + }); + sails.log.info('OIDC hook has been loaded successfully'); + } + }, + + getClient() { + return client; + }, + + isActive() { + return client !== null; + }, + }; +}; diff --git a/server/config/custom.js b/server/config/custom.js index 53f48b5c..24a9e5b8 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -35,11 +35,8 @@ module.exports.custom = { oidcIssuer: process.env.OIDC_ISSUER, oidcClientId: process.env.OIDC_CLIENT_ID, - oidcRedirectUri: process.env.OIDC_REDIRECT_URI, + oidcClientSecret: process.env.OIDC_CLIENT_SECRET, oidcScopes: process.env.OIDC_SCOPES || 'openid email profile', - oidcJwksUri: process.env.OIDC_JWKS_URI, - oidcAudience: process.env.OIDC_AUDIENCE, oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [], oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups', - oidcSkipUserInfo: process.env.OIDC_SKIP_USER_INFO === 'true', }; diff --git a/server/package-lock.json b/server/package-lock.json index f83bd094..e84318f4 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,7 +11,6 @@ "dotenv-cli": "^6.0.0", "filenamify": "^4.3.0", "jsonwebtoken": "^9.0.0", - "jwks-rsa": "^3.0.1", "knex": "^2.3.0", "lodash": "^4.17.21", "moment": "^2.29.4", @@ -200,103 +199,12 @@ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "node_modules/@types/node": { - "version": "20.4.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", - "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==" - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3821,22 +3729,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jwks-rsa": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.0.1.tgz", - "integrity": "sha512-UUOZ0CVReK1QVU3rbi9bC7N5/le8ziUj0A2ef1Q0M7OPD2KvjEYizptqIxGIo6fSLYDkqBrazILS18tYuRc8gw==", - "dependencies": { - "@types/express": "^4.17.14", - "@types/jsonwebtoken": "^9.0.0", - "debug": "^4.3.4", - "jose": "^4.10.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.4" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", @@ -3967,11 +3859,6 @@ "node": ">= 0.10" } }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, "node_modules/localforage": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.3.0.tgz", @@ -4000,11 +3887,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, "node_modules/lodash.issafeinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.issafeinteger/-/lodash.issafeinteger-4.0.4.tgz", @@ -4064,29 +3946,6 @@ "node": ">=10" } }, - "node_modules/lru-memoizer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", - "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - } - }, - "node_modules/lru-memoizer/node_modules/lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", - "dependencies": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "node_modules/lru-memoizer/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, "node_modules/machine": { "version": "15.2.2", "resolved": "https://registry.npmjs.org/machine/-/machine-15.2.2.tgz", @@ -8261,103 +8120,12 @@ } } }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "requires": { - "@types/node": "*" - } - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "@types/node": { - "version": "20.4.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", - "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "requires": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -11092,19 +10860,6 @@ "safe-buffer": "^5.0.1" } }, - "jwks-rsa": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.0.1.tgz", - "integrity": "sha512-UUOZ0CVReK1QVU3rbi9bC7N5/le8ziUj0A2ef1Q0M7OPD2KvjEYizptqIxGIo6fSLYDkqBrazILS18tYuRc8gw==", - "requires": { - "@types/express": "^4.17.14", - "@types/jsonwebtoken": "^9.0.0", - "debug": "^4.3.4", - "jose": "^4.10.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.4" - } - }, "jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", @@ -11195,11 +10950,6 @@ } } }, - "limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, "localforage": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.3.0.tgz", @@ -11222,11 +10972,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, "lodash.issafeinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.issafeinteger/-/lodash.issafeinteger-4.0.4.tgz", @@ -11277,31 +11022,6 @@ "yallist": "^4.0.0" } }, - "lru-memoizer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", - "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", - "requires": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", - "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - } - } - }, "machine": { "version": "15.2.2", "resolved": "https://registry.npmjs.org/machine/-/machine-15.2.2.tgz", diff --git a/server/package.json b/server/package.json index 62b9754e..d5036942 100644 --- a/server/package.json +++ b/server/package.json @@ -32,7 +32,6 @@ "dotenv-cli": "^6.0.0", "filenamify": "^4.3.0", "jsonwebtoken": "^9.0.0", - "jwks-rsa": "^3.0.1", "knex": "^2.3.0", "lodash": "^4.17.21", "moment": "^2.29.4",