emilsoftware-utilities 2.0.0-dev.69 → 2.0.0-dev.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/accessi/resource-server/runtime.js +51 -10
  2. package/dist/accessi/resource-server/runtime.js.map +1 -1
  3. package/dist/accessi/resource-server/types.d.ts +9 -2
  4. package/dist/accessi/resource-server/types.js.map +1 -1
  5. package/dist/accessi/server/standalone.js +3 -2
  6. package/dist/accessi/server/standalone.js.map +1 -1
  7. package/dist/accessi-module/AccessiModule.d.ts +4 -1
  8. package/dist/accessi-module/AccessiModule.js.map +1 -1
  9. package/dist/accessi-module/Console/console.js +5 -3
  10. package/dist/accessi-module/Console/generated/emilsoftware-utilities.d.ts +11 -1
  11. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js +12 -4
  12. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js.map +1 -1
  13. package/dist/accessi-module/Console/generated/model/index.d.ts +1 -0
  14. package/dist/accessi-module/Console/generated/model/index.js +1 -0
  15. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  16. package/dist/accessi-module/Console/generated/model/revokeTokenRequest.d.ts +10 -0
  17. package/dist/accessi-module/Console/generated/model/revokeTokenRequest.js +9 -0
  18. package/dist/accessi-module/Console/generated/model/revokeTokenRequest.js.map +1 -0
  19. package/dist/accessi-module/Console/wiki.js +5 -3
  20. package/dist/accessi-module/Console/wiki.js.map +1 -1
  21. package/dist/accessi-module/Controllers/AuthController.d.ts +2 -1
  22. package/dist/accessi-module/Controllers/AuthController.js +63 -0
  23. package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
  24. package/dist/accessi-module/Dtos/RevokeTokenRequest.d.ts +3 -0
  25. package/dist/accessi-module/Dtos/RevokeTokenRequest.js +27 -0
  26. package/dist/accessi-module/Dtos/RevokeTokenRequest.js.map +1 -0
  27. package/dist/accessi-module/Dtos/index.d.ts +1 -0
  28. package/dist/accessi-module/Dtos/index.js +1 -0
  29. package/dist/accessi-module/Dtos/index.js.map +1 -1
  30. package/dist/accessi-module/Services/AuthService/AuthService.d.ts +22 -2
  31. package/dist/accessi-module/Services/AuthService/AuthService.js +115 -19
  32. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  33. package/dist/accessi-module/Services/UserService/UserService.d.ts +6 -0
  34. package/dist/accessi-module/Services/UserService/UserService.js +61 -5
  35. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  36. package/dist/accessi-module/federated-auth/FederatedAuthService.js +5 -0
  37. package/dist/accessi-module/federated-auth/FederatedAuthService.js.map +1 -1
  38. package/dist/accessi-module/index.d.ts +5 -3
  39. package/dist/accessi-module/index.js +13 -2
  40. package/dist/accessi-module/index.js.map +1 -1
  41. package/dist/accessi-module/security/accessTokenRevocation.d.ts +47 -0
  42. package/dist/accessi-module/security/accessTokenRevocation.js +143 -0
  43. package/dist/accessi-module/security/accessTokenRevocation.js.map +1 -0
  44. package/dist/accessi-module/security/refreshTokenRotation.d.ts +42 -2
  45. package/dist/accessi-module/security/refreshTokenRotation.js +123 -10
  46. package/dist/accessi-module/security/refreshTokenRotation.js.map +1 -1
  47. package/dist/accessi-module/security/sessionRevocation.d.ts +21 -6
  48. package/dist/accessi-module/security/sessionRevocation.js +51 -5
  49. package/dist/accessi-module/security/sessionRevocation.js.map +1 -1
  50. package/dist/emilsoftware-utilities-2.0.0-dev.70.tgz +0 -0
  51. package/docs/accessi-openapi-fingerprint.json +2 -0
  52. package/package.json +3 -3
  53. package/dist/emilsoftware-utilities-2.0.0-dev.69.tgz +0 -0
@@ -26,8 +26,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.setupAccessiJwks = exports.getAccessiJwksPath = exports.createAccessiJwksHandler = exports.buildAccessiJwks = exports.LOGIN_AS_QUERY_PARAM = exports.buildLoginAsUrl = exports.ACCESSI_EMAIL_NOT_CONFIGURED = exports.AccessiEmailNotConfiguredError = exports.isEmailConfigured = exports.assertEmailConfigured = exports.getAccessiServiceToken = exports.RequireServiceTokenScopes = exports.ServiceTokenGuard = exports.isValidAdminBootstrapToken = exports.clearAdminBootstrapToken = exports.getAdminBootstrapToken = exports.getOrCreateAdminBootstrapToken = exports.AdminBootstrapService = exports.SchemaExportService = exports.ServiceTokenService = exports.discoverCatalogScripts = exports.computeCatalogScriptChecksum = exports.leadingStatementKeyword = exports.analyzeCatalogScript = exports.splitSqlStatements = exports.listPendingCatalogScripts = exports.applyCatalogScripts = exports.AccessiCatalogMigrator = exports.getRefreshTokenRotationStore = exports.createRedisRefreshTokenRotationStore = exports.createMemoryRefreshTokenRotationStore = exports.getSessionRevocationStore = exports.isAccessiTokenRevoked = exports.revokeAccessiUserSessions = exports.createRedisSessionRevocationStore = exports.createMemorySessionRevocationStore = exports.auditAccessi = exports.validateAccessiOptions = exports.assertValidAccessiOptions = exports.createAccessiEdgeRateLimitMiddleware = exports.getDefaultRateLimitStore = exports.createRedisRateLimitStore = exports.createMemoryRateLimitStore = exports.getDefaultConsoleEntryStore = exports.createRedisConsoleEntryStore = exports.createMemoryConsoleEntryStore = exports.AccessiDatabaseUpdater = exports.FederatedAuthExchangeController = exports.AccessiIntrospectionController = exports.AccessiModule = void 0;
30
- exports.ACCESSI_MODULE_SERVICES_LOCALS_KEY = exports.getAccessiModuleServices = exports.registerAccessiModuleServices = exports.accessiRequirement = exports.authenticateGen = exports.authorizeAccessi = exports.FederatedAuthService = void 0;
29
+ exports.getAccessiServiceToken = exports.RequireServiceTokenScopes = exports.ServiceTokenGuard = exports.isValidAdminBootstrapToken = exports.clearAdminBootstrapToken = exports.getAdminBootstrapToken = exports.getOrCreateAdminBootstrapToken = exports.AdminBootstrapService = exports.SchemaExportService = exports.ServiceTokenService = exports.discoverCatalogScripts = exports.computeCatalogScriptChecksum = exports.leadingStatementKeyword = exports.analyzeCatalogScript = exports.splitSqlStatements = exports.listPendingCatalogScripts = exports.applyCatalogScripts = exports.AccessiCatalogMigrator = exports.revokeAccessiAccessTokenIfConfigured = exports.revokeAccessiAccessToken = exports.isAccessiAccessTokenRevoked = exports.getAccessTokenRevocationStore = exports.createRedisAccessTokenRevocationStore = exports.createMemoryAccessTokenRevocationStore = exports.revokeRefreshTokenFamily = exports.isRefreshTokenFamilyRevoked = exports.getRefreshTokenRotationStore = exports.consumeRefreshToken = exports.createRedisRefreshTokenRotationStore = exports.createMemoryRefreshTokenRotationStore = exports.getSessionRevocationStore = exports.isAccessiTokenRevoked = exports.revokeAccessiUserSessionsIfConfigured = exports.revokeAccessiUserSessions = exports.createRedisSessionRevocationStore = exports.createMemorySessionRevocationStore = exports.auditAccessi = exports.validateAccessiOptions = exports.assertValidAccessiOptions = exports.createAccessiEdgeRateLimitMiddleware = exports.getDefaultRateLimitStore = exports.createRedisRateLimitStore = exports.createMemoryRateLimitStore = exports.getDefaultConsoleEntryStore = exports.createRedisConsoleEntryStore = exports.createMemoryConsoleEntryStore = exports.AccessiDatabaseUpdater = exports.FederatedAuthExchangeController = exports.AccessiIntrospectionController = exports.AccessiModule = void 0;
30
+ exports.ACCESSI_MODULE_SERVICES_LOCALS_KEY = exports.getAccessiModuleServices = exports.registerAccessiModuleServices = exports.accessiRequirement = exports.authenticateGen = exports.authorizeAccessi = exports.FederatedAuthService = exports.setupAccessiJwks = exports.getAccessiJwksPath = exports.createAccessiJwksHandler = exports.buildAccessiJwks = exports.LOGIN_AS_QUERY_PARAM = exports.buildLoginAsUrl = exports.ACCESSI_EMAIL_NOT_CONFIGURED = exports.AccessiEmailNotConfiguredError = exports.isEmailConfigured = exports.assertEmailConfigured = void 0;
31
31
  exports.initializeAccessiModule = initializeAccessiModule;
32
32
  const express_1 = __importDefault(require("express"));
33
33
  const core_1 = require("@nestjs/core");
@@ -178,12 +178,23 @@ var sessionRevocation_1 = require("./security/sessionRevocation");
178
178
  Object.defineProperty(exports, "createMemorySessionRevocationStore", { enumerable: true, get: function () { return sessionRevocation_1.createMemorySessionRevocationStore; } });
179
179
  Object.defineProperty(exports, "createRedisSessionRevocationStore", { enumerable: true, get: function () { return sessionRevocation_1.createRedisSessionRevocationStore; } });
180
180
  Object.defineProperty(exports, "revokeAccessiUserSessions", { enumerable: true, get: function () { return sessionRevocation_1.revokeAccessiUserSessions; } });
181
+ Object.defineProperty(exports, "revokeAccessiUserSessionsIfConfigured", { enumerable: true, get: function () { return sessionRevocation_1.revokeAccessiUserSessionsIfConfigured; } });
181
182
  Object.defineProperty(exports, "isAccessiTokenRevoked", { enumerable: true, get: function () { return sessionRevocation_1.isAccessiTokenRevoked; } });
182
183
  Object.defineProperty(exports, "getSessionRevocationStore", { enumerable: true, get: function () { return sessionRevocation_1.getSessionRevocationStore; } });
183
184
  var refreshTokenRotation_1 = require("./security/refreshTokenRotation");
184
185
  Object.defineProperty(exports, "createMemoryRefreshTokenRotationStore", { enumerable: true, get: function () { return refreshTokenRotation_1.createMemoryRefreshTokenRotationStore; } });
185
186
  Object.defineProperty(exports, "createRedisRefreshTokenRotationStore", { enumerable: true, get: function () { return refreshTokenRotation_1.createRedisRefreshTokenRotationStore; } });
187
+ Object.defineProperty(exports, "consumeRefreshToken", { enumerable: true, get: function () { return refreshTokenRotation_1.consumeRefreshToken; } });
186
188
  Object.defineProperty(exports, "getRefreshTokenRotationStore", { enumerable: true, get: function () { return refreshTokenRotation_1.getRefreshTokenRotationStore; } });
189
+ Object.defineProperty(exports, "isRefreshTokenFamilyRevoked", { enumerable: true, get: function () { return refreshTokenRotation_1.isRefreshTokenFamilyRevoked; } });
190
+ Object.defineProperty(exports, "revokeRefreshTokenFamily", { enumerable: true, get: function () { return refreshTokenRotation_1.revokeRefreshTokenFamily; } });
191
+ var accessTokenRevocation_1 = require("./security/accessTokenRevocation");
192
+ Object.defineProperty(exports, "createMemoryAccessTokenRevocationStore", { enumerable: true, get: function () { return accessTokenRevocation_1.createMemoryAccessTokenRevocationStore; } });
193
+ Object.defineProperty(exports, "createRedisAccessTokenRevocationStore", { enumerable: true, get: function () { return accessTokenRevocation_1.createRedisAccessTokenRevocationStore; } });
194
+ Object.defineProperty(exports, "getAccessTokenRevocationStore", { enumerable: true, get: function () { return accessTokenRevocation_1.getAccessTokenRevocationStore; } });
195
+ Object.defineProperty(exports, "isAccessiAccessTokenRevoked", { enumerable: true, get: function () { return accessTokenRevocation_1.isAccessiAccessTokenRevoked; } });
196
+ Object.defineProperty(exports, "revokeAccessiAccessToken", { enumerable: true, get: function () { return accessTokenRevocation_1.revokeAccessiAccessToken; } });
197
+ Object.defineProperty(exports, "revokeAccessiAccessTokenIfConfigured", { enumerable: true, get: function () { return accessTokenRevocation_1.revokeAccessiAccessTokenIfConfigured; } });
187
198
  var AccessiCatalogMigrator_1 = require("./database-updates/AccessiCatalogMigrator");
188
199
  Object.defineProperty(exports, "AccessiCatalogMigrator", { enumerable: true, get: function () { return AccessiCatalogMigrator_1.AccessiCatalogMigrator; } });
189
200
  Object.defineProperty(exports, "applyCatalogScripts", { enumerable: true, get: function () { return AccessiCatalogMigrator_1.applyCatalogScripts; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accessi-module/index.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport { AccessiModule, AccessiOptions } from \"./AccessiModule\";\nimport { Logger } from \"../Logger\";\nimport { AuthService } from \"./Services/AuthService/AuthService\";\nimport {\n buildOpenApiExcludePaths,\n isOpenApiRequestPath,\n resolveAccessiOpenApiOptions,\n setupOpenApi\n} from \"../openapi/OpenApiConfig\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessiRuntime\";\nimport { logAccessiStartup } from \"./startupLog\";\nimport { createAccessiValidationPipe } from './security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './security/AccessiHttpExceptionFilter';\nimport { assertEmailConfigured } from './security/emailConfiguration';\nimport { getAccessiJwksPath, setupAccessiJwks } from './security/jwks';\nimport { createAccessiEdgeRateLimitMiddleware } from './security/edgeRateLimit';\n\n/** Esito del bootstrap Express di Accessi. */\nexport interface AccessiModuleInitializationResult {\n /** Sotto-app Nest che serve `/api/accessi/*` e la documentazione. */\n nestApp: INestApplication;\n /** Documento OpenAPI generato, mergiabile nel documento unico dell'host. */\n document: OpenAPIObject;\n}\n\nfunction describeDatabaseTarget(options: AccessiOptions): string {\n const dbOptions = options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n\n return `${dbOptions.host ?? \"?\"}:${dbOptions.port ?? \"?\"} -> ${dbOptions.database ?? \"?\"} as ${dbOptions.user ?? \"?\"}`;\n}\n\n/**\n * Monta il modulo Accessi in un'applicazione Express gia esistente.\n *\n * Il bootstrap crea un host Nest isolato per le rotte `/api/accessi`, inizializza la\n * documentazione OpenAPI, validazione ed error handler, quindi rende disponibile\n * `authorizeAccessi` al resto dell'app.\n * Attendere questa Promise prima di aprire la porta HTTP: durante il bootstrap il middleware\n * restituisce `ACCESSI_AUTH_INITIALIZING` invece di autorizzare richieste parzialmente inizializzate.\n *\n * @param app Applicazione Express ospitante. Non viene sostituita ne vengono intercettate rotte estranee ad Accessi.\n * @param options Configurazione riservata del modulo; proviene normalmente da variabili d'ambiente o secret manager.\n * @returns Il sotto-app Nest e il documento OpenAPI generato (utile per il merge nell'host).\n */\nexport async function initializeAccessiModule(\n app: Application,\n options: AccessiOptions\n): Promise<AccessiModuleInitializationResult> {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n const logger: Logger = new Logger(\"initializeAccessiModule\");\n beginAccessiAuthInitialization();\n const startedAt = performance.now();\n const resolvedOpenApi = resolveAccessiOpenApiOptions(options.openapi);\n\n try {\n logger.info(\"Inizializzazione modulo accessi avviata.\");\n logger.info(`Target database accessi: ${describeDatabaseTarget(options)}`);\n logger.info(\n `Configurazione accessi: autoUpdateDatabase=${options.autoUpdateDatabase !== false}, legacyPasswordMigrationOnStartup=${options.legacyPasswordMigrationOnStartup !== false}`\n );\n const nestHostApp = express();\n if (options.http?.trustProxy !== undefined) {\n app.set('trust proxy', options.http.trustProxy);\n nestHostApp.set('trust proxy', options.http.trustProxy);\n }\n const jwksPath = getAccessiJwksPath(options);\n const isAccessiRoute = (path: string) =>\n path === \"/api/accessi\" ||\n path.startsWith(\"/api/accessi/\") ||\n isOpenApiRequestPath(path, resolvedOpenApi) ||\n (jwksPath !== undefined && path === jwksPath);\n const edgeRateLimit = options.edgeRateLimit\n ? createAccessiEdgeRateLimitMiddleware({\n ...options.edgeRateLimit,\n match: options.edgeRateLimit.match ?? ((path) => isAccessiRoute(path)),\n })\n : undefined;\n if (edgeRateLimit) {\n app.use(edgeRateLimit);\n }\n if (options.http?.securityHeaders !== false) {\n app.use((req: Request, res, next) => {\n if (!isAccessiRoute(req.path)) {\n return next();\n }\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'SAMEORIGIN');\n res.setHeader('Referrer-Policy', 'no-referrer');\n res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');\n return next();\n });\n }\n\n app.use((req: Request, res, next) => {\n if (!isAccessiRoute(req.path)) {\n return next();\n }\n\n return nestHostApp(req, res, next);\n });\n\n setupAccessiJwks(app, options);\n if (jwksPath) {\n logger.info(`JWKS accessi disponibile su: ${jwksPath}`);\n }\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n\n const nestApp = await NestFactory.create(AccessiModule.forRoot(options), nestExpressInstance, {\n bufferLogs: options.bufferLogs !== false,\n ...(options.logger ? { logger: options.logger } : {})\n });\n logger.info(\"Applicazione Nest accessi creata.\");\n\n if (options.http?.cors !== false) {\n nestApp.enableCors(options.http?.cors ?? { exposedHeaders: ['X-Total-Count'] });\n }\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n\n nestApp.setGlobalPrefix('api', {\n exclude: buildOpenApiExcludePaths(resolvedOpenApi)\n });\n\n const document = setupOpenApi(nestApp, resolvedOpenApi);\n logger.info(\"Avvio init del modulo accessi.\");\n await nestApp.init();\n logger.info(\"Init del modulo accessi completata.\");\n if (options.legacyPasswordMigrationOnStartup !== false) {\n logger.info(\"Avvio migrazione password legacy accessi.\");\n const passwordMigrationService = nestApp.get(AuthService);\n await passwordMigrationService.migrateLegacyEncryptedPasswords();\n logger.info(\"Migrazione password legacy accessi completata.\");\n }\n\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options, resolvedOpenApi);\n const elapsedMs = performance.now() - startedAt;\n logger.info(`Accessi initialized. Tempo totale bootstrap: ${elapsedMs.toFixed(2)} ms`);\n\n return { nestApp, document };\n } catch (error) {\n failAccessiAuthInitialization(error);\n logger.error(\n \"Errore in initialize AccessiModule:\",\n error instanceof Error\n ? { name: error.name, message: error.message, stack: error.stack }\n : { error: String(error) }\n );\n throw error;\n }\n}\n\nexport { AccessiModule } from \"./AccessiModule\";\nexport { AccessiIntrospectionController } from \"./Controllers/AccessiIntrospectionController\";\nexport { FederatedAuthExchangeController } from \"./federated-auth/FederatedAuthExchangeController\";\nexport type { AccessiTokenIntrospectionResult } from \"./Services/AuthService/AuthService\";\nexport { AccessiDatabaseUpdater } from \"./database-updates/AccessiDatabaseUpdater\";\nexport type { AccessiOptions, EmailOptions, JwtOptions, ExtensionFieldsOptions, PublicAuthRateLimitOptions, PublicAuthRateLimitRuleOptions } from \"./AccessiModule\";\nexport type { PublicRegistrationOptions, FederatedAuthenticationOptions, ServiceTokenOptions } from \"./AccessiModule\";\nexport type { CatalogScriptsOptions, CatalogScriptChecksumPolicy, ConsoleEntryOptions } from \"./AccessiModule\";\nexport {\n createMemoryConsoleEntryStore,\n createRedisConsoleEntryStore,\n getDefaultConsoleEntryStore\n} from './security/consoleEntry';\nexport type {\n ConsoleEntryRedisClient,\n ConsoleEntryRecord,\n ConsoleEntryStore,\n ConsoleEntryTicket,\n MemoryConsoleEntryStore,\n RedisConsoleEntryStoreOptions\n} from './security/consoleEntry';\nexport {\n createMemoryRateLimitStore,\n createRedisRateLimitStore,\n getDefaultRateLimitStore\n} from './security/rateLimitStore';\nexport type {\n MemoryRateLimitStore,\n RateLimitDecision,\n RateLimitRedisClient,\n RateLimitStore,\n RedisRateLimitStoreOptions\n} from './security/rateLimitStore';\nexport { createAccessiEdgeRateLimitMiddleware } from './security/edgeRateLimit';\nexport type {\n AccessiEdgeRateLimitMiddleware,\n AccessiEdgeRateLimitOptions\n} from './security/edgeRateLimit';\nexport {\n assertValidAccessiOptions,\n validateAccessiOptions\n} from './security/validateOptions';\nexport { auditAccessi } from './security/audit';\nexport {\n createMemorySessionRevocationStore,\n createRedisSessionRevocationStore,\n revokeAccessiUserSessions,\n isAccessiTokenRevoked,\n getSessionRevocationStore\n} from './security/sessionRevocation';\nexport type {\n MemorySessionRevocationStore,\n RedisSessionRevocationStoreOptions,\n SessionRevocationOptions,\n SessionRevocationRedisClient,\n SessionRevocationStore\n} from './security/sessionRevocation';\nexport {\n createMemoryRefreshTokenRotationStore,\n createRedisRefreshTokenRotationStore,\n getRefreshTokenRotationStore\n} from './security/refreshTokenRotation';\nexport type {\n MemoryRefreshTokenRotationStore,\n RedisRefreshTokenRotationStoreOptions,\n RefreshTokenRotationOptions,\n RefreshTokenRotationRedisClient,\n RefreshTokenRotationStore\n} from './security/refreshTokenRotation';\nexport type {\n AccessiOptionsValidationIssue,\n AccessiOptionsValidationResult\n} from './security/validateOptions';\nexport {\n AccessiCatalogMigrator,\n applyCatalogScripts,\n listPendingCatalogScripts,\n splitSqlStatements,\n analyzeCatalogScript,\n leadingStatementKeyword,\n computeCatalogScriptChecksum,\n discoverCatalogScripts\n} from \"./database-updates/AccessiCatalogMigrator\";\nexport type { CatalogMigrationReport, CatalogScriptMigration } from \"./database-updates/AccessiCatalogMigrator\";\nexport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nexport type {\n ServiceTokenMetadata,\n IssuedServiceToken,\n VerifiedServiceToken,\n IssueServiceTokenInput\n} from './Services/ServiceTokenService/ServiceTokenService';\nexport { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';\nexport type { SchemaDdlResult, SchemaDdlSection, SchemaEntitiesResult } from './Services/SchemaExportService/SchemaExportService';\nexport { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport type { BootstrapAdminResult } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport {\n getOrCreateAdminBootstrapToken,\n getAdminBootstrapToken,\n clearAdminBootstrapToken,\n isValidAdminBootstrapToken\n} from './security/adminBootstrap';\nexport { ServiceTokenGuard, RequireServiceTokenScopes, getAccessiServiceToken } from './security/serviceTokenGuard';\nexport { assertEmailConfigured, isEmailConfigured, AccessiEmailNotConfiguredError, ACCESSI_EMAIL_NOT_CONFIGURED } from './security/emailConfiguration';\nexport { buildLoginAsUrl, LOGIN_AS_QUERY_PARAM } from './security/loginAs';\nexport {\n buildAccessiJwks,\n createAccessiJwksHandler,\n getAccessiJwksPath,\n setupAccessiJwks\n} from './security/jwks';\nexport type { AccessiJwksDocument, AccessiJwksKey } from './security/jwks';\nexport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nexport type { FederatedAuthenticationResult, FederatedIdentity, FederatedProvider, VerifiedFederatedIdentity } from './federated-auth/FederatedAuthTypes';\nexport * from \"./Dtos\";\nexport {\n authorizeAccessi,\n authenticateGen\n} from \"./middleware/authenticateGen\";\nexport { accessiRequirement } from \"./middleware/accessiRequirements\";\nexport {\n registerAccessiModuleServices,\n getAccessiModuleServices,\n ACCESSI_MODULE_SERVICES_LOCALS_KEY\n} from \"./accessiRuntime\";\nexport type { AccessiModuleServices } from \"./accessiRuntime\";\nexport type {\n AccessiAuthorizationOptions,\n AccessiRequirementNode,\n AccessiCustomRequirementContext,\n AccessiCustomRequirementHandler\n} from \"./middleware/accessiRequirements\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accessi-module/index.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport { AccessiModule, AccessiOptions } from \"./AccessiModule\";\nimport { Logger } from \"../Logger\";\nimport { AuthService } from \"./Services/AuthService/AuthService\";\nimport {\n buildOpenApiExcludePaths,\n isOpenApiRequestPath,\n resolveAccessiOpenApiOptions,\n setupOpenApi\n} from \"../openapi/OpenApiConfig\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessiRuntime\";\nimport { logAccessiStartup } from \"./startupLog\";\nimport { createAccessiValidationPipe } from './security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './security/AccessiHttpExceptionFilter';\nimport { assertEmailConfigured } from './security/emailConfiguration';\nimport { getAccessiJwksPath, setupAccessiJwks } from './security/jwks';\nimport { createAccessiEdgeRateLimitMiddleware } from './security/edgeRateLimit';\n\n/** Esito del bootstrap Express di Accessi. */\nexport interface AccessiModuleInitializationResult {\n /** Sotto-app Nest che serve `/api/accessi/*` e la documentazione. */\n nestApp: INestApplication;\n /** Documento OpenAPI generato, mergiabile nel documento unico dell'host. */\n document: OpenAPIObject;\n}\n\nfunction describeDatabaseTarget(options: AccessiOptions): string {\n const dbOptions = options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n\n return `${dbOptions.host ?? \"?\"}:${dbOptions.port ?? \"?\"} -> ${dbOptions.database ?? \"?\"} as ${dbOptions.user ?? \"?\"}`;\n}\n\n/**\n * Monta il modulo Accessi in un'applicazione Express gia esistente.\n *\n * Il bootstrap crea un host Nest isolato per le rotte `/api/accessi`, inizializza la\n * documentazione OpenAPI, validazione ed error handler, quindi rende disponibile\n * `authorizeAccessi` al resto dell'app.\n * Attendere questa Promise prima di aprire la porta HTTP: durante il bootstrap il middleware\n * restituisce `ACCESSI_AUTH_INITIALIZING` invece di autorizzare richieste parzialmente inizializzate.\n *\n * @param app Applicazione Express ospitante. Non viene sostituita ne vengono intercettate rotte estranee ad Accessi.\n * @param options Configurazione riservata del modulo; proviene normalmente da variabili d'ambiente o secret manager.\n * @returns Il sotto-app Nest e il documento OpenAPI generato (utile per il merge nell'host).\n */\nexport async function initializeAccessiModule(\n app: Application,\n options: AccessiOptions\n): Promise<AccessiModuleInitializationResult> {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n const logger: Logger = new Logger(\"initializeAccessiModule\");\n beginAccessiAuthInitialization();\n const startedAt = performance.now();\n const resolvedOpenApi = resolveAccessiOpenApiOptions(options.openapi);\n\n try {\n logger.info(\"Inizializzazione modulo accessi avviata.\");\n logger.info(`Target database accessi: ${describeDatabaseTarget(options)}`);\n logger.info(\n `Configurazione accessi: autoUpdateDatabase=${options.autoUpdateDatabase !== false}, legacyPasswordMigrationOnStartup=${options.legacyPasswordMigrationOnStartup !== false}`\n );\n const nestHostApp = express();\n if (options.http?.trustProxy !== undefined) {\n app.set('trust proxy', options.http.trustProxy);\n nestHostApp.set('trust proxy', options.http.trustProxy);\n }\n const jwksPath = getAccessiJwksPath(options);\n const isAccessiRoute = (path: string) =>\n path === \"/api/accessi\" ||\n path.startsWith(\"/api/accessi/\") ||\n isOpenApiRequestPath(path, resolvedOpenApi) ||\n (jwksPath !== undefined && path === jwksPath);\n const edgeRateLimit = options.edgeRateLimit\n ? createAccessiEdgeRateLimitMiddleware({\n ...options.edgeRateLimit,\n match: options.edgeRateLimit.match ?? ((path) => isAccessiRoute(path)),\n })\n : undefined;\n if (edgeRateLimit) {\n app.use(edgeRateLimit);\n }\n if (options.http?.securityHeaders !== false) {\n app.use((req: Request, res, next) => {\n if (!isAccessiRoute(req.path)) {\n return next();\n }\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'SAMEORIGIN');\n res.setHeader('Referrer-Policy', 'no-referrer');\n res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');\n return next();\n });\n }\n\n app.use((req: Request, res, next) => {\n if (!isAccessiRoute(req.path)) {\n return next();\n }\n\n return nestHostApp(req, res, next);\n });\n\n setupAccessiJwks(app, options);\n if (jwksPath) {\n logger.info(`JWKS accessi disponibile su: ${jwksPath}`);\n }\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n\n const nestApp = await NestFactory.create(AccessiModule.forRoot(options), nestExpressInstance, {\n bufferLogs: options.bufferLogs !== false,\n ...(options.logger ? { logger: options.logger } : {})\n });\n logger.info(\"Applicazione Nest accessi creata.\");\n\n if (options.http?.cors !== false) {\n nestApp.enableCors(options.http?.cors ?? { exposedHeaders: ['X-Total-Count'] });\n }\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n\n nestApp.setGlobalPrefix('api', {\n exclude: buildOpenApiExcludePaths(resolvedOpenApi)\n });\n\n const document = setupOpenApi(nestApp, resolvedOpenApi);\n logger.info(\"Avvio init del modulo accessi.\");\n await nestApp.init();\n logger.info(\"Init del modulo accessi completata.\");\n if (options.legacyPasswordMigrationOnStartup !== false) {\n logger.info(\"Avvio migrazione password legacy accessi.\");\n const passwordMigrationService = nestApp.get(AuthService);\n await passwordMigrationService.migrateLegacyEncryptedPasswords();\n logger.info(\"Migrazione password legacy accessi completata.\");\n }\n\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options, resolvedOpenApi);\n const elapsedMs = performance.now() - startedAt;\n logger.info(`Accessi initialized. Tempo totale bootstrap: ${elapsedMs.toFixed(2)} ms`);\n\n return { nestApp, document };\n } catch (error) {\n failAccessiAuthInitialization(error);\n logger.error(\n \"Errore in initialize AccessiModule:\",\n error instanceof Error\n ? { name: error.name, message: error.message, stack: error.stack }\n : { error: String(error) }\n );\n throw error;\n }\n}\n\nexport { AccessiModule } from \"./AccessiModule\";\nexport { AccessiIntrospectionController } from \"./Controllers/AccessiIntrospectionController\";\nexport { FederatedAuthExchangeController } from \"./federated-auth/FederatedAuthExchangeController\";\nexport type { AccessiTokenIntrospectionResult } from \"./Services/AuthService/AuthService\";\nexport { AccessiDatabaseUpdater } from \"./database-updates/AccessiDatabaseUpdater\";\nexport type { AccessiOptions, EmailOptions, JwtOptions, ExtensionFieldsOptions, PublicAuthRateLimitOptions, PublicAuthRateLimitRuleOptions } from \"./AccessiModule\";\nexport type { PublicRegistrationOptions, FederatedAuthenticationOptions, ServiceTokenOptions } from \"./AccessiModule\";\nexport type { CatalogScriptsOptions, CatalogScriptChecksumPolicy, ConsoleEntryOptions } from \"./AccessiModule\";\nexport {\n createMemoryConsoleEntryStore,\n createRedisConsoleEntryStore,\n getDefaultConsoleEntryStore\n} from './security/consoleEntry';\nexport type {\n ConsoleEntryRedisClient,\n ConsoleEntryRecord,\n ConsoleEntryStore,\n ConsoleEntryTicket,\n MemoryConsoleEntryStore,\n RedisConsoleEntryStoreOptions\n} from './security/consoleEntry';\nexport {\n createMemoryRateLimitStore,\n createRedisRateLimitStore,\n getDefaultRateLimitStore\n} from './security/rateLimitStore';\nexport type {\n MemoryRateLimitStore,\n RateLimitDecision,\n RateLimitRedisClient,\n RateLimitStore,\n RedisRateLimitStoreOptions\n} from './security/rateLimitStore';\nexport { createAccessiEdgeRateLimitMiddleware } from './security/edgeRateLimit';\nexport type {\n AccessiEdgeRateLimitMiddleware,\n AccessiEdgeRateLimitOptions\n} from './security/edgeRateLimit';\nexport {\n assertValidAccessiOptions,\n validateAccessiOptions\n} from './security/validateOptions';\nexport { auditAccessi } from './security/audit';\nexport {\n createMemorySessionRevocationStore,\n createRedisSessionRevocationStore,\n revokeAccessiUserSessions,\n revokeAccessiUserSessionsIfConfigured,\n isAccessiTokenRevoked,\n getSessionRevocationStore\n} from './security/sessionRevocation';\nexport type {\n MemorySessionRevocationStore,\n RedisSessionRevocationStoreOptions,\n SessionRevocationOptions,\n SessionRevocationRedisClient,\n SessionRevocationStore\n} from './security/sessionRevocation';\nexport {\n createMemoryRefreshTokenRotationStore,\n createRedisRefreshTokenRotationStore,\n consumeRefreshToken,\n getRefreshTokenRotationStore,\n isRefreshTokenFamilyRevoked,\n revokeRefreshTokenFamily\n} from './security/refreshTokenRotation';\nexport type {\n MemoryRefreshTokenRotationStore,\n RedisRefreshTokenRotationStoreOptions,\n RefreshTokenConsumeResult,\n RefreshTokenRotationOptions,\n RefreshTokenRotationRedisClient,\n RefreshTokenRotationStore\n} from './security/refreshTokenRotation';\nexport {\n createMemoryAccessTokenRevocationStore,\n createRedisAccessTokenRevocationStore,\n getAccessTokenRevocationStore,\n isAccessiAccessTokenRevoked,\n revokeAccessiAccessToken,\n revokeAccessiAccessTokenIfConfigured\n} from './security/accessTokenRevocation';\nexport type {\n AccessTokenRevocationOptions,\n AccessTokenRevocationRedisClient,\n AccessTokenRevocationStore,\n MemoryAccessTokenRevocationStore,\n RedisAccessTokenRevocationStoreOptions\n} from './security/accessTokenRevocation';\nexport type {\n AccessiOptionsValidationIssue,\n AccessiOptionsValidationResult\n} from './security/validateOptions';\nexport {\n AccessiCatalogMigrator,\n applyCatalogScripts,\n listPendingCatalogScripts,\n splitSqlStatements,\n analyzeCatalogScript,\n leadingStatementKeyword,\n computeCatalogScriptChecksum,\n discoverCatalogScripts\n} from \"./database-updates/AccessiCatalogMigrator\";\nexport type { CatalogMigrationReport, CatalogScriptMigration } from \"./database-updates/AccessiCatalogMigrator\";\nexport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nexport type {\n ServiceTokenMetadata,\n IssuedServiceToken,\n VerifiedServiceToken,\n IssueServiceTokenInput\n} from './Services/ServiceTokenService/ServiceTokenService';\nexport { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';\nexport type { SchemaDdlResult, SchemaDdlSection, SchemaEntitiesResult } from './Services/SchemaExportService/SchemaExportService';\nexport { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport type { BootstrapAdminResult } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport {\n getOrCreateAdminBootstrapToken,\n getAdminBootstrapToken,\n clearAdminBootstrapToken,\n isValidAdminBootstrapToken\n} from './security/adminBootstrap';\nexport { ServiceTokenGuard, RequireServiceTokenScopes, getAccessiServiceToken } from './security/serviceTokenGuard';\nexport { assertEmailConfigured, isEmailConfigured, AccessiEmailNotConfiguredError, ACCESSI_EMAIL_NOT_CONFIGURED } from './security/emailConfiguration';\nexport { buildLoginAsUrl, LOGIN_AS_QUERY_PARAM } from './security/loginAs';\nexport {\n buildAccessiJwks,\n createAccessiJwksHandler,\n getAccessiJwksPath,\n setupAccessiJwks\n} from './security/jwks';\nexport type { AccessiJwksDocument, AccessiJwksKey } from './security/jwks';\nexport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nexport type { FederatedAuthenticationResult, FederatedIdentity, FederatedProvider, VerifiedFederatedIdentity } from './federated-auth/FederatedAuthTypes';\nexport * from \"./Dtos\";\nexport {\n authorizeAccessi,\n authenticateGen\n} from \"./middleware/authenticateGen\";\nexport { accessiRequirement } from \"./middleware/accessiRequirements\";\nexport {\n registerAccessiModuleServices,\n getAccessiModuleServices,\n ACCESSI_MODULE_SERVICES_LOCALS_KEY\n} from \"./accessiRuntime\";\nexport type { AccessiModuleServices } from \"./accessiRuntime\";\nexport type {\n AccessiAuthorizationOptions,\n AccessiRequirementNode,\n AccessiCustomRequirementContext,\n AccessiCustomRequirementHandler\n} from \"./middleware/accessiRequirements\";\n"]}
@@ -0,0 +1,47 @@
1
+ import type { AccessiOptions } from '../AccessiModule';
2
+ /**
3
+ * Revoca dei singoli access token tramite `jti`.
4
+ *
5
+ * A differenza della revoca per utente, questa annulla un solo token senza
6
+ * toccare le altre sessioni. La chiave scade al massimo a `exp` del token, cosi
7
+ * la memoria usata resta limitata alla durata dei token emessi.
8
+ */
9
+ export interface AccessTokenRevocationStore {
10
+ revoke(jti: string, expiresAtMs: number): Promise<void>;
11
+ isRevoked(jti: string): Promise<boolean>;
12
+ /** Health check opzionale per readiness del servizio. */
13
+ healthCheck?(): Promise<boolean>;
14
+ }
15
+ export interface AccessTokenRevocationOptions {
16
+ /** Default: disabilitata se non viene fornito uno store. */
17
+ enabled?: boolean;
18
+ /** Store condiviso (es. Redis) per revocare i token tra istanze. */
19
+ store?: AccessTokenRevocationStore;
20
+ /** TTL di default quando `exp` non e noto. Default: 24 ore. */
21
+ ttlMs?: number;
22
+ }
23
+ export interface MemoryAccessTokenRevocationStore extends AccessTokenRevocationStore {
24
+ clear(): void;
25
+ }
26
+ export declare function createMemoryAccessTokenRevocationStore(): MemoryAccessTokenRevocationStore;
27
+ export interface AccessTokenRevocationRedisClient {
28
+ eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;
29
+ }
30
+ export interface RedisAccessTokenRevocationStoreOptions {
31
+ client: AccessTokenRevocationRedisClient;
32
+ /** Default: `accessi:revoked-token:`. */
33
+ keyPrefix?: string;
34
+ /** Default: 24 ore. */
35
+ ttlMs?: number;
36
+ }
37
+ /** Store Redis per la revoca dei singoli access token. */
38
+ export declare function createRedisAccessTokenRevocationStore(options: RedisAccessTokenRevocationStoreOptions): AccessTokenRevocationStore;
39
+ export declare function getAccessTokenRevocationStore(options: AccessiOptions): AccessTokenRevocationStore | undefined;
40
+ /** True se il `jti` del token risulta revocato. */
41
+ export declare function isAccessiAccessTokenRevoked(options: AccessiOptions, payload: Record<string, unknown>): Promise<boolean>;
42
+ /** Revoca un singolo access token. Richiede uno store configurato. */
43
+ export declare function revokeAccessiAccessToken(options: AccessiOptions, jti: string, expiresAtMs?: number): Promise<void>;
44
+ /** Variante per i flussi applicativi: no-op se la revoca non e configurata. */
45
+ export declare function revokeAccessiAccessTokenIfConfigured(options: AccessiOptions, jti: string, expiresAtMs?: number): Promise<boolean>;
46
+ /** Solo per test: azzera lo store in memoria di default. */
47
+ export declare function clearDefaultAccessTokenRevocations(): void;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createMemoryAccessTokenRevocationStore = createMemoryAccessTokenRevocationStore;
13
+ exports.createRedisAccessTokenRevocationStore = createRedisAccessTokenRevocationStore;
14
+ exports.getAccessTokenRevocationStore = getAccessTokenRevocationStore;
15
+ exports.isAccessiAccessTokenRevoked = isAccessiAccessTokenRevoked;
16
+ exports.revokeAccessiAccessToken = revokeAccessiAccessToken;
17
+ exports.revokeAccessiAccessTokenIfConfigured = revokeAccessiAccessTokenIfConfigured;
18
+ exports.clearDefaultAccessTokenRevocations = clearDefaultAccessTokenRevocations;
19
+ const REVOKE_SCRIPT = "if tonumber(ARGV[2]) > 0 then return redis.call('SET', KEYS[1], '1', 'PX', ARGV[2]) else return redis.call('SET', KEYS[1], '1') end";
20
+ const IS_REVOKED_SCRIPT = "return redis.call('EXISTS', KEYS[1])";
21
+ function createMemoryAccessTokenRevocationStore() {
22
+ const revoked = new Map();
23
+ const prune = (now) => {
24
+ for (const [jti, expiresAt] of revoked) {
25
+ if (expiresAt <= now)
26
+ revoked.delete(jti);
27
+ }
28
+ };
29
+ return {
30
+ revoke(jti, expiresAtMs) {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ const now = Date.now();
33
+ prune(now);
34
+ revoked.set(jti, Math.max(expiresAtMs, now + 1));
35
+ });
36
+ },
37
+ isRevoked(jti) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ const expiresAt = revoked.get(jti);
40
+ if (expiresAt === undefined)
41
+ return false;
42
+ if (expiresAt <= Date.now()) {
43
+ revoked.delete(jti);
44
+ return false;
45
+ }
46
+ return true;
47
+ });
48
+ },
49
+ healthCheck() {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ return true;
52
+ });
53
+ },
54
+ clear() {
55
+ revoked.clear();
56
+ },
57
+ };
58
+ }
59
+ /** Store Redis per la revoca dei singoli access token. */
60
+ function createRedisAccessTokenRevocationStore(options) {
61
+ var _a, _b;
62
+ if (!(options === null || options === void 0 ? void 0 : options.client) || typeof options.client.eval !== 'function') {
63
+ throw new Error('Redis access token revocation store: client.eval non configurato.');
64
+ }
65
+ const keyPrefix = ((_a = options.keyPrefix) !== null && _a !== void 0 ? _a : 'accessi:revoked-token:').replace(/:+$/, ':');
66
+ const ttlMs = Math.max(0, Math.trunc((_b = options.ttlMs) !== null && _b !== void 0 ? _b : 24 * 60 * 60 * 1000));
67
+ return {
68
+ revoke(jti, expiresAtMs) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const ttl = Number.isFinite(expiresAtMs)
71
+ ? Math.max(0, Math.trunc(expiresAtMs - Date.now()))
72
+ : ttlMs;
73
+ yield options.client.eval(REVOKE_SCRIPT, 1, `${keyPrefix}${jti}`, ttl);
74
+ });
75
+ },
76
+ isRevoked(jti) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const result = yield options.client.eval(IS_REVOKED_SCRIPT, 1, `${keyPrefix}${jti}`);
79
+ return Number(result) > 0;
80
+ });
81
+ },
82
+ healthCheck() {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ try {
85
+ const result = yield options.client.eval("return redis.call('PING')", 0);
86
+ return String(result).toUpperCase() === 'PONG';
87
+ }
88
+ catch (_a) {
89
+ return false;
90
+ }
91
+ });
92
+ },
93
+ };
94
+ }
95
+ const defaultMemoryStore = createMemoryAccessTokenRevocationStore();
96
+ function getAccessTokenRevocationStore(options) {
97
+ var _a, _b, _c;
98
+ if (((_a = options.accessTokenRevocation) === null || _a === void 0 ? void 0 : _a.enabled) === false)
99
+ return undefined;
100
+ if ((_b = options.accessTokenRevocation) === null || _b === void 0 ? void 0 : _b.store)
101
+ return options.accessTokenRevocation.store;
102
+ return ((_c = options.accessTokenRevocation) === null || _c === void 0 ? void 0 : _c.enabled) === true ? defaultMemoryStore : undefined;
103
+ }
104
+ /** True se il `jti` del token risulta revocato. */
105
+ function isAccessiAccessTokenRevoked(options, payload) {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ const store = getAccessTokenRevocationStore(options);
108
+ if (!store)
109
+ return false;
110
+ const jti = typeof payload.jti === 'string' ? payload.jti : undefined;
111
+ if (!jti)
112
+ return false;
113
+ return store.isRevoked(jti);
114
+ });
115
+ }
116
+ /** Revoca un singolo access token. Richiede uno store configurato. */
117
+ function revokeAccessiAccessToken(options, jti, expiresAtMs) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ var _a, _b;
120
+ const store = getAccessTokenRevocationStore(options);
121
+ if (!store)
122
+ throw new Error('Access token revocation store non configurato.');
123
+ if (typeof jti !== 'string' || jti.trim() === '') {
124
+ throw new Error('jti non valido.');
125
+ }
126
+ const ttlMs = Math.max(0, Math.trunc((_b = (_a = options.accessTokenRevocation) === null || _a === void 0 ? void 0 : _a.ttlMs) !== null && _b !== void 0 ? _b : 24 * 60 * 60 * 1000));
127
+ yield store.revoke(jti, expiresAtMs !== null && expiresAtMs !== void 0 ? expiresAtMs : Date.now() + ttlMs);
128
+ });
129
+ }
130
+ /** Variante per i flussi applicativi: no-op se la revoca non e configurata. */
131
+ function revokeAccessiAccessTokenIfConfigured(options, jti, expiresAtMs) {
132
+ return __awaiter(this, void 0, void 0, function* () {
133
+ if (!getAccessTokenRevocationStore(options))
134
+ return false;
135
+ yield revokeAccessiAccessToken(options, jti, expiresAtMs);
136
+ return true;
137
+ });
138
+ }
139
+ /** Solo per test: azzera lo store in memoria di default. */
140
+ function clearDefaultAccessTokenRevocations() {
141
+ defaultMemoryStore.clear();
142
+ }
143
+ //# sourceMappingURL=accessTokenRevocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessTokenRevocation.js","sourceRoot":"","sources":["../../../src/accessi-module/security/accessTokenRevocation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccessiOptions } from '../AccessiModule';\n\n/**\n * Revoca dei singoli access token tramite `jti`.\n *\n * A differenza della revoca per utente, questa annulla un solo token senza\n * toccare le altre sessioni. La chiave scade al massimo a `exp` del token, cosi\n * la memoria usata resta limitata alla durata dei token emessi.\n */\nexport interface AccessTokenRevocationStore {\n revoke(jti: string, expiresAtMs: number): Promise<void>;\n isRevoked(jti: string): Promise<boolean>;\n /** Health check opzionale per readiness del servizio. */\n healthCheck?(): Promise<boolean>;\n}\n\nexport interface AccessTokenRevocationOptions {\n /** Default: disabilitata se non viene fornito uno store. */\n enabled?: boolean;\n /** Store condiviso (es. Redis) per revocare i token tra istanze. */\n store?: AccessTokenRevocationStore;\n /** TTL di default quando `exp` non e noto. Default: 24 ore. */\n ttlMs?: number;\n}\n\nexport interface MemoryAccessTokenRevocationStore extends AccessTokenRevocationStore {\n clear(): void;\n}\n\nconst REVOKE_SCRIPT =\n \"if tonumber(ARGV[2]) > 0 then return redis.call('SET', KEYS[1], '1', 'PX', ARGV[2]) else return redis.call('SET', KEYS[1], '1') end\";\nconst IS_REVOKED_SCRIPT = \"return redis.call('EXISTS', KEYS[1])\";\n\nexport function createMemoryAccessTokenRevocationStore(): MemoryAccessTokenRevocationStore {\n const revoked = new Map<string, number>();\n const prune = (now: number) => {\n for (const [jti, expiresAt] of revoked) {\n if (expiresAt <= now) revoked.delete(jti);\n }\n };\n return {\n async revoke(jti, expiresAtMs) {\n const now = Date.now();\n prune(now);\n revoked.set(jti, Math.max(expiresAtMs, now + 1));\n },\n async isRevoked(jti) {\n const expiresAt = revoked.get(jti);\n if (expiresAt === undefined) return false;\n if (expiresAt <= Date.now()) {\n revoked.delete(jti);\n return false;\n }\n return true;\n },\n async healthCheck() {\n return true;\n },\n clear() {\n revoked.clear();\n },\n };\n}\n\nexport interface AccessTokenRevocationRedisClient {\n eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;\n}\n\nexport interface RedisAccessTokenRevocationStoreOptions {\n client: AccessTokenRevocationRedisClient;\n /** Default: `accessi:revoked-token:`. */\n keyPrefix?: string;\n /** Default: 24 ore. */\n ttlMs?: number;\n}\n\n/** Store Redis per la revoca dei singoli access token. */\nexport function createRedisAccessTokenRevocationStore(\n options: RedisAccessTokenRevocationStoreOptions,\n): AccessTokenRevocationStore {\n if (!options?.client || typeof options.client.eval !== 'function') {\n throw new Error('Redis access token revocation store: client.eval non configurato.');\n }\n\n const keyPrefix = (options.keyPrefix ?? 'accessi:revoked-token:').replace(/:+$/, ':');\n const ttlMs = Math.max(0, Math.trunc(options.ttlMs ?? 24 * 60 * 60 * 1000));\n\n return {\n async revoke(jti, expiresAtMs) {\n const ttl = Number.isFinite(expiresAtMs)\n ? Math.max(0, Math.trunc(expiresAtMs - Date.now()))\n : ttlMs;\n await options.client.eval(REVOKE_SCRIPT, 1, `${keyPrefix}${jti}`, ttl);\n },\n\n async isRevoked(jti) {\n const result = await options.client.eval(IS_REVOKED_SCRIPT, 1, `${keyPrefix}${jti}`);\n return Number(result) > 0;\n },\n\n async healthCheck() {\n try {\n const result = await options.client.eval(\"return redis.call('PING')\", 0);\n return String(result).toUpperCase() === 'PONG';\n } catch {\n return false;\n }\n },\n };\n}\n\nconst defaultMemoryStore = createMemoryAccessTokenRevocationStore();\n\nexport function getAccessTokenRevocationStore(\n options: AccessiOptions,\n): AccessTokenRevocationStore | undefined {\n if (options.accessTokenRevocation?.enabled === false) return undefined;\n if (options.accessTokenRevocation?.store) return options.accessTokenRevocation.store;\n return options.accessTokenRevocation?.enabled === true ? defaultMemoryStore : undefined;\n}\n\n/** True se il `jti` del token risulta revocato. */\nexport async function isAccessiAccessTokenRevoked(\n options: AccessiOptions,\n payload: Record<string, unknown>,\n): Promise<boolean> {\n const store = getAccessTokenRevocationStore(options);\n if (!store) return false;\n const jti = typeof payload.jti === 'string' ? payload.jti : undefined;\n if (!jti) return false;\n return store.isRevoked(jti);\n}\n\n/** Revoca un singolo access token. Richiede uno store configurato. */\nexport async function revokeAccessiAccessToken(\n options: AccessiOptions,\n jti: string,\n expiresAtMs?: number,\n): Promise<void> {\n const store = getAccessTokenRevocationStore(options);\n if (!store) throw new Error('Access token revocation store non configurato.');\n if (typeof jti !== 'string' || jti.trim() === '') {\n throw new Error('jti non valido.');\n }\n const ttlMs = Math.max(0, Math.trunc(options.accessTokenRevocation?.ttlMs ?? 24 * 60 * 60 * 1000));\n await store.revoke(jti, expiresAtMs ?? Date.now() + ttlMs);\n}\n\n/** Variante per i flussi applicativi: no-op se la revoca non e configurata. */\nexport async function revokeAccessiAccessTokenIfConfigured(\n options: AccessiOptions,\n jti: string,\n expiresAtMs?: number,\n): Promise<boolean> {\n if (!getAccessTokenRevocationStore(options)) return false;\n await revokeAccessiAccessToken(options, jti, expiresAtMs);\n return true;\n}\n\n/** Solo per test: azzera lo store in memoria di default. */\nexport function clearDefaultAccessTokenRevocations(): void {\n defaultMemoryStore.clear();\n}\n"]}
@@ -7,14 +7,39 @@ import type { AccessiOptions } from '../AccessiModule';
7
7
  */
8
8
  export interface RefreshTokenRotationStore {
9
9
  consume(jti: string, expiresAtMs: number): Promise<boolean>;
10
+ /**
11
+ * Come `consume`, ma restituisce anche quando il token era gia stato
12
+ * consumato: serve a distinguere un retry concorrente da un possibile furto.
13
+ */
14
+ consumeWithMetadata?(jti: string, expiresAtMs: number): Promise<RefreshTokenConsumeResult>;
15
+ /** Revoca l'intera famiglia di refresh token (stesso login). */
16
+ revokeFamily?(familyId: string, expiresAtMs: number): Promise<void>;
17
+ /** True se la famiglia e stata revocata per riuso sospetto. */
18
+ isFamilyRevoked?(familyId: string): Promise<boolean>;
10
19
  /** Health check opzionale per readiness del servizio. */
11
20
  healthCheck?(): Promise<boolean>;
12
21
  }
22
+ /** Esito del consumo atomico di un refresh token. */
23
+ export interface RefreshTokenConsumeResult {
24
+ consumed: boolean;
25
+ /** Quando il token era gia stato consumato, il timestamp del primo consumo. */
26
+ consumedAtMs?: number;
27
+ }
13
28
  export interface RefreshTokenRotationOptions {
14
29
  /** Default: disabilitata se non viene fornito uno store. */
15
30
  enabled?: boolean;
16
31
  /** Store condiviso (es. Redis) per il monouso tra istanze. */
17
32
  store?: RefreshTokenRotationStore;
33
+ /**
34
+ * Finestra entro cui il riuso di un token appena consumato e trattato come
35
+ * retry concorrente (nessuna revoca famiglia). Default: 10000 ms.
36
+ */
37
+ reuseGraceMs?: number;
38
+ /**
39
+ * Se true, dopo un riuso rilevato revoca anche tutte le sessioni utente.
40
+ * Default: false, per non disconnettere dispositivi legittimi.
41
+ */
42
+ revokeUserSessionsOnReuse?: boolean;
18
43
  }
19
44
  export interface MemoryRefreshTokenRotationStore extends RefreshTokenRotationStore {
20
45
  clear(): void;
@@ -27,14 +52,29 @@ export interface RedisRefreshTokenRotationStoreOptions {
27
52
  client: RefreshTokenRotationRedisClient;
28
53
  /** Default: `accessi:refresh-used:`. */
29
54
  keyPrefix?: string;
55
+ /** Default: `accessi:refresh-family-revoked:`. */
56
+ familyKeyPrefix?: string;
30
57
  }
31
58
  /**
32
59
  * Store Redis per refresh token monouso.
33
60
  *
34
- * Usa `SET ... PX ... NX` atomico: due refresh concorrenti dello stesso token
35
- * producono un solo successo.
61
+ * Il consumo e atomico: due refresh concorrenti dello stesso token producono
62
+ * un solo successo. La famiglia viene revocata con una chiave separata che
63
+ * scade insieme ai token che ne fanno parte.
36
64
  */
37
65
  export declare function createRedisRefreshTokenRotationStore(options: RedisRefreshTokenRotationStoreOptions): RefreshTokenRotationStore;
38
66
  export declare function getRefreshTokenRotationStore(options: AccessiOptions): RefreshTokenRotationStore | undefined;
67
+ /**
68
+ * True se la famiglia di refresh token e stata revocata. Ritorna false se lo
69
+ * store non supporta le famiglie (implementazioni legacy).
70
+ */
71
+ export declare function isRefreshTokenFamilyRevoked(options: AccessiOptions, familyId: unknown): Promise<boolean>;
72
+ /**
73
+ * Revoca la famiglia di refresh token fino alla scadenza indicata. Ritorna
74
+ * false se lo store non supporta le famiglie.
75
+ */
76
+ export declare function revokeRefreshTokenFamily(options: AccessiOptions, familyId: string, expiresAtMs: number): Promise<boolean>;
77
+ /** Consumo atomico con metadati, se lo store li supporta. */
78
+ export declare function consumeRefreshToken(options: AccessiOptions, jti: string, expiresAtMs: number): Promise<RefreshTokenConsumeResult | undefined>;
39
79
  /** Solo per test: azzera lo store in memoria di default. */
40
80
  export declare function clearDefaultRefreshTokenRotations(): void;
@@ -12,23 +12,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.createMemoryRefreshTokenRotationStore = createMemoryRefreshTokenRotationStore;
13
13
  exports.createRedisRefreshTokenRotationStore = createRedisRefreshTokenRotationStore;
14
14
  exports.getRefreshTokenRotationStore = getRefreshTokenRotationStore;
15
+ exports.isRefreshTokenFamilyRevoked = isRefreshTokenFamilyRevoked;
16
+ exports.revokeRefreshTokenFamily = revokeRefreshTokenFamily;
17
+ exports.consumeRefreshToken = consumeRefreshToken;
15
18
  exports.clearDefaultRefreshTokenRotations = clearDefaultRefreshTokenRotations;
16
19
  const CONSUME_SCRIPT = `
17
20
  local ttl = tonumber(ARGV[2])
18
21
  if not ttl or ttl <= 0 then return nil end
19
- return redis.call('SET', KEYS[1], '1', 'PX', ttl, 'NX')
22
+ local stored = redis.call('GET', KEYS[1])
23
+ if not stored then
24
+ redis.call('SET', KEYS[1], ARGV[1], 'PX', ttl)
25
+ return {1, 0}
26
+ end
27
+ return {0, tonumber(stored) or 0}
20
28
  `;
29
+ const REVOKE_FAMILY_SCRIPT = "if tonumber(ARGV[1]) > 0 then return redis.call('SET', KEYS[1], '1', 'PX', ARGV[1]) else return redis.call('SET', KEYS[1], '1') end";
30
+ const IS_FAMILY_REVOKED_SCRIPT = "return redis.call('EXISTS', KEYS[1])";
31
+ function parseConsumeResult(result) {
32
+ if (Array.isArray(result)) {
33
+ const consumed = Number(result[0]) === 1 || result[0] === true;
34
+ const consumedAtMs = Number(result[1]);
35
+ return Object.assign({ consumed }, (Number.isFinite(consumedAtMs) && consumedAtMs > 0 ? { consumedAtMs } : {}));
36
+ }
37
+ // Client minimali (o store precedenti) rispondono 'OK' / null.
38
+ return { consumed: result !== null && result !== undefined && result !== false };
39
+ }
21
40
  function createMemoryRefreshTokenRotationStore() {
22
41
  const used = new Map();
42
+ const revokedFamilies = new Map();
43
+ const prune = (now) => {
44
+ for (const [jti, entry] of used) {
45
+ if (entry.expiresAtMs <= now)
46
+ used.delete(jti);
47
+ }
48
+ for (const [familyId, expiresAt] of revokedFamilies) {
49
+ if (expiresAt <= now)
50
+ revokedFamilies.delete(familyId);
51
+ }
52
+ };
53
+ const consumeWithMetadata = (jti, expiresAtMs) => __awaiter(this, void 0, void 0, function* () {
54
+ const now = Date.now();
55
+ prune(now);
56
+ const existing = used.get(jti);
57
+ if (existing && existing.expiresAtMs > now) {
58
+ return { consumed: false, consumedAtMs: existing.consumedAtMs };
59
+ }
60
+ used.set(jti, { expiresAtMs, consumedAtMs: now });
61
+ return { consumed: true };
62
+ });
23
63
  return {
24
64
  consume(jti, expiresAtMs) {
25
65
  return __awaiter(this, void 0, void 0, function* () {
26
- const now = Date.now();
27
- const existing = used.get(jti);
28
- if (existing !== undefined && existing > now) {
66
+ return (yield consumeWithMetadata(jti, expiresAtMs)).consumed;
67
+ });
68
+ },
69
+ consumeWithMetadata,
70
+ revokeFamily(familyId, expiresAtMs) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ revokedFamilies.set(familyId, Math.max(expiresAtMs, Date.now() + 1));
73
+ });
74
+ },
75
+ isFamilyRevoked(familyId) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ const expiresAt = revokedFamilies.get(familyId);
78
+ if (expiresAt === undefined)
79
+ return false;
80
+ if (expiresAt <= Date.now()) {
81
+ revokedFamilies.delete(familyId);
29
82
  return false;
30
83
  }
31
- used.set(jti, expiresAtMs);
32
84
  return true;
33
85
  });
34
86
  },
@@ -39,27 +91,49 @@ function createMemoryRefreshTokenRotationStore() {
39
91
  },
40
92
  clear() {
41
93
  used.clear();
94
+ revokedFamilies.clear();
42
95
  },
43
96
  };
44
97
  }
45
98
  /**
46
99
  * Store Redis per refresh token monouso.
47
100
  *
48
- * Usa `SET ... PX ... NX` atomico: due refresh concorrenti dello stesso token
49
- * producono un solo successo.
101
+ * Il consumo e atomico: due refresh concorrenti dello stesso token producono
102
+ * un solo successo. La famiglia viene revocata con una chiave separata che
103
+ * scade insieme ai token che ne fanno parte.
50
104
  */
51
105
  function createRedisRefreshTokenRotationStore(options) {
52
- var _a;
106
+ var _a, _b;
53
107
  if (!(options === null || options === void 0 ? void 0 : options.client) || typeof options.client.eval !== 'function') {
54
108
  throw new Error('Redis refresh rotation store: client.eval non configurato.');
55
109
  }
56
110
  const keyPrefix = ((_a = options.keyPrefix) !== null && _a !== void 0 ? _a : 'accessi:refresh-used:').replace(/:+$/, ':');
111
+ const familyKeyPrefix = ((_b = options.familyKeyPrefix) !== null && _b !== void 0 ? _b : 'accessi:refresh-family-revoked:').replace(/:+$/, ':');
57
112
  return {
58
113
  consume(jti, expiresAtMs) {
59
114
  return __awaiter(this, void 0, void 0, function* () {
60
115
  const ttlMs = Math.max(1, Math.trunc(expiresAtMs - Date.now()));
61
- const result = yield options.client.eval(CONSUME_SCRIPT, 1, `${keyPrefix}${jti}`, ttlMs);
62
- return result !== null && result !== undefined && result !== false;
116
+ const result = yield options.client.eval(CONSUME_SCRIPT, 1, `${keyPrefix}${jti}`, Date.now(), ttlMs);
117
+ return parseConsumeResult(result).consumed;
118
+ });
119
+ },
120
+ consumeWithMetadata(jti, expiresAtMs) {
121
+ return __awaiter(this, void 0, void 0, function* () {
122
+ const ttlMs = Math.max(1, Math.trunc(expiresAtMs - Date.now()));
123
+ const result = yield options.client.eval(CONSUME_SCRIPT, 1, `${keyPrefix}${jti}`, Date.now(), ttlMs);
124
+ return parseConsumeResult(result);
125
+ });
126
+ },
127
+ revokeFamily(familyId, expiresAtMs) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ const ttlMs = Math.max(0, Math.trunc(expiresAtMs - Date.now()));
130
+ yield options.client.eval(REVOKE_FAMILY_SCRIPT, 1, `${familyKeyPrefix}${familyId}`, ttlMs);
131
+ });
132
+ },
133
+ isFamilyRevoked(familyId) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ const result = yield options.client.eval(IS_FAMILY_REVOKED_SCRIPT, 1, `${familyKeyPrefix}${familyId}`);
136
+ return Number(result) > 0;
63
137
  });
64
138
  },
65
139
  healthCheck() {
@@ -84,6 +158,45 @@ function getRefreshTokenRotationStore(options) {
84
158
  return options.refreshTokenRotation.store;
85
159
  return ((_c = options.refreshTokenRotation) === null || _c === void 0 ? void 0 : _c.enabled) === true ? defaultMemoryStore : undefined;
86
160
  }
161
+ /**
162
+ * True se la famiglia di refresh token e stata revocata. Ritorna false se lo
163
+ * store non supporta le famiglie (implementazioni legacy).
164
+ */
165
+ function isRefreshTokenFamilyRevoked(options, familyId) {
166
+ return __awaiter(this, void 0, void 0, function* () {
167
+ if (typeof familyId !== 'string' || familyId.trim() === '')
168
+ return false;
169
+ const store = getRefreshTokenRotationStore(options);
170
+ if (!(store === null || store === void 0 ? void 0 : store.isFamilyRevoked))
171
+ return false;
172
+ return store.isFamilyRevoked(familyId);
173
+ });
174
+ }
175
+ /**
176
+ * Revoca la famiglia di refresh token fino alla scadenza indicata. Ritorna
177
+ * false se lo store non supporta le famiglie.
178
+ */
179
+ function revokeRefreshTokenFamily(options, familyId, expiresAtMs) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ const store = getRefreshTokenRotationStore(options);
182
+ if (!(store === null || store === void 0 ? void 0 : store.revokeFamily))
183
+ return false;
184
+ yield store.revokeFamily(familyId, expiresAtMs);
185
+ return true;
186
+ });
187
+ }
188
+ /** Consumo atomico con metadati, se lo store li supporta. */
189
+ function consumeRefreshToken(options, jti, expiresAtMs) {
190
+ return __awaiter(this, void 0, void 0, function* () {
191
+ const store = getRefreshTokenRotationStore(options);
192
+ if (!store)
193
+ return undefined;
194
+ if (store.consumeWithMetadata) {
195
+ return store.consumeWithMetadata(jti, expiresAtMs);
196
+ }
197
+ return { consumed: yield store.consume(jti, expiresAtMs) };
198
+ });
199
+ }
87
200
  /** Solo per test: azzera lo store in memoria di default. */
88
201
  function clearDefaultRefreshTokenRotations() {
89
202
  defaultMemoryStore.clear();