From 9f2176cd4bae9932904f341a6c4252a2c59e8b8e Mon Sep 17 00:00:00 2001 From: Guillermo Pages Date: Wed, 24 Sep 2025 09:58:55 +0200 Subject: [PATCH] freeze: just commit to have a history track, but this code is not working yet --- README.md | 228 ++++----- package-lock.json | 529 ++++++++++---------- package.json | 4 +- src/config/appConfigMap.ts | 3 +- src/index.ts | 2 +- src/loaders/index.ts | 6 +- src/loaders/oidcStandardRoutes.ts | 69 --- src/loaders/oidcStandardRoutesMiddleware.ts | 45 ++ src/oidc/oidcConfigBuilder.ts | 32 ++ 9 files changed, 449 insertions(+), 469 deletions(-) delete mode 100644 src/loaders/oidcStandardRoutes.ts create mode 100644 src/loaders/oidcStandardRoutesMiddleware.ts create mode 100644 src/oidc/oidcConfigBuilder.ts diff --git a/README.md b/README.md index 5f2e175..59ce5de 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # swissoid-back -SwissOID authentication package for Node.js backends. Provides reusable authentication components for integrating SwissOID (OpenID Connect) authentication into Node.js applications. +SwissOID authentication backend for Node.js applications. Provides reusable OIDC authentication components for integrating with SwissOID (Swiss eID). ## Features -- 🔐 Complete OIDC Authorization Code Flow implementation -- 🍪 Secure cookie-based session management -- 🔑 JWT verification with JWKS support -- 📦 Dependency Injection ready (di-why compatible) -- 🚀 TypeScript support with full type definitions -- ⚡ Redis-based session storage +- 🔐 Full OIDC Authorization Code Flow implementation +- 🍪 Session management with Redis +- 🔑 JWT verification with JWKS +- 🎯 Built for di-why dependency injection +- 📦 Reusable authentication components +- 🔄 Configurable via environment variables or appConfig ## Installation @@ -17,169 +17,137 @@ SwissOID authentication package for Node.js backends. Provides reusable authenti npm install swissoid-back ``` -## Usage +## Requirements -### Basic Setup with Dependency Injection +- Node.js >= 18 +- Redis server for session storage +- SwissOID client credentials -```typescript -import { - oidcRoutesLDEGen, - swissoidSessionServiceLDEGen, - cookieManagerLDEGen, - swissoidJWTVerifierLDEGen, - redisClientLDEGen -} from 'swissoid-back'; -import DiContainer from 'di-why'; +## Usage -const container = new DiContainer({ - load: { - // Redis client for session storage - redisClient: redisClientLDEGen(), +### With express-knifey / graphql-knifey (Recommended) - // Session service - sessionService: swissoidSessionServiceLDEGen(), +swissoid-back integrates seamlessly with express-knifey's middleware system: - // Cookie manager - cookieManager: cookieManagerLDEGen({ - domain: '.example.com', - sessionName: 'app_session' +```typescript +import DiContainer, { mergeLDs } from 'di-why'; +import { apolloSubgraphServerModularLDGen } from 'graphql-knifey'; +import { swissoidAuthLoadDict } from 'swissoid-back'; + +// Define middleware configuration including OIDC routes +const middlewareConfig = { + '/graphql': [ + { name: 'expressCorsMiddleware', priority: 90 }, + { name: 'expressCookieParserMiddleware', priority: 80 }, + { name: 'expressBodyParserMiddleware', priority: 70 }, + { name: 'expressGraphqlMiddleware', required: true, priority: -100 }, + ], + '*': [ + // Add OIDC routes as global middleware + { name: 'oidcStandardRoutesMiddleware', priority: 50 }, + ] +}; + +const diContainer = new DiContainer({ + load: mergeLDs( + // GraphQL server with middleware config + apolloSubgraphServerModularLDGen({ + resolvers, + typeDefs, + middlewareConfig }), - // JWT verifier - jwtVerifier: swissoidJWTVerifierLDEGen(), + // SwissOID authentication components + swissoidAuthLoadDict, - // OIDC routes - oidcRoutes: oidcRoutesLDEGen({ - clientId: 'your-client-id', - cookieDomain: '.example.com' - }) - } + // Your other loaders... + ) }); ``` -### Express Integration +### Direct Express Usage + +```typescript +import { swissoidAuthLoadDict } from 'swissoid-back'; + +// Load OIDC routes directly (this will mount them on the express app) +await diContainer.load('oidcStandardRoutes'); +``` + +### AppConfig Integration + +swissoid-back provides an appConfigMap that can be merged with your application's configuration: ```typescript -import express from 'express'; -import cookieParser from 'cookie-parser'; - -const app = express(); - -// Required middleware -app.use(cookieParser()); - -// Mount OIDC routes -const oidcRoutes = await container.get('oidcRoutes'); -app.use(oidcRoutes); - -// Routes provided: -// GET /login - Initiates OIDC flow -// POST /oidc/callback - Handles callback from IdP -// GET /oidc/finalize - Sets cookies in first-party context -// GET /auth/status - Returns authentication status -// GET /auth/userinfo - Returns user information -// GET /auth/logout - Destroys session -// POST /auth/logout - Destroys session (JSON response) +import { swissoidMergeAppConfigMap } from 'swissoid-back'; + +const merged = swissoidMergeAppConfigMap(yourAppConfigMap); +export type AppConfig = ReturnType; ``` ### Configuration -Required environment variables: +Create a `.env` file with the required configuration (see `.env.example` for all options): -```bash +```env # SwissOID Configuration SWISSOID_ISSUER=https://api.swissoid.com SWISSOID_CLIENT_ID=your-client-id SWISSOID_CLIENT_SECRET=your-client-secret SWISSOID_TOKEN_ENDPOINT=https://api.swissoid.com/token SWISSOID_JWKS_URI=https://api.swissoid.com/.well-known/jwks.json -SWISSOID_AUTHORIZE_ENDPOINT=https://api.swissoid.com/authorize -# RP Configuration -RP_CALLBACK_URL=https://your-app.com/oidc/callback -RP_COOKIE_DOMAIN=.your-app.com -RP_FRONTEND_URL=https://app.your-app.com +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 # Session Configuration -SESSION_COOKIE_NAME=app_session +SESSION_COOKIE_NAME=connect.sid SESSION_SECRET=your-session-secret -STATE_SIGNING_SECRET=your-state-signing-secret -# Redis -REDIS_URL=redis://localhost:6379 +# RP Configuration +RP_FRONTEND_URL=http://localhost:3000 +COOKIE_DOMAIN=localhost +OIDC_REDIRECT_BASE_URL=http://localhost:3668 ``` -### Manual Usage (without DI) - -```typescript -import { createOIDCRoutes, SwissOIDSessionService, CookieManager } from 'swissoid-back'; -import Redis from 'ioredis'; +## Routes -// Create Redis client -const redisClient = new Redis('redis://localhost:6379'); +The package provides the following OIDC routes when loaded: -// Create session service -const sessionService = new SwissOIDSessionService(redisClient, console); +- `GET /login` - Initiates OIDC authorization flow +- `POST /oidc/callback` - Handles OIDC callback from SwissOID +- `POST /oidc/finalize` - Completes authentication and sets session +- `GET /auth/status` - Returns current authentication status +- `POST /auth/logout` - Logs out the user -// Create cookie manager -const cookieManager = new CookieManager({ - domain: '.example.com', - sessionName: 'app_session', - secureCookie: true, - sameSite: 'lax' -}); - -// Create OIDC routes -const oidcRoutes = createOIDCRoutes({ - logger: console, - sessionService, - redisClient, - issuer: 'https://api.swissoid.com', - clientId: 'your-client-id', - // ... other config -}); +## Components -app.use(oidcRoutes); -``` +The `swissoidAuthLoadDict` includes: -## API Reference +- **redisClient**: Redis connection for session storage +- **sessionService**: Session management service +- **cookieManager**: Cookie handling utilities +- **oidcStandardRoutes**: Express router with OIDC endpoints +- **oidcStandardRoutesMiddleware**: Middleware-compatible version for express-knifey integration +- **swissoidAppConfigMap**: AppConfig map with priority 60 -### Components +## Exports -- **oidcRoutesLDEGen**: Loader for OIDC route handlers -- **swissoidSessionServiceLDEGen**: Loader for session management service -- **cookieManagerLDEGen**: Loader for cookie operations -- **swissoidJWTVerifierLDEGen**: Loader for JWT verification -- **redisClientLDEGen**: Loader for Redis client +```typescript +// Main LoadDict for di-why +import { swissoidAuthLoadDict } from 'swissoid-back'; -### Types +// AppConfig utilities +import { swissoidMergeAppConfigMap } from 'swissoid-back'; -```typescript -interface OIDCConfig { - issuer: string; - clientId: string; - clientSecret?: string; - tokenEndpoint: string; - jwksUri: string; - authorizeEndpoint: string; - callbackUrl: string; - cookieDomain: string; - frontendUrl: string; - sessionCookieName: string; - sessionSecret: string; - stateSigningSecret: string; -} - -interface SessionData { - sub: string; - email?: string; - name?: string; - iat: number; - exp: number; - createdAt?: number; - lastAccessedAt?: number; - metadata?: Record; -} +// Individual loaders if needed +import { + sessionService, + cookieManager, + oidcStandardRoutes, + oidcStandardRoutesMiddleware +} from 'swissoid-back'; ``` ## License diff --git a/package-lock.json b/package-lock.json index e08791d..d04a149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@types/node-fetch": "^2.6.13", "cookie-parser": "^1.4.7", "di-why": "^0.20.0", - "express": "^5.1.0", + "express": "^4.21.2", "ioredis": "^5.7.0", "jose": "^6.1.0", "node-fetch": "^3.3.2", @@ -21,7 +21,7 @@ }, "devDependencies": { "@types/cookie-parser": "^1.4.9", - "@types/express": "^5.0.3", + "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^18.0.0", "jest": "^30.1.3", @@ -1214,14 +1214,15 @@ } }, "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", "@types/serve-static": "*" } }, @@ -1237,6 +1238,19 @@ "@types/send": "*" } }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -1642,34 +1656,13 @@ ] }, "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -1744,6 +1737,12 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1869,25 +1868,44 @@ } }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "license": "MIT", "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=18" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -2249,9 +2267,9 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -2397,6 +2415,16 @@ "node": ">= 0.8" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -2633,77 +2661,75 @@ } }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 18" + "node": ">= 0.10.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/express/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/express/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" + "ms": "2.0.0" } }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2758,22 +2784,38 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -2856,12 +2898,12 @@ } }, "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/fs.realpath": { @@ -3109,15 +3151,6 @@ "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3129,12 +3162,12 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" @@ -3258,12 +3291,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4168,22 +4195,19 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "license": "MIT", - "engines": { - "node": ">=18" - }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -4195,6 +4219,15 @@ "dev": true, "license": "MIT" }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -4209,6 +4242,18 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4306,9 +4351,9 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4424,6 +4469,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -4590,14 +4636,10 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", @@ -4701,12 +4743,12 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -4725,34 +4767,18 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.7.0", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8" } }, "node_modules/react-is": { @@ -4832,22 +4858,6 @@ "node": ">=8" } }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4885,61 +4895,66 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">= 18" + "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "license": "MIT", "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" }, "engines": { - "node": ">= 18" + "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { @@ -5108,9 +5123,9 @@ "license": "MIT" }, "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5520,35 +5535,13 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" @@ -5663,6 +5656,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -5828,6 +5830,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/package.json b/package.json index 16d472e..479032e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@types/node-fetch": "^2.6.13", "cookie-parser": "^1.4.7", "di-why": "^0.20.0", - "express": "^5.1.0", + "express": "^4.21.2", "ioredis": "^5.7.0", "jose": "^6.1.0", "node-fetch": "^3.3.2", @@ -32,7 +32,7 @@ }, "devDependencies": { "@types/cookie-parser": "^1.4.9", - "@types/express": "^5.0.3", + "@types/express": "^4.17.21", "@types/jest": "^30.0.0", "@types/node": "^18.0.0", "jest": "^30.1.3", diff --git a/src/config/appConfigMap.ts b/src/config/appConfigMap.ts index 88537e6..97055e5 100644 --- a/src/config/appConfigMap.ts +++ b/src/config/appConfigMap.ts @@ -105,4 +105,5 @@ const appConfigMap = function (env: { export default appConfigMap; -export type SwissoidAppConfigMap = typeof appConfigMap; \ No newline at end of file +export type SwissoidAppConfigMap = typeof appConfigMap; +export type SwissoidAppConfig = ReturnType; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index c58fb66..31685f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ export { swissoidAuthLoadDict }; export default swissoidAuthLoadDict; // Individual loaders if needed separately -export { sessionService, cookieManager, oidcStandardRoutes } from './loaders'; +export { sessionService, cookieManager, oidcStandardRoutesMiddleware } from './loaders'; // Export utilities export { swissoidAppConfigMapNamespace } from './utils/swissoidAppConfigMapListAdd'; diff --git a/src/loaders/index.ts b/src/loaders/index.ts index 6eddef1..7dc5761 100644 --- a/src/loaders/index.ts +++ b/src/loaders/index.ts @@ -3,18 +3,18 @@ import { LoadDict } from 'di-why'; import redisClient from './redisClient'; import sessionService from './sessionService'; import cookieManager from './cookieManager'; -import oidcStandardRoutes from './oidcStandardRoutes'; +import oidcStandardRoutesMiddleware from './oidcStandardRoutesMiddleware'; import appConfigMap from '../config/appConfigMap'; import { swissoidAppConfigMapNamespace } from '../utils/swissoidAppConfigMapListAdd'; import { addMergeableConfigMap } from 'di-why'; -export { redisClient, sessionService, cookieManager, oidcStandardRoutes }; +export { redisClient, sessionService, cookieManager, oidcStandardRoutesMiddleware }; const loadDict: LoadDict = { redisClient, sessionService, cookieManager, - oidcStandardRoutes, + oidcStandardRoutesMiddleware, // Middleware version for graphql-knifey integration ...addMergeableConfigMap(appConfigMap, swissoidAppConfigMapNamespace), }; diff --git a/src/loaders/oidcStandardRoutes.ts b/src/loaders/oidcStandardRoutes.ts deleted file mode 100644 index 2456590..0000000 --- a/src/loaders/oidcStandardRoutes.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { LoadDictElement } from 'di-why/build/src/DiContainer'; -import { Express } from 'express'; -import { createOidcStandardRoutes } from '../oidc/OIDCStandardRoutes'; - -const loadDictElement: LoadDictElement = { - factory: ({ - app, - logger, - sessionService, - appConfig, - redisClient - }) => { - // Debug log appConfig to see what's available - logger.log('[OIDC_LOADER] AppConfig received:', { - hasCookieDomain: 'cookieDomain' in appConfig, - cookieDomainValue: appConfig.cookieDomain || 'UNDEFINED', - cookieDomainType: typeof appConfig.cookieDomain, - appConfigKeys: Object.keys(appConfig).sort(), - // Log a few other expected keys to verify appConfig structure - hasSwissoidIssuer: 'swissoidIssuer' in appConfig, - swissoidIssuerValue: appConfig.swissoidIssuer || 'UNDEFINED' - }); - - // Configuration for standard OIDC flow with signed state - const config = { - logger, - sessionService, - redisClient, // Added for JTI replay prevention - - // SwissOID configuration - swissoidIssuer: appConfig.swissoidIssuer, - swissoidClientId: appConfig.swissoidClientId, - swissoidClientSecret: appConfig.swissoidClientSecret, - swissoidTokenEndpoint: appConfig.swissoidTokenEndpoint, - swissoidJwksUri: appConfig.swissoidJwksUri, - swissoidAuthorizeEndpoint: appConfig.swissoidAuthorizeEndpoint || `${appConfig.swissoidIssuer}/authorize`, - - // RP configuration - rpCallbackUrl: appConfig.rpCallbackUrl, - rpCookieDomain: appConfig.cookieDomain, - rpFrontendUrl: appConfig.rpFrontendUrl, - - // Session configuration - sessionCookieName: appConfig.sessionCookieName, - sessionSecret: appConfig.sessionSecret, - - // State signing secret (use a dedicated secret or derive from session secret) - stateSigningSecret: appConfig.stateSigningSecret || appConfig.sessionSecret + '-state-signing' - }; - - const router = createOidcStandardRoutes(config); - - // Mount the standard OIDC routes - (app as Express).use(router); - - logger.log('Standard OIDC routes mounted - /login, POST /oidc/callback, /auth/status, /auth/userinfo, /auth/logout'); - - return 'oidcStandardRoutes-loaded'; - }, - locateDeps: { - app: 'app', - logger: 'logger', - sessionService: 'sessionService', - appConfig: 'appConfig', - redisClient: 'redisClient' - } -}; - -export default loadDictElement; \ No newline at end of file diff --git a/src/loaders/oidcStandardRoutesMiddleware.ts b/src/loaders/oidcStandardRoutesMiddleware.ts new file mode 100644 index 0000000..a72d9fa --- /dev/null +++ b/src/loaders/oidcStandardRoutesMiddleware.ts @@ -0,0 +1,45 @@ +import { LoadDictElement } from 'di-why'; +import { Express } from 'express'; +import { createOidcStandardRoutes } from '../oidc/OIDCStandardRoutes'; +import { buildOidcConfig } from './oidcConfigBuilder'; + +/** + * OIDC Standard Routes as a Middleware Attacher + * Compatible with express-knifey's middleware system + */ +const loadDictElement: LoadDictElement<(path: string | '*') => void> = { + factory: ({ + app, + logger, + sessionService, + appConfig, + redisClient + }) => { + // Build configuration using shared builder + const config = { + logger, + sessionService, + ...buildOidcConfig(appConfig, redisClient) + }; + + const router = createOidcStandardRoutes(config); + + // Return a MiddlewareAttacher function + // The path parameter is ignored since OIDC routes define their own paths + return (path: string | '*') => { + // Mount the OIDC routes on the Express app + (app as Express).use(router); + + logger.log('OIDC Standard Routes mounted via middleware system - /login, POST /oidc/callback, /auth/status, /auth/logout'); + }; + }, + locateDeps: { + app: 'app', + logger: 'logger', + sessionService: 'sessionService', + appConfig: 'appConfig', + redisClient: 'redisClient' + } +}; + +export default loadDictElement; \ No newline at end of file diff --git a/src/oidc/oidcConfigBuilder.ts b/src/oidc/oidcConfigBuilder.ts new file mode 100644 index 0000000..3f2ae0d --- /dev/null +++ b/src/oidc/oidcConfigBuilder.ts @@ -0,0 +1,32 @@ +import Redis from "ioredis"; +import { SwissoidAppConfig } from "../config/appConfigMap"; + +/** + * Shared configuration builder for OIDC loaders + */ +export function buildOidcConfig(appConfig: SwissoidAppConfig, redisClient: Redis) { + return { + redisClient, + + // SwissOID configuration + swissoidIssuer: appConfig.swissoidIssuer, + swissoidClientId: appConfig.swissoidClientId, + swissoidClientSecret: appConfig.swissoidClientSecret, + swissoidTokenEndpoint: appConfig.swissoidTokenEndpoint, + swissoidJwksUri: appConfig.swissoidJwksUri, + swissoidAuthorizeEndpoint: appConfig.swissoidAuthorizeEndpoint || `${appConfig.swissoidIssuer}/authorize`, + + // RP configuration + // These are validated to exist in appConfigMap, but TypeScript can't infer that + rpCallbackUrl: appConfig.rpCallbackUrl!, + rpCookieDomain: appConfig.cookieDomain!, + rpFrontendUrl: appConfig.rpFrontendUrl, + + // Session configuration + sessionCookieName: appConfig.sessionCookieName, + sessionSecret: appConfig.sessionSecret, + + // State signing secret + stateSigningSecret: appConfig.stateSigningSecret || appConfig.sessionSecret + '-state-signing' + }; +} \ No newline at end of file