diff --git a/api/src/auth/router.ts b/api/src/auth/router.ts index f638a4ba..404f8d05 100644 --- a/api/src/auth/router.ts +++ b/api/src/auth/router.ts @@ -5,7 +5,7 @@ import { reqUser, reqIp, reqSiteUrl, reqUserAuthenticated, session, httpError, r import bodyParser from 'body-parser' import Cookies from 'cookies' import Debug from 'debug' -import { sendMailI18n, postUserIdentityWebhook, getOidcProviderId, oauthGlobalProviders, initOidcProvider, getOAuthProviderById, getOAuthProviderByState, reqSite, getSiteByUrl, check2FASession, is2FAValid, cookie2FAName, getTokenPayload, prepareCallbackUrl, signToken, decodeToken, setSessionCookies, getDefaultUserOrg, logout, keepalive, logoutOAuthToken, readOAuthToken, writeOAuthToken, authProviderMemberInfo, patchCoreAuthUser, saml2ServiceProvider, initServerSession, getSamlProviderById, authProviderLoginCallback, getDefaultLoginRedirect } from '#services' +import { sendMailI18n, postUserIdentityWebhook, getOAuthProviderById, getOAuthProviderByState, reqSite, getSiteByUrl, check2FASession, is2FAValid, cookie2FAName, getTokenPayload, prepareCallbackUrl, signToken, decodeToken, setSessionCookies, getDefaultUserOrg, logout, keepalive, logoutOAuthToken, readOAuthToken, writeOAuthToken, deleteOAuthToken, authProviderMemberInfo, patchCoreAuthUser, saml2ServiceProvider, initServerSession, getSamlProviderById, authProviderLoginCallback, getDefaultLoginRedirect, resolveCoreIdProvider } from '#services' import type { SdStorage } from '../storages/interface.ts' import type { ActionPayload, ServerSession, User } from '#types' import eventsLog, { type EventLogContext } from '@data-fair/lib-express/events-log.js' @@ -14,7 +14,6 @@ import { reqI18n } from '#i18n' import limiter from '../utils/limiter.ts' import storages from '#storages' import { checkPassword, validatePassword, type Password } from '../utils/passwords.ts' -import { type OpenIDConnect } from '#types/site/index.ts' import { publicGlobalProviders, publicSiteProviders } from './providers.ts' import { type OAuthRelayState } from '../oauth/service.ts' import { type Saml2RelayState, getUserAttrs as getSamlUserAttrs } from '../saml2/service.ts' @@ -465,18 +464,7 @@ router.post('/keepalive', async (req, res, next) => { // with the provider (user exists, has role, etc) const coreIdProvider = user.coreIdProvider if (coreIdProvider?.type === 'oauth' || coreIdProvider?.type === 'oidc') { - let provider - const site = await reqSite(req) - if (site?.authMode === 'onlyBackOffice' || !site?.authMode) { - provider = oauthGlobalProviders().find(p => p.id === coreIdProvider.id) - } else { - let authSite = site - if (site.authMode === 'onlyOtherSite' && site.authOnlyOtherSite) { - authSite = await getSiteByUrl('https://' + site.authOnlyOtherSite) ?? site - } - const providerInfo = authSite.authProviders?.find(p => p.type === 'oidc' && getOidcProviderId(p.discovery) === coreIdProvider.id) as OpenIDConnect | undefined - provider = providerInfo && await initOidcProvider(providerInfo, `https://${authSite.host}${authSite.path ?? ''}/simple-directory`) - } + const provider = await resolveCoreIdProvider(req, coreIdProvider) if (!provider) { await logout(req, res) return res.status(401).send('Fournisseur d\'identité principal inconnu') @@ -521,8 +509,40 @@ router.post('/keepalive', async (req, res, next) => { router.delete('/', async (req, res) => { const logContext: EventLogContext = { req } + const sessionUser = reqUser(req) + + // gather OIDC logout info before clearing the session + let endSessionUrl: string | undefined + if (sessionUser) { + const storage = await storages.getSessionStorage(reqSession(req)) + const fullUser = await storage.getUser(sessionUser.id) + const coreIdProvider = fullUser?.coreIdProvider + if (coreIdProvider?.type === 'oidc' && fullUser) { + try { + const provider = await resolveCoreIdProvider(req, coreIdProvider) + if (provider?.endSessionEndpoint) { + const url = new URL(provider.endSessionEndpoint) + url.searchParams.set('client_id', provider.client.id) + const oauthToken = await readOAuthToken(fullUser, provider) + if (oauthToken?.token?.id_token) { + url.searchParams.set('id_token_hint', oauthToken.token.id_token) + } + url.searchParams.set('post_logout_redirect_uri', reqSiteUrl(req) + '/simple-directory/login') + endSessionUrl = url.href + await deleteOAuthToken(fullUser, provider) + } + } catch (err) { + console.warn('failed to build end_session_endpoint URL', err) + } + } + } + await logout(req, res) eventsLog.info('sd.auth.session-delete', 'a session was deleted', logContext) + + if (endSessionUrl) { + return res.status(200).json({ endSessionUrl }) + } res.status(204).send() }) diff --git a/api/src/oauth/oidc.ts b/api/src/oauth/oidc.ts index b85db20a..b1e982b9 100644 --- a/api/src/oauth/oidc.ts +++ b/api/src/oauth/oidc.ts @@ -99,6 +99,7 @@ export async function completeOidcProvider (p: OpenIDConnect): Promise & { title?: string, icon?: string, scope: string, + endSessionEndpoint?: string, auth: { tokenHost: string, tokenPath: string, @@ -85,6 +86,21 @@ export const getOAuthProviderByState = async (req: Request, state: string): Prom } } +// Resolve an OIDC provider from a user's coreIdProvider reference. +// Handles onlyOtherSite auth delegation. +export const resolveCoreIdProvider = async (req: Request, coreIdProvider: { id: string, type: string }): Promise => { + const site = await reqSite(req) + if (site?.authMode === 'onlyBackOffice' || !site?.authMode) { + return oauthGlobalProviders().find(p => p.id === coreIdProvider.id) + } + let authSite = site + if (site.authMode === 'onlyOtherSite' && site.authOnlyOtherSite) { + authSite = await getSiteByUrl('https://' + site.authOnlyOtherSite) ?? site + } + const providerInfo = authSite.authProviders?.find(p => p.type === 'oidc' && getOidcProviderId(p.discovery) === coreIdProvider.id) as OpenIDConnect | undefined + return providerInfo ? await initOidcProvider(providerInfo, `https://${authSite.host}${authSite.path ?? ''}/simple-directory`) : undefined +} + async function initOAuthProvider (p: OAuthProvider, publicUrl = config.publicUrl): Promise { const client = { id: p.client.id, secret: decipher(p.client.secret as CipheredContent) } const oauthClient = new oauth2.AuthorizationCode({ diff --git a/api/src/services.ts b/api/src/services.ts index 3e46f9ab..ec8d9320 100644 --- a/api/src/services.ts +++ b/api/src/services.ts @@ -4,7 +4,7 @@ export * from './avatars/service.ts' export * from './invitations/service.ts' export * from './limits/service.ts' export * from './mails/service.ts' -export { initOidcProvider, oauthGlobalProviders, getOidcProviderId, getOAuthProviderById, getOAuthProviderByState } from './oauth/service.ts' +export { initOidcProvider, oauthGlobalProviders, getOidcProviderId, getOAuthProviderById, getOAuthProviderByState, resolveCoreIdProvider } from './oauth/service.ts' export * from './oauth-tokens/service.ts' export { saml2ServiceProvider, saml2GlobalProviders, getSamlProviderId, getSamlConfigId, getSamlProviderById } from './saml2/service.ts' export { reqSite, getSiteByUrl, getRedirectSite, getSiteBaseUrl, getSiteByHost } from './sites/service.ts' diff --git a/package-lock.json b/package-lock.json index 17d46f4f..dfa2512b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -691,6 +691,7 @@ "resolved": "https://registry.npmjs.org/@data-fair/lib-node/-/lib-node-2.12.1.tgz", "integrity": "sha512-qNQwLV1XbzrRs/UlhHWWJRArenIy7qdg9JKuWK/NPnlLAJAzTvWkiOM/Q2UVnFo2CquJ4+7uUMymhEt7+nBw+Q==", "license": "MIT", + "peer": true, "dependencies": { "@data-fair/lib-common-types": "^1.8.4", "@data-fair/lib-utils": "^1.1.0", @@ -762,20 +763,6 @@ } } }, - "node_modules/@data-fair/lib-types-builder/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@data-fair/lib-utils": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@data-fair/lib-utils/-/lib-utils-1.10.1.tgz", @@ -805,6 +792,7 @@ "resolved": "https://registry.npmjs.org/@data-fair/lib-vue/-/lib-vue-1.27.1.tgz", "integrity": "sha512-vpUu7GXvdy3fNYiX6Y/heka+FBLrltcy/9ksCfVn0yTsOiXrS3tFwaD3RUMQUF+Q72KsGNI47A6TN9fgpOb/5w==", "license": "MIT", + "peer": true, "dependencies": { "@data-fair/lib-common-types": "^1.7.1", "@data-fair/lib-utils": "^1.9.0", @@ -1855,6 +1843,7 @@ "resolved": "https://registry.npmjs.org/@json-layout/vocabulary/-/vocabulary-2.3.3.tgz", "integrity": "sha512-M2IqHnPcpKUAOrFSSH7Nv7yu9nkQll8R4kPcz5FRWE223rpg9KYV5cmTXzZ0HAglN949I48a2u8l4ua2PknKpQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.17.1", "ajv-errors": "^3.0.0", @@ -1896,6 +1885,7 @@ "resolved": "https://registry.npmjs.org/@koumoul/vjsf-compiler/-/vjsf-compiler-1.2.3.tgz", "integrity": "sha512-fsN0+paFY3v0iZk/O4mPREKWInkAnrLBBfqgcoSxIgworq77t/J8AbCby3lFtwSChrEZ4lUBEgfJ0drxe5wN2w==", "license": "MIT", + "peer": true, "dependencies": { "@json-layout/core": "^2.0.0", "@json-layout/vocabulary": "^2.8.0", @@ -1927,6 +1917,7 @@ "resolved": "https://registry.npmjs.org/@json-layout/vocabulary/-/vocabulary-2.8.0.tgz", "integrity": "sha512-7jJ/719ksvlz+BKTBy+2HlQGxmfS2xmFCQvWlP4lOxvy8R+E/YAfR4n73W0qFl4B/F8f6YrBYIszhaVsPXhs4A==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.17.1", "ajv-errors": "^3.0.0", @@ -2899,6 +2890,7 @@ "node_modules/@types/express": { "version": "4.17.21", "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -3006,6 +2998,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.20.0" } @@ -3194,6 +3187,7 @@ "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", @@ -3342,9 +3336,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3595,13 +3589,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz", - "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz", + "integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@vue/shared": "3.5.30", + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.31", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -3620,26 +3614,26 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz", - "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz", + "integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.30", - "@vue/shared": "3.5.30" + "@vue/compiler-core": "3.5.31", + "@vue/shared": "3.5.31" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", - "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz", + "integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@vue/compiler-core": "3.5.30", - "@vue/compiler-dom": "3.5.30", - "@vue/compiler-ssr": "3.5.30", - "@vue/shared": "3.5.30", + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.31", + "@vue/compiler-dom": "3.5.31", + "@vue/compiler-ssr": "3.5.31", + "@vue/shared": "3.5.31", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.8", @@ -3647,13 +3641,13 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz", - "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz", + "integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.30", - "@vue/shared": "3.5.30" + "@vue/compiler-dom": "3.5.31", + "@vue/shared": "3.5.31" } }, "node_modules/@vue/compiler-vue2": { @@ -3695,53 +3689,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz", - "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz", + "integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.30" + "@vue/shared": "3.5.31" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz", - "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz", + "integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.30", - "@vue/shared": "3.5.30" + "@vue/reactivity": "3.5.31", + "@vue/shared": "3.5.31" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz", - "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz", + "integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.30", - "@vue/runtime-core": "3.5.30", - "@vue/shared": "3.5.30", + "@vue/reactivity": "3.5.31", + "@vue/runtime-core": "3.5.31", + "@vue/shared": "3.5.31", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz", - "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz", + "integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.30", - "@vue/shared": "3.5.30" + "@vue/compiler-ssr": "3.5.31", + "@vue/shared": "3.5.31" }, "peerDependencies": { - "vue": "3.5.30" + "vue": "3.5.31" } }, "node_modules/@vue/shared": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz", - "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz", + "integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==", "license": "MIT" }, "node_modules/@vuetify/loader-shared": { @@ -3852,6 +3846,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3881,6 +3876,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4794,6 +4790,7 @@ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -4984,7 +4981,8 @@ "version": "1.11.20", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/de-indent": { "version": "1.0.2", @@ -5079,8 +5077,7 @@ }, "node_modules/destr": { "version": "2.0.3", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/destroy": { "version": "1.2.0", @@ -5668,6 +5665,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6961,6 +6959,7 @@ "version": "4.4.5", "hasInstallScript": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.0" }, @@ -8955,8 +8954,7 @@ }, "node_modules/node-fetch-native": { "version": "1.6.4", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", @@ -10280,6 +10278,7 @@ "node_modules/rollup": { "version": "4.24.0", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.6" }, @@ -10920,9 +10919,9 @@ } }, "node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "license": "BSD-3-Clause", "engines": { "node": ">=20.0.0" @@ -11414,6 +11413,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11470,6 +11470,7 @@ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "devOptional": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "tldts": "^6.1.32" }, @@ -11660,6 +11661,7 @@ "node_modules/typescript": { "version": "5.6.3", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12228,6 +12230,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -12308,16 +12311,17 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz", - "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", + "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", "license": "MIT", + "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.30", - "@vue/compiler-sfc": "3.5.30", - "@vue/runtime-dom": "3.5.30", - "@vue/server-renderer": "3.5.30", - "@vue/shared": "3.5.30" + "@vue/compiler-dom": "3.5.31", + "@vue/compiler-sfc": "3.5.31", + "@vue/runtime-dom": "3.5.31", + "@vue/server-renderer": "3.5.31", + "@vue/shared": "3.5.31" }, "peerDependencies": { "typescript": "*" @@ -12397,6 +12401,7 @@ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz", "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==", "license": "MIT", + "peer": true, "dependencies": { "@intlify/core-base": "10.0.8", "@intlify/shared": "10.0.8", @@ -12417,6 +12422,7 @@ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", "license": "MIT", + "peer": true, "dependencies": { "@vue/devtools-api": "^6.6.4" }, @@ -12444,10 +12450,11 @@ } }, "node_modules/vuetify": { - "version": "3.12.3", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.12.3.tgz", - "integrity": "sha512-7QzgftMu8OYKRz/jr2yntPEJ7WFmAzMn8jyeUcW7gz539MjQbDF3UrqZXW3aWi458UVJW9WWvzQn9x5B75Aijw==", + "version": "3.12.4", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.12.4.tgz", + "integrity": "sha512-9pyvSdL4vGEioOSJKjoyRxyPc2HYYINWkwxE4ku8PuGGRhCoEchXL57EL1JabIy+D8lUsx+co/G+tUaksvpXrw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/johnleider"