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

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 (149) hide show
  1. package/dist/accessi/resource-server/errors.d.ts +1 -1
  2. package/dist/accessi/resource-server/errors.js.map +1 -1
  3. package/dist/accessi/resource-server/express.js +21 -0
  4. package/dist/accessi/resource-server/express.js.map +1 -1
  5. package/dist/accessi/resource-server/nest.js +13 -0
  6. package/dist/accessi/resource-server/nest.js.map +1 -1
  7. package/dist/accessi/resource-server/runtime.js +11 -1
  8. package/dist/accessi/resource-server/runtime.js.map +1 -1
  9. package/dist/accessi/resource-server/types.d.ts +7 -0
  10. package/dist/accessi/resource-server/types.js.map +1 -1
  11. package/dist/accessi/server/index.d.ts +1 -0
  12. package/dist/accessi/server/index.js +1 -0
  13. package/dist/accessi/server/index.js.map +1 -1
  14. package/dist/accessi/server/standalone.d.ts +41 -0
  15. package/dist/accessi/server/standalone.js +264 -0
  16. package/dist/accessi/server/standalone.js.map +1 -0
  17. package/dist/accessi-module/AccessiModule.d.ts +53 -2
  18. package/dist/accessi-module/AccessiModule.js +5 -2
  19. package/dist/accessi-module/AccessiModule.js.map +1 -1
  20. package/dist/accessi-module/Console/accessiFetch.d.ts +5 -0
  21. package/dist/accessi-module/Console/accessiFetch.js +7 -4
  22. package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
  23. package/dist/accessi-module/Console/console.js +21 -7
  24. package/dist/accessi-module/Console/generated/emilsoftware-utilities.d.ts +31 -1
  25. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js +28 -3
  26. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js.map +1 -1
  27. package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.d.ts +12 -0
  28. package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.js +9 -0
  29. package/dist/accessi-module/Console/generated/model/exchangeFederatedIdentityRequest.js.map +1 -0
  30. package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.d.ts +15 -0
  31. package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.js +3 -0
  32. package/dist/accessi-module/Console/generated/model/federatedAuthenticationResultDto.js.map +1 -0
  33. package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.d.ts +14 -0
  34. package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.js +3 -0
  35. package/dist/accessi-module/Console/generated/model/federatedExchangeResponse.js.map +1 -0
  36. package/dist/accessi-module/Console/generated/model/index.d.ts +6 -0
  37. package/dist/accessi-module/Console/generated/model/index.js +6 -0
  38. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  39. package/dist/accessi-module/Console/generated/model/loginResult.d.ts +2 -0
  40. package/dist/accessi-module/Console/generated/model/loginResult.js.map +1 -1
  41. package/dist/accessi-module/Console/generated/model/refreshTokenRequest.d.ts +10 -0
  42. package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js +9 -0
  43. package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js.map +1 -0
  44. package/dist/accessi-module/Console/generated/model/refreshTokenResponse.d.ts +14 -0
  45. package/dist/accessi-module/Console/generated/model/refreshTokenResponse.js +3 -0
  46. package/dist/accessi-module/Console/generated/model/refreshTokenResponse.js.map +1 -0
  47. package/dist/accessi-module/Console/generated/model/refreshTokenResult.d.ts +13 -0
  48. package/dist/accessi-module/Console/generated/model/refreshTokenResult.js +3 -0
  49. package/dist/accessi-module/Console/generated/model/refreshTokenResult.js.map +1 -0
  50. package/dist/accessi-module/Console/wiki.js +16 -3
  51. package/dist/accessi-module/Console/wiki.js.map +1 -1
  52. package/dist/accessi-module/Controllers/AccessiConsoleController.d.ts +6 -1
  53. package/dist/accessi-module/Controllers/AccessiConsoleController.js +24 -1
  54. package/dist/accessi-module/Controllers/AccessiConsoleController.js.map +1 -1
  55. package/dist/accessi-module/Controllers/AuthController.d.ts +3 -1
  56. package/dist/accessi-module/Controllers/AuthController.js +90 -8
  57. package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
  58. package/dist/accessi-module/Controllers/ConsoleEntryController.d.ts +2 -0
  59. package/dist/accessi-module/Controllers/ConsoleEntryController.js +11 -3
  60. package/dist/accessi-module/Controllers/ConsoleEntryController.js.map +1 -1
  61. package/dist/accessi-module/Controllers/EmailController.js +1 -1
  62. package/dist/accessi-module/Controllers/EmailController.js.map +1 -1
  63. package/dist/accessi-module/Controllers/UserController.js +1 -1
  64. package/dist/accessi-module/Controllers/UserController.js.map +1 -1
  65. package/dist/accessi-module/Dtos/FederatedExchangeDtos.d.ts +21 -0
  66. package/dist/accessi-module/Dtos/FederatedExchangeDtos.js +74 -0
  67. package/dist/accessi-module/Dtos/FederatedExchangeDtos.js.map +1 -0
  68. package/dist/accessi-module/Dtos/LoginResponse.d.ts +1 -0
  69. package/dist/accessi-module/Dtos/LoginResponse.js +6 -0
  70. package/dist/accessi-module/Dtos/LoginResponse.js.map +1 -1
  71. package/dist/accessi-module/Dtos/RefreshTokenDtos.d.ts +12 -0
  72. package/dist/accessi-module/Dtos/RefreshTokenDtos.js +50 -0
  73. package/dist/accessi-module/Dtos/RefreshTokenDtos.js.map +1 -0
  74. package/dist/accessi-module/Dtos/index.d.ts +2 -0
  75. package/dist/accessi-module/Dtos/index.js +2 -0
  76. package/dist/accessi-module/Dtos/index.js.map +1 -1
  77. package/dist/accessi-module/Services/AuthService/AuthService.d.ts +14 -0
  78. package/dist/accessi-module/Services/AuthService/AuthService.js +99 -6
  79. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  80. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js +8 -16
  81. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js.map +1 -1
  82. package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.d.ts +15 -0
  83. package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.js +85 -0
  84. package/dist/accessi-module/federated-auth/FederatedAuthExchangeController.js.map +1 -0
  85. package/dist/accessi-module/federated-auth/FederatedAuthService.js +7 -1
  86. package/dist/accessi-module/federated-auth/FederatedAuthService.js.map +1 -1
  87. package/dist/accessi-module/index.d.ts +17 -1
  88. package/dist/accessi-module/index.js +71 -5
  89. package/dist/accessi-module/index.js.map +1 -1
  90. package/dist/accessi-module/jwt/jwt.strategy.js +4 -0
  91. package/dist/accessi-module/jwt/jwt.strategy.js.map +1 -1
  92. package/dist/accessi-module/middleware/authenticateGen.js +4 -0
  93. package/dist/accessi-module/middleware/authenticateGen.js.map +1 -1
  94. package/dist/accessi-module/security/audit.d.ts +7 -0
  95. package/dist/accessi-module/security/audit.js +14 -0
  96. package/dist/accessi-module/security/audit.js.map +1 -0
  97. package/dist/accessi-module/security/authenticatedToken.d.ts +1 -1
  98. package/dist/accessi-module/security/authenticatedToken.js +3 -3
  99. package/dist/accessi-module/security/authenticatedToken.js.map +1 -1
  100. package/dist/accessi-module/security/consoleEntry.d.ts +58 -11
  101. package/dist/accessi-module/security/consoleEntry.js +181 -31
  102. package/dist/accessi-module/security/consoleEntry.js.map +1 -1
  103. package/dist/accessi-module/security/edgeRateLimit.d.ts +33 -0
  104. package/dist/accessi-module/security/edgeRateLimit.js +81 -0
  105. package/dist/accessi-module/security/edgeRateLimit.js.map +1 -0
  106. package/dist/accessi-module/security/jwks.d.ts +33 -0
  107. package/dist/accessi-module/security/jwks.js +83 -0
  108. package/dist/accessi-module/security/jwks.js.map +1 -0
  109. package/dist/accessi-module/security/jwksBootstrap.d.ts +15 -0
  110. package/dist/accessi-module/security/jwksBootstrap.js +44 -0
  111. package/dist/accessi-module/security/jwksBootstrap.js.map +1 -0
  112. package/dist/accessi-module/security/jwtTokenOptions.d.ts +9 -2
  113. package/dist/accessi-module/security/jwtTokenOptions.js +18 -4
  114. package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -1
  115. package/dist/accessi-module/security/passwordResetToken.d.ts +1 -1
  116. package/dist/accessi-module/security/passwordResetToken.js.map +1 -1
  117. package/dist/accessi-module/security/publicAuthRateLimit.d.ts +9 -7
  118. package/dist/accessi-module/security/publicAuthRateLimit.js +55 -78
  119. package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
  120. package/dist/accessi-module/security/rateLimitStore.d.ts +34 -0
  121. package/dist/accessi-module/security/rateLimitStore.js +145 -0
  122. package/dist/accessi-module/security/rateLimitStore.js.map +1 -0
  123. package/dist/accessi-module/security/refreshTokenRotation.d.ts +40 -0
  124. package/dist/accessi-module/security/refreshTokenRotation.js +91 -0
  125. package/dist/accessi-module/security/refreshTokenRotation.js.map +1 -0
  126. package/dist/accessi-module/security/serviceTokenGuard.d.ts +3 -1
  127. package/dist/accessi-module/security/serviceTokenGuard.js +11 -6
  128. package/dist/accessi-module/security/serviceTokenGuard.js.map +1 -1
  129. package/dist/accessi-module/security/serviceTokenRateLimit.d.ts +5 -4
  130. package/dist/accessi-module/security/serviceTokenRateLimit.js +33 -29
  131. package/dist/accessi-module/security/serviceTokenRateLimit.js.map +1 -1
  132. package/dist/accessi-module/security/sessionRevocation.d.ts +52 -0
  133. package/dist/accessi-module/security/sessionRevocation.js +124 -0
  134. package/dist/accessi-module/security/sessionRevocation.js.map +1 -0
  135. package/dist/accessi-module/security/validateOptions.d.ts +19 -0
  136. package/dist/accessi-module/security/validateOptions.js +115 -0
  137. package/dist/accessi-module/security/validateOptions.js.map +1 -0
  138. package/dist/emilsoftware-utilities-2.0.0-dev.69.tgz +0 -0
  139. package/dist/initEmilsoftwareModule.js +39 -3
  140. package/dist/initEmilsoftwareModule.js.map +1 -1
  141. package/dist/scripts/startAccessiService.d.ts +1 -0
  142. package/dist/scripts/startAccessiService.js +11 -0
  143. package/dist/scripts/startAccessiService.js.map +1 -0
  144. package/docs/accessi-openapi-fingerprint.json +190 -0
  145. package/docs/gateway-rate-limit.md +98 -0
  146. package/docs/standalone-service.md +176 -0
  147. package/integration-prompt.md +299 -8
  148. package/package.json +11 -5
  149. package/dist/emilsoftware-utilities-2.0.0-dev.67.tgz +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"authenticateGen.js","sourceRoot":"","sources":["../../../src/accessi-module/middleware/authenticateGen.ts"],"names":[],"mappings":"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","sourcesContent":["import { NextFunction, Request, Response } from \"express\";\nimport * as jwt from \"jsonwebtoken\";\nimport { Inject, Injectable } from \"@nestjs/common\";\nimport type { AccessiOptions } from \"../AccessiModule\";\nimport { PermissionService } from \"../Services/PermissionService/PermissionService\";\nimport { UserService } from \"../Services/UserService/UserService\";\nimport { Logger } from \"../../Logger\";\nimport {\n AccessiAuthorizationOptions,\n AccessiCustomRequirementContext,\n GrantsResult,\n RequirementEvaluationError,\n buildRequirementTree,\n evaluateRequirement,\n} from \"./accessiRequirements\";\nimport {\n buildAuthenticatedTokenPayload,\n extractAccessiBearerToken,\n isAuthenticatedUserEnabledForJwt,\n isAccessiTokenAllowedForUser,\n resolveCodiceUtenteFromTokenPayload,\n} from \"../security/authenticatedToken\";\nimport { getAccessiAccessTokenVerificationConfig } from \"../security/jwtTokenOptions\";\n\nconst logger = new Logger(\"AuthenticateGen\");\nconst ACCESSI_AUTH_SERVICE_LOCALS_KEY = \"accessiAuthService\";\nconst ACCESSI_AUTH_SERVICE_GLOBAL_KEY = Symbol.for(\"emilsoftware.accessiAuthService\");\nconst ACCESSI_AUTH_SERVICE_WAIT_MS = 15000;\n\ntype Deferred<T> = {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n};\n\nclass AuthMiddlewareError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n public readonly details?: Record<string, unknown>\n ) {\n super(message);\n this.name = \"AuthMiddlewareError\";\n }\n}\n\nfunction authError(\n status: number,\n code: string,\n message: string,\n details?: Record<string, unknown>\n) {\n return new AuthMiddlewareError(status, code, message, details);\n}\n\nfunction normalizeAuthError(error: unknown): AuthMiddlewareError {\n if (error instanceof AuthMiddlewareError) return error;\n if (error instanceof RequirementEvaluationError) {\n return authError(500, error.code, error.message);\n }\n if (error instanceof Error) {\n return authError(500, \"AUTH_INTERNAL_ERROR\", error.message, {\n originalError: error.name,\n });\n }\n return authError(500, \"AUTH_INTERNAL_ERROR\", \"Unexpected authentication error\");\n}\n\nfunction logAuthFailure(req: Request, authErr: AuthMiddlewareError) {\n const payload = {\n code: authErr.code,\n status: authErr.status,\n message: authErr.message,\n details: authErr.details,\n method: req.method,\n path: req.originalUrl ?? req.url,\n ip: req.ip,\n };\n\n const message = `${authErr.status >= 500 ? \"Authentication failure\" : \"Authentication denied\"} ${JSON.stringify(payload)}`;\n if (authErr.status >= 500) logger.error(message);\n else logger.warning(message);\n}\n\n/** Sends the documented Accessi error contract from Express middleware. */\nfunction sendAuthenticationError(res: Response, authErr: AuthMiddlewareError): Response {\n const message =\n authErr.status === 403\n ? 'Operazione non autorizzata.'\n : authErr.status >= 500\n ? 'Errore interno del modulo Accessi.'\n : 'Autenticazione non valida o scaduta.';\n\n return res.status(authErr.status).json({\n severity: 'error',\n status: authErr.status,\n statusCode: 2,\n code: authErr.code,\n // Kept for callers of older versions which already inspected `error`.\n error: authErr.code,\n message,\n });\n}\n\nasync function authorizeWithDependencies(\n req: Request,\n res: Response,\n next: NextFunction,\n options: AccessiAuthorizationOptions | undefined,\n accessiOptions: AccessiOptions,\n permissionService: PermissionService,\n userService: UserService\n) {\n type AuthenticatedRequest = Request & {\n userGrants?: unknown;\n user?: unknown;\n data?: unknown;\n };\n const authenticatedRequest = req as AuthenticatedRequest;\n\n try {\n const authHeader = req.headers.authorization;\n if (!authHeader) {\n throw authError(401, \"AUTH_HEADER_MISSING\", \"Authorization header not found\");\n }\n\n const token = extractAccessiBearerToken(authHeader);\n if (!token) {\n throw authError(\n 401,\n \"AUTH_TOKEN_MISSING\",\n \"Token not found in Authorization header\"\n );\n }\n\n let verification: ReturnType<typeof getAccessiAccessTokenVerificationConfig>;\n try {\n verification = getAccessiAccessTokenVerificationConfig(accessiOptions);\n } catch {\n throw authError(500, \"AUTH_JWT_SECRET_MISSING\", \"JWT signing key not configured\");\n }\n\n let decoded: unknown;\n try {\n decoded = jwt.verify(token, verification.key, verification.verifyOptions);\n } catch {\n throw authError(401, \"AUTH_TOKEN_INVALID\", \"Invalid JWT token\");\n }\n\n const codiceUtente = resolveCodiceUtenteFromTokenPayload(decoded);\n if (!codiceUtente) {\n throw authError(\n 401,\n \"AUTH_USER_CODE_MISSING\",\n \"codiceUtente not found in token payload\"\n );\n }\n\n const currentUser = await userService.getAuthenticatedUserSnapshot(codiceUtente);\n if (!isAuthenticatedUserEnabledForJwt(currentUser) || !isAccessiTokenAllowedForUser(decoded, currentUser)) {\n throw authError(\n 401,\n \"AUTH_USER_DISABLED\",\n \"User is no longer authorized\"\n );\n }\n\n const authenticatedPayload = buildAuthenticatedTokenPayload(decoded, currentUser);\n\n const requirementTree = buildRequirementTree(options);\n if (requirementTree) {\n if (!accessiOptions?.databaseOptions) {\n throw authError(\n 500,\n \"AUTH_DATABASE_OPTIONS_MISSING\",\n \"Database options not configured\"\n );\n }\n\n let grantsResultCache: GrantsResult | null = null;\n const getGrantsResult = async () => {\n if (!grantsResultCache) {\n grantsResultCache = await permissionService.getUserRolesAndGrants(\n codiceUtente\n );\n }\n return grantsResultCache;\n };\n\n const requirementContext: AccessiCustomRequirementContext = {\n req,\n decodedToken: authenticatedPayload,\n userCode: codiceUtente,\n getGrantsResult,\n };\n\n const hasPermissions = await evaluateRequirement(\n requirementTree,\n requirementContext,\n options\n );\n\n if (!hasPermissions) {\n throw authError(\n 403,\n \"AUTH_INSUFFICIENT_PERMISSIONS\",\n \"User does not have required permissions\"\n );\n }\n\n authenticatedRequest.userGrants = await getGrantsResult();\n }\n\n authenticatedRequest.user = authenticatedPayload;\n authenticatedRequest.data = authenticatedPayload;\n return next();\n } catch (error: unknown) {\n const authErr = normalizeAuthError(error);\n logAuthFailure(req, authErr);\n return sendAuthenticationError(res, authErr);\n }\n}\n\n@Injectable()\n/** Nest service behind the Express-compatible `authorizeAccessi` middleware. */\nexport class AuthenticateGenService {\n constructor(\n @Inject(\"ACCESSI_OPTIONS\")\n private readonly accessiOptions: AccessiOptions,\n private readonly permissionService: PermissionService,\n private readonly userService: UserService\n ) { }\n\n /**\n * Verifica JWT, stato corrente dell'utente e policy opzionale, poi popola `req.user` e `req.data`.\n * Se una policy richiede grant, popola anche `req.userGrants`; non chiamarlo direttamente fuori da test o adapter.\n */\n async authorize(\n req: Request,\n res: Response,\n next: NextFunction,\n options?: AccessiAuthorizationOptions\n ) {\n return authorizeWithDependencies(\n req,\n res,\n next,\n options,\n this.accessiOptions,\n this.permissionService,\n this.userService\n );\n }\n}\n\nlet authenticateGenServiceRef: AuthenticateGenService | null = null;\nlet authServiceBootstrapDeferred: Deferred<AuthenticateGenService> | null = null;\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n let reject!: (reason?: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n}\n\nfunction isAuthenticateGenService(value: unknown): value is AuthenticateGenService {\n return Boolean(\n value &&\n typeof (value as { authorize?: unknown }).authorize === \"function\"\n );\n}\n\n/** Segnala al middleware che il bootstrap e in corso, consentendo una risposta 503 temporanea invece di un 500. */\nexport function beginAccessiAuthInitialization() {\n if (!authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred = createDeferred<AuthenticateGenService>();\n }\n}\n\n/** Registra il servizio costruito da Nest nel bridge usato dalle rotte Express dell'app ospitante. */\nexport function setAccessiAuthService(service: AuthenticateGenService) {\n authenticateGenServiceRef = service;\n (globalThis as Record<symbol, unknown>)[ACCESSI_AUTH_SERVICE_GLOBAL_KEY] = service;\n if (authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred.resolve(service);\n authServiceBootstrapDeferred = null;\n }\n}\n\n/** Propaga il fallimento del bootstrap alle richieste che stavano attendendo il bridge di autorizzazione. */\nexport function failAccessiAuthInitialization(error: unknown) {\n if (authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred.reject(error);\n authServiceBootstrapDeferred = null;\n }\n}\n\nfunction getAccessiAuthServiceFromRequest(req: Request): AuthenticateGenService | null {\n const appLocalsService = (req.app?.locals as Record<string, unknown> | undefined)?.[\n ACCESSI_AUTH_SERVICE_LOCALS_KEY\n ];\n\n if (isAuthenticateGenService(appLocalsService)) {\n return appLocalsService as AuthenticateGenService;\n }\n\n const globalService = (globalThis as Record<symbol, unknown>)[\n ACCESSI_AUTH_SERVICE_GLOBAL_KEY\n ];\n if (isAuthenticateGenService(globalService)) {\n return globalService as AuthenticateGenService;\n }\n\n return authenticateGenServiceRef;\n}\n\nasync function waitForBootstrappedService(): Promise<AuthenticateGenService | null> {\n if (!authServiceBootstrapDeferred) {\n return null;\n }\n\n const bootstrapPromise = authServiceBootstrapDeferred.promise;\n const timeoutPromise = new Promise<null>((resolve) => {\n setTimeout(() => resolve(null), ACCESSI_AUTH_SERVICE_WAIT_MS);\n });\n\n try {\n const resolved = await Promise.race([bootstrapPromise, timeoutPromise]);\n return isAuthenticateGenService(resolved) ? resolved : null;\n } catch {\n return null;\n }\n}\n\nasync function resolveAccessiAuthService(req: Request): Promise<AuthenticateGenService | null> {\n const immediateService = getAccessiAuthServiceFromRequest(req);\n if (immediateService) {\n return immediateService;\n }\n\n const bootstrappedService = await waitForBootstrappedService();\n if (bootstrappedService) {\n return bootstrappedService;\n }\n\n return getAccessiAuthServiceFromRequest(req);\n}\n\n/**\n * Middleware Express pubblico di Accessi.\n *\n * Applicarlo dopo `initializeAccessiModule`: legge `Authorization: Bearer <jwt>`, verifica l'utente corrente\n * e applica eventuali requisiti. Su successo valorizza `req.user` e continua; su errore risponde direttamente\n * con il contratto Accessi, quindi il chiamante non deve aggiungere un proprio `next(error)`.\n */\nexport async function authorizeAccessi(\n req: Request,\n res: Response,\n next: NextFunction,\n options?: AccessiAuthorizationOptions\n) {\n const authService = await resolveAccessiAuthService(req);\n\n if (!authService) {\n const status = authServiceBootstrapDeferred ? 503 : 500;\n const code = authServiceBootstrapDeferred\n ? 'ACCESSI_AUTH_INITIALIZING'\n : 'ACCESSI_AUTH_NOT_INITIALIZED';\n logger.error(\n `Authentication service not initialized ${JSON.stringify({\n method: req.method,\n path: req.originalUrl ?? req.url,\n status,\n })}`\n );\n return res.status(status).json({\n severity: 'error',\n status,\n statusCode: 2,\n code,\n error: code,\n message: status === 503\n ? 'Il modulo Accessi e in inizializzazione. Riprova tra poco.'\n : 'Il modulo Accessi non e disponibile.',\n });\n }\n\n return authService.authorize(req, res, next, options);\n}\n\n/** Alias storico di `authorizeAccessi`, mantenuto per retrocompatibilita. Preferire il nome esplicito nelle nuove integrazioni. */\nexport const authenticateGen = authorizeAccessi;\n"]}
1
+ {"version":3,"file":"authenticateGen.js","sourceRoot":"","sources":["../../../src/accessi-module/middleware/authenticateGen.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyRA,wEAIC;AAGD,sDAOC;AAGD,sEAKC;AA4DD,4CAiCC;AA3YD,kDAAoC;AACpC,2CAAoD;AAEpD,uFAAoF;AACpF,qEAAkE;AAClE,yCAAsC;AACtC,+DAO+B;AAC/B,uEAMwC;AACxC,iEAAsF;AACtF,qEAAsE;AAEtE,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,iBAAiB,CAAC,CAAC;AAC7C,MAAM,+BAA+B,GAAG,oBAAoB,CAAC;AAC7D,MAAM,+BAA+B,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AACtF,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAQ3C,MAAM,mBAAoB,SAAQ,KAAK;IACrC,YACkB,MAAc,EACd,IAAY,EAC5B,OAAe,EACC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAEZ,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,SAAS,SAAS,CAChB,MAAc,EACd,IAAY,EACZ,OAAe,EACf,OAAiC;IAEjC,OAAO,IAAI,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,mBAAmB;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,KAAK,YAAY,gDAA0B,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC,GAAG,EAAE,qBAAqB,EAAE,KAAK,CAAC,OAAO,EAAE;YAC1D,aAAa,EAAE,KAAK,CAAC,IAAI;SAC1B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC,GAAG,EAAE,qBAAqB,EAAE,iCAAiC,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,OAA4B;;IAChE,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,MAAA,GAAG,CAAC,WAAW,mCAAI,GAAG,CAAC,GAAG;QAChC,EAAE,EAAE,GAAG,CAAC,EAAE;KACX,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,uBAAuB,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3H,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG;QAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;;QAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,2EAA2E;AAC3E,SAAS,uBAAuB,CAAC,GAAa,EAAE,OAA4B;IAC1E,MAAM,OAAO,GACX,OAAO,CAAC,MAAM,KAAK,GAAG;QACpB,CAAC,CAAC,6BAA6B;QAC/B,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,GAAG;YACrB,CAAC,CAAC,oCAAoC;YACtC,CAAC,CAAC,sCAAsC,CAAC;IAE/C,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;QACrC,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,CAAC;QACb,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,sEAAsE;QACtE,KAAK,EAAE,OAAO,CAAC,IAAI;QACnB,OAAO;KACR,CAAC,CAAC;AACL,CAAC;AAED,SAAe,yBAAyB,CACtC,GAAY,EACZ,GAAa,EACb,IAAkB,EAClB,OAAgD,EAChD,cAA8B,EAC9B,iBAAoC,EACpC,WAAwB;;QAOxB,MAAM,oBAAoB,GAAG,GAA2B,CAAC;QAEzD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;YAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,SAAS,CAAC,GAAG,EAAE,qBAAqB,EAAE,gCAAgC,CAAC,CAAC;YAChF,CAAC;YAED,MAAM,KAAK,GAAG,IAAA,8CAAyB,EAAC,UAAU,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,SAAS,CACb,GAAG,EACH,oBAAoB,EACpB,yCAAyC,CAC1C,CAAC;YACJ,CAAC;YAED,IAAI,YAAwE,CAAC;YAC7E,IAAI,CAAC;gBACH,YAAY,GAAG,IAAA,yDAAuC,EAAC,cAAc,CAAC,CAAC;YACzE,CAAC;YAAC,WAAM,CAAC;gBACP,MAAM,SAAS,CAAC,GAAG,EAAE,yBAAyB,EAAE,gCAAgC,CAAC,CAAC;YACpF,CAAC;YAED,IAAI,OAAgB,CAAC;YACrB,IAAI,CAAC;gBACH,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;YAC5E,CAAC;YAAC,WAAM,CAAC;gBACP,MAAM,SAAS,CAAC,GAAG,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,YAAY,GAAG,IAAA,wDAAmC,EAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,SAAS,CACb,GAAG,EACH,wBAAwB,EACxB,yCAAyC,CAC1C,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,IAAA,yCAAqB,EAAC,cAAc,EAAE,OAAkC,EAAE,YAAY,CAAC,EAAE,CAAC;gBAClG,MAAM,SAAS,CAAC,GAAG,EAAE,oBAAoB,EAAE,iBAAiB,CAAC,CAAC;YAChE,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,4BAA4B,CAAC,YAAY,CAAC,CAAC;YACjF,IAAI,CAAC,IAAA,qDAAgC,EAAC,WAAW,CAAC,IAAI,CAAC,IAAA,iDAA4B,EAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;gBAC1G,MAAM,SAAS,CACb,GAAG,EACH,oBAAoB,EACpB,8BAA8B,CAC/B,CAAC;YACJ,CAAC;YAED,MAAM,oBAAoB,GAAG,IAAA,mDAA8B,EAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAElF,MAAM,eAAe,GAAG,IAAA,0CAAoB,EAAC,OAAO,CAAC,CAAC;YACtD,IAAI,eAAe,EAAE,CAAC;gBACpB,IAAI,CAAC,CAAA,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,eAAe,CAAA,EAAE,CAAC;oBACrC,MAAM,SAAS,CACb,GAAG,EACH,+BAA+B,EAC/B,iCAAiC,CAClC,CAAC;gBACJ,CAAC;gBAED,IAAI,iBAAiB,GAAwB,IAAI,CAAC;gBAClD,MAAM,eAAe,GAAG,GAAS,EAAE;oBACjC,IAAI,CAAC,iBAAiB,EAAE,CAAC;wBACvB,iBAAiB,GAAG,MAAM,iBAAiB,CAAC,qBAAqB,CAC/D,YAAY,CACb,CAAC;oBACJ,CAAC;oBACD,OAAO,iBAAiB,CAAC;gBAC3B,CAAC,CAAA,CAAC;gBAEF,MAAM,kBAAkB,GAAoC;oBAC1D,GAAG;oBACH,YAAY,EAAE,oBAAoB;oBAClC,QAAQ,EAAE,YAAY;oBACtB,eAAe;iBAChB,CAAC;gBAEF,MAAM,cAAc,GAAG,MAAM,IAAA,yCAAmB,EAC9C,eAAe,EACf,kBAAkB,EAClB,OAAO,CACR,CAAC;gBAEF,IAAI,CAAC,cAAc,EAAE,CAAC;oBACpB,MAAM,SAAS,CACb,GAAG,EACH,+BAA+B,EAC/B,yCAAyC,CAC1C,CAAC;gBACJ,CAAC;gBAED,oBAAoB,CAAC,UAAU,GAAG,MAAM,eAAe,EAAE,CAAC;YAC5D,CAAC;YAED,oBAAoB,CAAC,IAAI,GAAG,oBAAoB,CAAC;YACjD,oBAAoB,CAAC,IAAI,GAAG,oBAAoB,CAAC;YACjD,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC1C,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC7B,OAAO,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;CAAA;AAIM,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,YAEmB,cAA8B,EAC9B,iBAAoC,EACpC,WAAwB;QAFxB,mBAAc,GAAd,cAAc,CAAgB;QAC9B,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,gBAAW,GAAX,WAAW,CAAa;IACvC,CAAC;IAEL;;;OAGG;IACG,SAAS,CACb,GAAY,EACZ,GAAa,EACb,IAAkB,EAClB,OAAqC;;YAErC,OAAO,yBAAyB,CAC9B,GAAG,EACH,GAAG,EACH,IAAI,EACJ,OAAO,EACP,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,WAAW,CACjB,CAAC;QACJ,CAAC;KAAA;CACF,CAAA;AA5BY,wDAAsB;iCAAtB,sBAAsB;IAFlC,IAAA,mBAAU,GAAE;IACb,gFAAgF;;IAG3E,WAAA,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAA;6CAEU,qCAAiB;QACvB,yBAAW;GALhC,sBAAsB,CA4BlC;AAED,IAAI,yBAAyB,GAAkC,IAAI,CAAC;AACpE,IAAI,4BAA4B,GAA4C,IAAI,CAAC;AAEjF,SAAS,cAAc;IACrB,IAAI,OAA4B,CAAC;IACjC,IAAI,MAAmC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC1C,OAAO,GAAG,GAAG,CAAC;QACd,MAAM,GAAG,GAAG,CAAC;IACf,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc;IAC9C,OAAO,OAAO,CACZ,KAAK;QACL,OAAQ,KAAiC,CAAC,SAAS,KAAK,UAAU,CACnE,CAAC;AACJ,CAAC;AAED,mHAAmH;AACnH,SAAgB,8BAA8B;IAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;QAClC,4BAA4B,GAAG,cAAc,EAA0B,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,sGAAsG;AACtG,SAAgB,qBAAqB,CAAC,OAA+B;IACnE,yBAAyB,GAAG,OAAO,CAAC;IACnC,UAAsC,CAAC,+BAA+B,CAAC,GAAG,OAAO,CAAC;IACnF,IAAI,4BAA4B,EAAE,CAAC;QACjC,4BAA4B,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,4BAA4B,GAAG,IAAI,CAAC;IACtC,CAAC;AACH,CAAC;AAED,6GAA6G;AAC7G,SAAgB,6BAA6B,CAAC,KAAc;IAC1D,IAAI,4BAA4B,EAAE,CAAC;QACjC,4BAA4B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3C,4BAA4B,GAAG,IAAI,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,gCAAgC,CAAC,GAAY;;IACpD,MAAM,gBAAgB,GAAG,MAAC,MAAA,GAAG,CAAC,GAAG,0CAAE,MAA8C,0CAC/E,+BAA+B,CAChC,CAAC;IAEF,IAAI,wBAAwB,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC/C,OAAO,gBAA0C,CAAC;IACpD,CAAC;IAED,MAAM,aAAa,GAAI,UAAsC,CAC3D,+BAA+B,CAChC,CAAC;IACF,IAAI,wBAAwB,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5C,OAAO,aAAuC,CAAC;IACjD,CAAC;IAED,OAAO,yBAAyB,CAAC;AACnC,CAAC;AAED,SAAe,0BAA0B;;QACvC,IAAI,CAAC,4BAA4B,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,gBAAgB,GAAG,4BAA4B,CAAC,OAAO,CAAC;QAC9D,MAAM,cAAc,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnD,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,4BAA4B,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC;YACxE,OAAO,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CAAA;AAED,SAAe,yBAAyB,CAAC,GAAY;;QACnD,MAAM,gBAAgB,GAAG,gCAAgC,CAAC,GAAG,CAAC,CAAC;QAC/D,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,MAAM,mBAAmB,GAAG,MAAM,0BAA0B,EAAE,CAAC;QAC/D,IAAI,mBAAmB,EAAE,CAAC;YACxB,OAAO,mBAAmB,CAAC;QAC7B,CAAC;QAED,OAAO,gCAAgC,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;CAAA;AAED;;;;;;GAMG;AACH,SAAsB,gBAAgB,CACpC,GAAY,EACZ,GAAa,EACb,IAAkB,EAClB,OAAqC;;;QAErC,MAAM,WAAW,GAAG,MAAM,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEzD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,4BAA4B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACxD,MAAM,IAAI,GAAG,4BAA4B;gBACvC,CAAC,CAAC,2BAA2B;gBAC7B,CAAC,CAAC,8BAA8B,CAAC;YACnC,MAAM,CAAC,KAAK,CACV,0CAA0C,IAAI,CAAC,SAAS,CAAC;gBACvD,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,IAAI,EAAE,MAAA,GAAG,CAAC,WAAW,mCAAI,GAAG,CAAC,GAAG;gBAChC,MAAM;aACP,CAAC,EAAE,CACL,CAAC;YACF,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;gBAC7B,QAAQ,EAAE,OAAO;gBACjB,MAAM;gBACN,UAAU,EAAE,CAAC;gBACb,IAAI;gBACJ,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,MAAM,KAAK,GAAG;oBACrB,CAAC,CAAC,4DAA4D;oBAC9D,CAAC,CAAC,sCAAsC;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,OAAO,WAAW,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;CAAA;AAED,mIAAmI;AACtH,QAAA,eAAe,GAAG,gBAAgB,CAAC","sourcesContent":["import { NextFunction, Request, Response } from \"express\";\nimport * as jwt from \"jsonwebtoken\";\nimport { Inject, Injectable } from \"@nestjs/common\";\nimport type { AccessiOptions } from \"../AccessiModule\";\nimport { PermissionService } from \"../Services/PermissionService/PermissionService\";\nimport { UserService } from \"../Services/UserService/UserService\";\nimport { Logger } from \"../../Logger\";\nimport {\n AccessiAuthorizationOptions,\n AccessiCustomRequirementContext,\n GrantsResult,\n RequirementEvaluationError,\n buildRequirementTree,\n evaluateRequirement,\n} from \"./accessiRequirements\";\nimport {\n buildAuthenticatedTokenPayload,\n extractAccessiBearerToken,\n isAuthenticatedUserEnabledForJwt,\n isAccessiTokenAllowedForUser,\n resolveCodiceUtenteFromTokenPayload,\n} from \"../security/authenticatedToken\";\nimport { getAccessiAccessTokenVerificationConfig } from \"../security/jwtTokenOptions\";\nimport { isAccessiTokenRevoked } from \"../security/sessionRevocation\";\n\nconst logger = new Logger(\"AuthenticateGen\");\nconst ACCESSI_AUTH_SERVICE_LOCALS_KEY = \"accessiAuthService\";\nconst ACCESSI_AUTH_SERVICE_GLOBAL_KEY = Symbol.for(\"emilsoftware.accessiAuthService\");\nconst ACCESSI_AUTH_SERVICE_WAIT_MS = 15000;\n\ntype Deferred<T> = {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n};\n\nclass AuthMiddlewareError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n public readonly details?: Record<string, unknown>\n ) {\n super(message);\n this.name = \"AuthMiddlewareError\";\n }\n}\n\nfunction authError(\n status: number,\n code: string,\n message: string,\n details?: Record<string, unknown>\n) {\n return new AuthMiddlewareError(status, code, message, details);\n}\n\nfunction normalizeAuthError(error: unknown): AuthMiddlewareError {\n if (error instanceof AuthMiddlewareError) return error;\n if (error instanceof RequirementEvaluationError) {\n return authError(500, error.code, error.message);\n }\n if (error instanceof Error) {\n return authError(500, \"AUTH_INTERNAL_ERROR\", error.message, {\n originalError: error.name,\n });\n }\n return authError(500, \"AUTH_INTERNAL_ERROR\", \"Unexpected authentication error\");\n}\n\nfunction logAuthFailure(req: Request, authErr: AuthMiddlewareError) {\n const payload = {\n code: authErr.code,\n status: authErr.status,\n message: authErr.message,\n details: authErr.details,\n method: req.method,\n path: req.originalUrl ?? req.url,\n ip: req.ip,\n };\n\n const message = `${authErr.status >= 500 ? \"Authentication failure\" : \"Authentication denied\"} ${JSON.stringify(payload)}`;\n if (authErr.status >= 500) logger.error(message);\n else logger.warning(message);\n}\n\n/** Sends the documented Accessi error contract from Express middleware. */\nfunction sendAuthenticationError(res: Response, authErr: AuthMiddlewareError): Response {\n const message =\n authErr.status === 403\n ? 'Operazione non autorizzata.'\n : authErr.status >= 500\n ? 'Errore interno del modulo Accessi.'\n : 'Autenticazione non valida o scaduta.';\n\n return res.status(authErr.status).json({\n severity: 'error',\n status: authErr.status,\n statusCode: 2,\n code: authErr.code,\n // Kept for callers of older versions which already inspected `error`.\n error: authErr.code,\n message,\n });\n}\n\nasync function authorizeWithDependencies(\n req: Request,\n res: Response,\n next: NextFunction,\n options: AccessiAuthorizationOptions | undefined,\n accessiOptions: AccessiOptions,\n permissionService: PermissionService,\n userService: UserService\n) {\n type AuthenticatedRequest = Request & {\n userGrants?: unknown;\n user?: unknown;\n data?: unknown;\n };\n const authenticatedRequest = req as AuthenticatedRequest;\n\n try {\n const authHeader = req.headers.authorization;\n if (!authHeader) {\n throw authError(401, \"AUTH_HEADER_MISSING\", \"Authorization header not found\");\n }\n\n const token = extractAccessiBearerToken(authHeader);\n if (!token) {\n throw authError(\n 401,\n \"AUTH_TOKEN_MISSING\",\n \"Token not found in Authorization header\"\n );\n }\n\n let verification: ReturnType<typeof getAccessiAccessTokenVerificationConfig>;\n try {\n verification = getAccessiAccessTokenVerificationConfig(accessiOptions);\n } catch {\n throw authError(500, \"AUTH_JWT_SECRET_MISSING\", \"JWT signing key not configured\");\n }\n\n let decoded: unknown;\n try {\n decoded = jwt.verify(token, verification.key, verification.verifyOptions);\n } catch {\n throw authError(401, \"AUTH_TOKEN_INVALID\", \"Invalid JWT token\");\n }\n\n const codiceUtente = resolveCodiceUtenteFromTokenPayload(decoded);\n if (!codiceUtente) {\n throw authError(\n 401,\n \"AUTH_USER_CODE_MISSING\",\n \"codiceUtente not found in token payload\"\n );\n }\n if (await isAccessiTokenRevoked(accessiOptions, decoded as Record<string, unknown>, codiceUtente)) {\n throw authError(401, \"AUTH_TOKEN_REVOKED\", \"Session revoked\");\n }\n\n const currentUser = await userService.getAuthenticatedUserSnapshot(codiceUtente);\n if (!isAuthenticatedUserEnabledForJwt(currentUser) || !isAccessiTokenAllowedForUser(decoded, currentUser)) {\n throw authError(\n 401,\n \"AUTH_USER_DISABLED\",\n \"User is no longer authorized\"\n );\n }\n\n const authenticatedPayload = buildAuthenticatedTokenPayload(decoded, currentUser);\n\n const requirementTree = buildRequirementTree(options);\n if (requirementTree) {\n if (!accessiOptions?.databaseOptions) {\n throw authError(\n 500,\n \"AUTH_DATABASE_OPTIONS_MISSING\",\n \"Database options not configured\"\n );\n }\n\n let grantsResultCache: GrantsResult | null = null;\n const getGrantsResult = async () => {\n if (!grantsResultCache) {\n grantsResultCache = await permissionService.getUserRolesAndGrants(\n codiceUtente\n );\n }\n return grantsResultCache;\n };\n\n const requirementContext: AccessiCustomRequirementContext = {\n req,\n decodedToken: authenticatedPayload,\n userCode: codiceUtente,\n getGrantsResult,\n };\n\n const hasPermissions = await evaluateRequirement(\n requirementTree,\n requirementContext,\n options\n );\n\n if (!hasPermissions) {\n throw authError(\n 403,\n \"AUTH_INSUFFICIENT_PERMISSIONS\",\n \"User does not have required permissions\"\n );\n }\n\n authenticatedRequest.userGrants = await getGrantsResult();\n }\n\n authenticatedRequest.user = authenticatedPayload;\n authenticatedRequest.data = authenticatedPayload;\n return next();\n } catch (error: unknown) {\n const authErr = normalizeAuthError(error);\n logAuthFailure(req, authErr);\n return sendAuthenticationError(res, authErr);\n }\n}\n\n@Injectable()\n/** Nest service behind the Express-compatible `authorizeAccessi` middleware. */\nexport class AuthenticateGenService {\n constructor(\n @Inject(\"ACCESSI_OPTIONS\")\n private readonly accessiOptions: AccessiOptions,\n private readonly permissionService: PermissionService,\n private readonly userService: UserService\n ) { }\n\n /**\n * Verifica JWT, stato corrente dell'utente e policy opzionale, poi popola `req.user` e `req.data`.\n * Se una policy richiede grant, popola anche `req.userGrants`; non chiamarlo direttamente fuori da test o adapter.\n */\n async authorize(\n req: Request,\n res: Response,\n next: NextFunction,\n options?: AccessiAuthorizationOptions\n ) {\n return authorizeWithDependencies(\n req,\n res,\n next,\n options,\n this.accessiOptions,\n this.permissionService,\n this.userService\n );\n }\n}\n\nlet authenticateGenServiceRef: AuthenticateGenService | null = null;\nlet authServiceBootstrapDeferred: Deferred<AuthenticateGenService> | null = null;\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n let reject!: (reason?: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n}\n\nfunction isAuthenticateGenService(value: unknown): value is AuthenticateGenService {\n return Boolean(\n value &&\n typeof (value as { authorize?: unknown }).authorize === \"function\"\n );\n}\n\n/** Segnala al middleware che il bootstrap e in corso, consentendo una risposta 503 temporanea invece di un 500. */\nexport function beginAccessiAuthInitialization() {\n if (!authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred = createDeferred<AuthenticateGenService>();\n }\n}\n\n/** Registra il servizio costruito da Nest nel bridge usato dalle rotte Express dell'app ospitante. */\nexport function setAccessiAuthService(service: AuthenticateGenService) {\n authenticateGenServiceRef = service;\n (globalThis as Record<symbol, unknown>)[ACCESSI_AUTH_SERVICE_GLOBAL_KEY] = service;\n if (authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred.resolve(service);\n authServiceBootstrapDeferred = null;\n }\n}\n\n/** Propaga il fallimento del bootstrap alle richieste che stavano attendendo il bridge di autorizzazione. */\nexport function failAccessiAuthInitialization(error: unknown) {\n if (authServiceBootstrapDeferred) {\n authServiceBootstrapDeferred.reject(error);\n authServiceBootstrapDeferred = null;\n }\n}\n\nfunction getAccessiAuthServiceFromRequest(req: Request): AuthenticateGenService | null {\n const appLocalsService = (req.app?.locals as Record<string, unknown> | undefined)?.[\n ACCESSI_AUTH_SERVICE_LOCALS_KEY\n ];\n\n if (isAuthenticateGenService(appLocalsService)) {\n return appLocalsService as AuthenticateGenService;\n }\n\n const globalService = (globalThis as Record<symbol, unknown>)[\n ACCESSI_AUTH_SERVICE_GLOBAL_KEY\n ];\n if (isAuthenticateGenService(globalService)) {\n return globalService as AuthenticateGenService;\n }\n\n return authenticateGenServiceRef;\n}\n\nasync function waitForBootstrappedService(): Promise<AuthenticateGenService | null> {\n if (!authServiceBootstrapDeferred) {\n return null;\n }\n\n const bootstrapPromise = authServiceBootstrapDeferred.promise;\n const timeoutPromise = new Promise<null>((resolve) => {\n setTimeout(() => resolve(null), ACCESSI_AUTH_SERVICE_WAIT_MS);\n });\n\n try {\n const resolved = await Promise.race([bootstrapPromise, timeoutPromise]);\n return isAuthenticateGenService(resolved) ? resolved : null;\n } catch {\n return null;\n }\n}\n\nasync function resolveAccessiAuthService(req: Request): Promise<AuthenticateGenService | null> {\n const immediateService = getAccessiAuthServiceFromRequest(req);\n if (immediateService) {\n return immediateService;\n }\n\n const bootstrappedService = await waitForBootstrappedService();\n if (bootstrappedService) {\n return bootstrappedService;\n }\n\n return getAccessiAuthServiceFromRequest(req);\n}\n\n/**\n * Middleware Express pubblico di Accessi.\n *\n * Applicarlo dopo `initializeAccessiModule`: legge `Authorization: Bearer <jwt>`, verifica l'utente corrente\n * e applica eventuali requisiti. Su successo valorizza `req.user` e continua; su errore risponde direttamente\n * con il contratto Accessi, quindi il chiamante non deve aggiungere un proprio `next(error)`.\n */\nexport async function authorizeAccessi(\n req: Request,\n res: Response,\n next: NextFunction,\n options?: AccessiAuthorizationOptions\n) {\n const authService = await resolveAccessiAuthService(req);\n\n if (!authService) {\n const status = authServiceBootstrapDeferred ? 503 : 500;\n const code = authServiceBootstrapDeferred\n ? 'ACCESSI_AUTH_INITIALIZING'\n : 'ACCESSI_AUTH_NOT_INITIALIZED';\n logger.error(\n `Authentication service not initialized ${JSON.stringify({\n method: req.method,\n path: req.originalUrl ?? req.url,\n status,\n })}`\n );\n return res.status(status).json({\n severity: 'error',\n status,\n statusCode: 2,\n code,\n error: code,\n message: status === 503\n ? 'Il modulo Accessi e in inizializzazione. Riprova tra poco.'\n : 'Il modulo Accessi non e disponibile.',\n });\n }\n\n return authService.authorize(req, res, next, options);\n}\n\n/** Alias storico di `authorizeAccessi`, mantenuto per retrocompatibilita. Preferire il nome esplicito nelle nuove integrazioni. */\nexport const authenticateGen = authorizeAccessi;\n"]}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Audit strutturato degli eventi di sicurezza Accessi.
3
+ *
4
+ * Il Logger redige gia campi come token, password, secret e authorization.
5
+ * Non inserire mai payload completi o segreti nei dettagli.
6
+ */
7
+ export declare function auditAccessi(event: string, details?: Record<string, unknown>): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.auditAccessi = auditAccessi;
4
+ const Logger_1 = require("../../Logger");
5
+ /**
6
+ * Audit strutturato degli eventi di sicurezza Accessi.
7
+ *
8
+ * Il Logger redige gia campi come token, password, secret e authorization.
9
+ * Non inserire mai payload completi o segreti nei dettagli.
10
+ */
11
+ function auditAccessi(event, details = {}) {
12
+ new Logger_1.Logger('AccessiAudit').warning(JSON.stringify(Object.assign({ event, timestamp: new Date().toISOString() }, details)));
13
+ }
14
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/accessi-module/security/audit.ts"],"names":[],"mappings":";;AAQA,oCAQC;AAhBD,yCAAsC;AAEtC;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,KAAa,EAAE,UAAmC,EAAE;IAC/E,IAAI,eAAM,CAAC,cAAc,CAAC,CAAC,OAAO,CAChC,IAAI,CAAC,SAAS,iBACZ,KAAK,EACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAChC,OAAO,EACV,CACH,CAAC;AACJ,CAAC","sourcesContent":["import { Logger } from '../../Logger';\n\n/**\n * Audit strutturato degli eventi di sicurezza Accessi.\n *\n * Il Logger redige gia campi come token, password, secret e authorization.\n * Non inserire mai payload completi o segreti nei dettagli.\n */\nexport function auditAccessi(event: string, details: Record<string, unknown> = {}): void {\n new Logger('AccessiAudit').warning(\n JSON.stringify({\n event,\n timestamp: new Date().toISOString(),\n ...details,\n }),\n );\n}\n"]}
@@ -13,7 +13,7 @@ export interface AccessiAuthenticatedUserSnapshot {
13
13
  /** Shared, strict Bearer parsing for the Nest guard and Express middleware. */
14
14
  export declare function extractAccessiBearerToken(header: unknown): string | undefined;
15
15
  /** Estrae il codice utente sia dai JWT Accessi moderni sia dai payload legacy compatibili. */
16
- export declare function resolveCodiceUtenteFromTokenPayload(decoded: unknown): number | undefined;
16
+ export declare function resolveCodiceUtenteFromTokenPayload(decoded: unknown, expectedType?: 'access' | 'refresh'): number | undefined;
17
17
  /**
18
18
  * Mantiene le claim estranee al modulo ma sovrascrive il profilo con valori correnti e autorevoli dal database.
19
19
  * `tokenPayload` conserva il payload verificato originale per audit o integrazioni che ne abbiano necessita.
@@ -21,10 +21,10 @@ function extractAccessiBearerToken(header) {
21
21
  return (_a = /^Bearer[ \t]+([^\s]+)$/i.exec(header.trim())) === null || _a === void 0 ? void 0 : _a[1];
22
22
  }
23
23
  /** Estrae il codice utente sia dai JWT Accessi moderni sia dai payload legacy compatibili. */
24
- function resolveCodiceUtenteFromTokenPayload(decoded) {
24
+ function resolveCodiceUtenteFromTokenPayload(decoded, expectedType = 'access') {
25
25
  var _a;
26
- // `typ` obbligatorio: un token senza tipo non e un access token Accessi.
27
- if (!isRecord(decoded) || decoded.typ !== 'access') {
26
+ // `typ` obbligatorio: un token senza tipo non e un token Accessi valido.
27
+ if (!isRecord(decoded) || decoded.typ !== expectedType) {
28
28
  return undefined;
29
29
  }
30
30
  const userData = isRecord(decoded.userData) ? decoded.userData : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"authenticatedToken.js","sourceRoot":"","sources":["../../../src/accessi-module/security/authenticatedToken.ts"],"names":[],"mappings":"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","sourcesContent":["import { StatoRegistrazione } from '../Dtos/StatoRegistrazione';\n\n/** Stato autorevole minimo riletto dal database per ogni verifica JWT. */\nexport interface AccessiAuthenticatedUserSnapshot {\n codiceUtente: number;\n email?: string;\n statoRegistrazione: StatoRegistrazione;\n flagSuper: boolean;\n flagAdmin: boolean;\n flagDueFattori?: boolean;\n passwordlessLoginEnabled?: boolean;\n passwordLoginEnabled?: boolean;\n}\n\nfunction normalizeBooleanFlag(value: unknown): boolean {\n return value === true || value === 1 || value === '1';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Shared, strict Bearer parsing for the Nest guard and Express middleware. */\nexport function extractAccessiBearerToken(header: unknown): string | undefined {\n if (typeof header !== 'string') return undefined;\n return /^Bearer[ \\t]+([^\\s]+)$/i.exec(header.trim())?.[1];\n}\n\n/** Estrae il codice utente sia dai JWT Accessi moderni sia dai payload legacy compatibili. */\nexport function resolveCodiceUtenteFromTokenPayload(decoded: unknown): number | undefined {\n // `typ` obbligatorio: un token senza tipo non e un access token Accessi.\n if (!isRecord(decoded) || decoded.typ !== 'access') {\n return undefined;\n }\n\n const userData = isRecord(decoded.userData) ? decoded.userData : undefined;\n const utente = isRecord(decoded.utente)\n ? decoded.utente\n : isRecord(userData?.utente)\n ? userData.utente\n : undefined;\n\n const codiceUtente = Number(utente?.codiceUtente ?? decoded.codiceUtente);\n if (!Number.isSafeInteger(codiceUtente) || codiceUtente <= 0) {\n return undefined;\n }\n\n return codiceUtente;\n}\n\n/**\n * Mantiene le claim estranee al modulo ma sovrascrive il profilo con valori correnti e autorevoli dal database.\n * `tokenPayload` conserva il payload verificato originale per audit o integrazioni che ne abbiano necessita.\n */\nexport function buildAuthenticatedTokenPayload(\n decoded: unknown,\n utente: AccessiAuthenticatedUserSnapshot,\n): Record<string, unknown> {\n const basePayload = isRecord(decoded) ? { ...decoded } : {};\n const userData = isRecord(basePayload.userData) ? { ...basePayload.userData } : {};\n\n const normalizedUser = {\n ...utente,\n flagSuper: normalizeBooleanFlag(utente.flagSuper),\n flagAdmin: normalizeBooleanFlag(utente.flagAdmin),\n flagDueFattori: normalizeBooleanFlag(utente.flagDueFattori),\n passwordlessLoginEnabled: normalizeBooleanFlag(utente.passwordlessLoginEnabled),\n };\n\n return {\n ...basePayload,\n codiceUtente: normalizedUser.codiceUtente,\n email: normalizedUser.email,\n statoRegistrazione: normalizedUser.statoRegistrazione,\n flagSuper: normalizedUser.flagSuper,\n flagAdmin: normalizedUser.flagAdmin,\n flagDueFattori: normalizedUser.flagDueFattori,\n passwordlessLoginEnabled: normalizedUser.passwordlessLoginEnabled,\n passwordLoginEnabled: normalizedUser.passwordLoginEnabled,\n utente: normalizedUser,\n userData: {\n ...userData,\n utente: normalizedUser,\n },\n tokenPayload: decoded,\n };\n}\n\n/**\n * Riconosce i token emessi dalla console per l'accesso temporaneo \"Login as\".\n * L'attore ha gia completato l'autenticazione amministrativa, quindi il token\n * non richiede un ulteriore codice email anche se il target ha la 2FA attiva.\n */\nexport function isLoginAsTokenPayload(decoded: unknown): boolean {\n if (!isRecord(decoded) || decoded.loginAs !== true) return false;\n return Array.isArray(decoded.amr) && decoded.amr.includes('login_as');\n}\n\n/** Enabling 2FA invalidates sessions which have not completed the required verification. */\nexport function isAccessiTokenAllowedForUser(decoded: unknown, user: AccessiAuthenticatedUserSnapshot): boolean {\n if (!isRecord(decoded)) return false;\n if (isLoginAsTokenPayload(decoded)) return true;\n const methods = Array.isArray(decoded.amr) ? decoded.amr : [];\n if (user.flagDueFattori && !methods.includes('otp')) return false;\n if (methods.includes('passwordless') && (!user.flagDueFattori || !user.passwordlessLoginEnabled)) return false;\n return true;\n}\n\n/** Un JWT puo essere usato soltanto da utenti nello stato confermato, indipendentemente dalla sua scadenza. */\nexport function isAuthenticatedUserEnabledForJwt(\n utente: AccessiAuthenticatedUserSnapshot | null | undefined,\n): utente is AccessiAuthenticatedUserSnapshot {\n return utente?.statoRegistrazione === StatoRegistrazione.CONF;\n}\n"]}
1
+ {"version":3,"file":"authenticatedToken.js","sourceRoot":"","sources":["../../../src/accessi-module/security/authenticatedToken.ts"],"names":[],"mappings":";;AAuBA,8DAGC;AAGD,kFAsBC;AAMD,wEAgCC;AAOD,sDAGC;AAGD,oEAOC;AAGD,4EAIC;AApHD,mEAAgE;AAchE,SAAS,oBAAoB,CAAC,KAAc;IAC1C,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,CAAC;AACxD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,+EAA+E;AAC/E,SAAgB,yBAAyB,CAAC,MAAe;;IACvD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACjD,OAAO,MAAA,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,0CAAG,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,8FAA8F;AAC9F,SAAgB,mCAAmC,CACjD,OAAgB,EAChB,eAAqC,QAAQ;;IAE7C,yEAAyE;IACzE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,KAAK,YAAY,EAAE,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;QACrC,CAAC,CAAC,OAAO,CAAC,MAAM;QAChB,CAAC,CAAC,QAAQ,CAAC,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,CAAC;YAC1B,CAAC,CAAC,QAAQ,CAAC,MAAM;YACjB,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,YAAY,GAAG,MAAM,CAAC,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,YAAY,mCAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,SAAgB,8BAA8B,CAC5C,OAAgB,EAChB,MAAwC;IAExC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAM,OAAO,EAAG,CAAC,CAAC,EAAE,CAAC;IAC5D,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAM,WAAW,CAAC,QAAQ,EAAG,CAAC,CAAC,EAAE,CAAC;IAEnF,MAAM,cAAc,mCACf,MAAM,KACT,SAAS,EAAE,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,EACjD,SAAS,EAAE,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,EACjD,cAAc,EAAE,oBAAoB,CAAC,MAAM,CAAC,cAAc,CAAC,EAC3D,wBAAwB,EAAE,oBAAoB,CAAC,MAAM,CAAC,wBAAwB,CAAC,GAChF,CAAC;IAEF,uCACK,WAAW,KACd,YAAY,EAAE,cAAc,CAAC,YAAY,EACzC,KAAK,EAAE,cAAc,CAAC,KAAK,EAC3B,kBAAkB,EAAE,cAAc,CAAC,kBAAkB,EACrD,SAAS,EAAE,cAAc,CAAC,SAAS,EACnC,SAAS,EAAE,cAAc,CAAC,SAAS,EACnC,cAAc,EAAE,cAAc,CAAC,cAAc,EAC7C,wBAAwB,EAAE,cAAc,CAAC,wBAAwB,EACjE,oBAAoB,EAAE,cAAc,CAAC,oBAAoB,EACzD,MAAM,EAAE,cAAc,EACtB,QAAQ,kCACH,QAAQ,KACX,MAAM,EAAE,cAAc,KAExB,YAAY,EAAE,OAAO,IACrB;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,qBAAqB,CAAC,OAAgB;IACpD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjE,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACxE,CAAC;AAED,4FAA4F;AAC5F,SAAgB,4BAA4B,CAAC,OAAgB,EAAE,IAAsC;IACnG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,qBAAqB,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/G,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+GAA+G;AAC/G,SAAgB,gCAAgC,CAC9C,MAA2D;IAE3D,OAAO,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,kBAAkB,MAAK,uCAAkB,CAAC,IAAI,CAAC;AAChE,CAAC","sourcesContent":["import { StatoRegistrazione } from '../Dtos/StatoRegistrazione';\n\n/** Stato autorevole minimo riletto dal database per ogni verifica JWT. */\nexport interface AccessiAuthenticatedUserSnapshot {\n codiceUtente: number;\n email?: string;\n statoRegistrazione: StatoRegistrazione;\n flagSuper: boolean;\n flagAdmin: boolean;\n flagDueFattori?: boolean;\n passwordlessLoginEnabled?: boolean;\n passwordLoginEnabled?: boolean;\n}\n\nfunction normalizeBooleanFlag(value: unknown): boolean {\n return value === true || value === 1 || value === '1';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Shared, strict Bearer parsing for the Nest guard and Express middleware. */\nexport function extractAccessiBearerToken(header: unknown): string | undefined {\n if (typeof header !== 'string') return undefined;\n return /^Bearer[ \\t]+([^\\s]+)$/i.exec(header.trim())?.[1];\n}\n\n/** Estrae il codice utente sia dai JWT Accessi moderni sia dai payload legacy compatibili. */\nexport function resolveCodiceUtenteFromTokenPayload(\n decoded: unknown,\n expectedType: 'access' | 'refresh' = 'access',\n): number | undefined {\n // `typ` obbligatorio: un token senza tipo non e un token Accessi valido.\n if (!isRecord(decoded) || decoded.typ !== expectedType) {\n return undefined;\n }\n\n const userData = isRecord(decoded.userData) ? decoded.userData : undefined;\n const utente = isRecord(decoded.utente)\n ? decoded.utente\n : isRecord(userData?.utente)\n ? userData.utente\n : undefined;\n\n const codiceUtente = Number(utente?.codiceUtente ?? decoded.codiceUtente);\n if (!Number.isSafeInteger(codiceUtente) || codiceUtente <= 0) {\n return undefined;\n }\n\n return codiceUtente;\n}\n\n/**\n * Mantiene le claim estranee al modulo ma sovrascrive il profilo con valori correnti e autorevoli dal database.\n * `tokenPayload` conserva il payload verificato originale per audit o integrazioni che ne abbiano necessita.\n */\nexport function buildAuthenticatedTokenPayload(\n decoded: unknown,\n utente: AccessiAuthenticatedUserSnapshot,\n): Record<string, unknown> {\n const basePayload = isRecord(decoded) ? { ...decoded } : {};\n const userData = isRecord(basePayload.userData) ? { ...basePayload.userData } : {};\n\n const normalizedUser = {\n ...utente,\n flagSuper: normalizeBooleanFlag(utente.flagSuper),\n flagAdmin: normalizeBooleanFlag(utente.flagAdmin),\n flagDueFattori: normalizeBooleanFlag(utente.flagDueFattori),\n passwordlessLoginEnabled: normalizeBooleanFlag(utente.passwordlessLoginEnabled),\n };\n\n return {\n ...basePayload,\n codiceUtente: normalizedUser.codiceUtente,\n email: normalizedUser.email,\n statoRegistrazione: normalizedUser.statoRegistrazione,\n flagSuper: normalizedUser.flagSuper,\n flagAdmin: normalizedUser.flagAdmin,\n flagDueFattori: normalizedUser.flagDueFattori,\n passwordlessLoginEnabled: normalizedUser.passwordlessLoginEnabled,\n passwordLoginEnabled: normalizedUser.passwordLoginEnabled,\n utente: normalizedUser,\n userData: {\n ...userData,\n utente: normalizedUser,\n },\n tokenPayload: decoded,\n };\n}\n\n/**\n * Riconosce i token emessi dalla console per l'accesso temporaneo \"Login as\".\n * L'attore ha gia completato l'autenticazione amministrativa, quindi il token\n * non richiede un ulteriore codice email anche se il target ha la 2FA attiva.\n */\nexport function isLoginAsTokenPayload(decoded: unknown): boolean {\n if (!isRecord(decoded) || decoded.loginAs !== true) return false;\n return Array.isArray(decoded.amr) && decoded.amr.includes('login_as');\n}\n\n/** Enabling 2FA invalidates sessions which have not completed the required verification. */\nexport function isAccessiTokenAllowedForUser(decoded: unknown, user: AccessiAuthenticatedUserSnapshot): boolean {\n if (!isRecord(decoded)) return false;\n if (isLoginAsTokenPayload(decoded)) return true;\n const methods = Array.isArray(decoded.amr) ? decoded.amr : [];\n if (user.flagDueFattori && !methods.includes('otp')) return false;\n if (methods.includes('passwordless') && (!user.flagDueFattori || !user.passwordlessLoginEnabled)) return false;\n return true;\n}\n\n/** Un JWT puo essere usato soltanto da utenti nello stato confermato, indipendentemente dalla sua scadenza. */\nexport function isAuthenticatedUserEnabledForJwt(\n utente: AccessiAuthenticatedUserSnapshot | null | undefined,\n): utente is AccessiAuthenticatedUserSnapshot {\n return utente?.statoRegistrazione === StatoRegistrazione.CONF;\n}\n"]}
@@ -1,16 +1,63 @@
1
+ /**
2
+ * Ticket di ingresso alla console, monouso e a breve scadenza.
3
+ *
4
+ * Serve a passare una sessione gia autenticata (frontend che possiede un JWT Accessi)
5
+ * alla console amministrativa senza reinserire le credenziali e senza mettere il JWT
6
+ * nell'URL: il frontend scambia il proprio JWT con un ticket opaco, apre la console
7
+ * con `#entry=<ticket>` e la console lo scambia con il token originale.
8
+ *
9
+ * Il ticket e valido una sola volta, per pochi secondi, ed e legato all'utente che
10
+ * lo ha richiesto (che deve avere accesso alla console). Lo store di default e in
11
+ * memoria; in multi-istanza usare `createRedisConsoleEntryStore`.
12
+ */
13
+ export interface ConsoleEntryRecord {
14
+ token: string;
15
+ codiceUtente: number;
16
+ }
1
17
  export interface ConsoleEntryTicket {
2
18
  ticket: string;
3
19
  expiresInSeconds: number;
4
20
  }
5
- /** Emette un ticket monouso per l'utente indicato, conservando il suo JWT per lo scambio. */
6
- export declare function issueConsoleEntryTicket(record: {
7
- token: string;
8
- codiceUtente: number;
9
- }, ttlMs?: number): ConsoleEntryTicket;
10
- /** Consuma (monouso) un ticket e restituisce il JWT collegato, oppure null se scaduto/assente/invalido. */
11
- export declare function consumeConsoleEntryTicket(ticket: unknown): {
12
- token: string;
13
- codiceUtente: number;
14
- } | null;
15
- /** Solo per i test: azzera lo store in memoria. */
21
+ /** Store pluggable: memoria per single instance, Redis per multi-istanza. */
22
+ export interface ConsoleEntryStore {
23
+ issue(record: ConsoleEntryRecord, ttlMs: number): Promise<ConsoleEntryTicket>;
24
+ consume(ticket: string): Promise<ConsoleEntryRecord | null>;
25
+ /** Health check opzionale per readiness del servizio. */
26
+ healthCheck?(): Promise<boolean>;
27
+ }
28
+ /** Store in memoria con API sincrona per retrocompatibilita e API async per l'interfaccia. */
29
+ export interface MemoryConsoleEntryStore extends ConsoleEntryStore {
30
+ issueSync(record: ConsoleEntryRecord, ttlMs?: number): ConsoleEntryTicket;
31
+ consumeSync(ticket: string): ConsoleEntryRecord | null;
32
+ clear(): void;
33
+ }
34
+ /** Store in memoria: adatto a sviluppo e a una singola istanza. */
35
+ export declare function createMemoryConsoleEntryStore(maxEntries?: number): MemoryConsoleEntryStore;
36
+ /** Store di default in memoria, usato quando `AccessiOptions.consoleEntry.store` non e configurato. */
37
+ export declare function getDefaultConsoleEntryStore(): ConsoleEntryStore;
38
+ export declare function issueConsoleEntryTicket(record: ConsoleEntryRecord, ttlMs?: number): ConsoleEntryTicket;
39
+ export declare function consumeConsoleEntryTicket(ticket: unknown): ConsoleEntryRecord | null;
40
+ /** Solo per i test: azzera lo store in memoria di default. */
16
41
  export declare function clearConsoleEntryTickets(): void;
42
+ /** Client Redis minimale: compatibile con ioredis (`redis.eval(script, numKeys, ...args)`). */
43
+ export interface ConsoleEntryRedisClient {
44
+ eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;
45
+ }
46
+ export interface RedisConsoleEntryStoreOptions {
47
+ client: ConsoleEntryRedisClient;
48
+ /** Segreto per cifrare il JWT nel Redis. Non usare il JWT in chiaro. */
49
+ encryptionKey: string | Buffer;
50
+ /** Default: `accessi:console-entry:`. */
51
+ keyPrefix?: string;
52
+ /** Tentativi in caso di collisione del ticket. Default: 5. */
53
+ maxIssueAttempts?: number;
54
+ }
55
+ /**
56
+ * Store Redis per ticket console.
57
+ *
58
+ * - il JWT viene cifrato con AES-256-GCM prima di essere scritto;
59
+ * - `SET ... PX ... NX` evita collisioni;
60
+ * - il consume usa `GET` + `DEL` atomici in Lua, quindi il ticket e monouso
61
+ * anche con piu istanze.
62
+ */
63
+ export declare function createRedisConsoleEntryStore(options: RedisConsoleEntryStoreOptions): ConsoleEntryStore;
@@ -1,50 +1,200 @@
1
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
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createMemoryConsoleEntryStore = createMemoryConsoleEntryStore;
13
+ exports.getDefaultConsoleEntryStore = getDefaultConsoleEntryStore;
3
14
  exports.issueConsoleEntryTicket = issueConsoleEntryTicket;
4
15
  exports.consumeConsoleEntryTicket = consumeConsoleEntryTicket;
5
16
  exports.clearConsoleEntryTickets = clearConsoleEntryTickets;
17
+ exports.createRedisConsoleEntryStore = createRedisConsoleEntryStore;
6
18
  const crypto_1 = require("crypto");
7
19
  const DEFAULT_TTL_MS = 60000;
8
- const MAX_ENTRIES = 5000;
9
- const entries = new Map();
10
- function sweep(now) {
11
- for (const [ticket, record] of entries) {
12
- if (record.expiresAt <= now)
13
- entries.delete(ticket);
14
- }
20
+ const DEFAULT_MAX_ENTRIES = 5000;
21
+ const TICKET_PATTERN = /^[a-f0-9]{64}$/i;
22
+ function createTicket() {
23
+ return (0, crypto_1.randomBytes)(32).toString('hex');
24
+ }
25
+ function normalizeTtl(ttlMs) {
26
+ return Math.max(1, Math.trunc(ttlMs));
27
+ }
28
+ /** Store in memoria: adatto a sviluppo e a una singola istanza. */
29
+ function createMemoryConsoleEntryStore(maxEntries = DEFAULT_MAX_ENTRIES) {
30
+ const entries = new Map();
31
+ const sweep = (now) => {
32
+ for (const [ticket, record] of entries) {
33
+ if (record.expiresAt <= now)
34
+ entries.delete(ticket);
35
+ }
36
+ };
37
+ const issueSync = (record, ttlMs = DEFAULT_TTL_MS) => {
38
+ // Il TTL negativo resta supportato per test e per il comportamento storico:
39
+ // il ticket e immediatamente scaduto.
40
+ const ttl = Math.trunc(ttlMs);
41
+ const now = Date.now();
42
+ sweep(now);
43
+ while (entries.size >= maxEntries) {
44
+ const oldest = entries.keys().next().value;
45
+ if (oldest === undefined)
46
+ break;
47
+ entries.delete(oldest);
48
+ }
49
+ const ticket = createTicket();
50
+ entries.set(ticket, Object.assign(Object.assign({}, record), { expiresAt: now + ttl }));
51
+ return { ticket, expiresInSeconds: Math.max(0, Math.ceil(ttl / 1000)) };
52
+ };
53
+ const consumeSync = (ticket) => {
54
+ if (typeof ticket !== 'string' || !TICKET_PATTERN.test(ticket)) {
55
+ return null;
56
+ }
57
+ const record = entries.get(ticket);
58
+ if (!record)
59
+ return null;
60
+ entries.delete(ticket);
61
+ if (record.expiresAt <= Date.now())
62
+ return null;
63
+ return { token: record.token, codiceUtente: record.codiceUtente };
64
+ };
65
+ return {
66
+ issueSync,
67
+ consumeSync,
68
+ clear: () => entries.clear(),
69
+ issue(record, ttlMs) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ return issueSync(record, ttlMs);
72
+ });
73
+ },
74
+ consume(ticket) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ return consumeSync(ticket);
77
+ });
78
+ },
79
+ healthCheck() {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ return true;
82
+ });
83
+ },
84
+ };
85
+ }
86
+ const defaultStore = createMemoryConsoleEntryStore();
87
+ /** Store di default in memoria, usato quando `AccessiOptions.consoleEntry.store` non e configurato. */
88
+ function getDefaultConsoleEntryStore() {
89
+ return defaultStore;
15
90
  }
16
- /** Emette un ticket monouso per l'utente indicato, conservando il suo JWT per lo scambio. */
91
+ // Funzioni storiche sincrone: mantengono il comportamento per test e consumer esistenti.
17
92
  function issueConsoleEntryTicket(record, ttlMs = DEFAULT_TTL_MS) {
18
- const now = Date.now();
19
- sweep(now);
20
- // Limita la memoria anche sotto attacco: rimuove la voce piu vecchia.
21
- while (entries.size >= MAX_ENTRIES) {
22
- const oldest = entries.keys().next().value;
23
- if (oldest === undefined)
24
- break;
25
- entries.delete(oldest);
26
- }
27
- const ticket = (0, crypto_1.randomBytes)(32).toString('hex');
28
- entries.set(ticket, Object.assign(Object.assign({}, record), { expiresAt: now + ttlMs }));
29
- return { ticket, expiresInSeconds: Math.ceil(ttlMs / 1000) };
93
+ return defaultStore.issueSync(record, ttlMs);
30
94
  }
31
- /** Consuma (monouso) un ticket e restituisce il JWT collegato, oppure null se scaduto/assente/invalido. */
32
95
  function consumeConsoleEntryTicket(ticket) {
33
- if (typeof ticket !== 'string' || !/^[a-f0-9]{64}$/i.test(ticket)) {
34
- return null;
96
+ return typeof ticket === 'string' ? defaultStore.consumeSync(ticket) : null;
97
+ }
98
+ /** Solo per i test: azzera lo store in memoria di default. */
99
+ function clearConsoleEntryTickets() {
100
+ defaultStore.clear();
101
+ }
102
+ const SET_SCRIPT = "return redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2], 'NX')";
103
+ const CONSUME_SCRIPT = "local value = redis.call('GET', KEYS[1]) if value then redis.call('DEL', KEYS[1]) end return value";
104
+ function deriveEncryptionKey(secret) {
105
+ const source = Buffer.isBuffer(secret) ? secret : Buffer.from(secret, 'utf8');
106
+ if (source.length === 0) {
107
+ throw new Error('Redis console entry encryptionKey non configurata.');
35
108
  }
36
- const record = entries.get(ticket);
37
- if (!record) {
109
+ return source.length === 32 ? source : (0, crypto_1.createHash)('sha256').update(source).digest();
110
+ }
111
+ function encryptRecord(record, key) {
112
+ const iv = (0, crypto_1.randomBytes)(12);
113
+ const cipher = (0, crypto_1.createCipheriv)('aes-256-gcm', key, iv);
114
+ const ciphertext = Buffer.concat([
115
+ cipher.update(JSON.stringify(record), 'utf8'),
116
+ cipher.final(),
117
+ ]);
118
+ const tag = cipher.getAuthTag();
119
+ return [iv, tag, ciphertext].map((part) => part.toString('base64url')).join('.');
120
+ }
121
+ function decryptRecord(value, key) {
122
+ const [ivPart, tagPart, ciphertextPart] = value.split('.');
123
+ if (!ivPart || !tagPart || !ciphertextPart)
38
124
  return null;
125
+ try {
126
+ const decipher = (0, crypto_1.createDecipheriv)('aes-256-gcm', key, Buffer.from(ivPart, 'base64url'));
127
+ decipher.setAuthTag(Buffer.from(tagPart, 'base64url'));
128
+ const plaintext = Buffer.concat([
129
+ decipher.update(Buffer.from(ciphertextPart, 'base64url')),
130
+ decipher.final(),
131
+ ]).toString('utf8');
132
+ const parsed = JSON.parse(plaintext);
133
+ if (!parsed ||
134
+ typeof parsed !== 'object' ||
135
+ typeof parsed.token !== 'string' ||
136
+ typeof parsed.codiceUtente !== 'number') {
137
+ return null;
138
+ }
139
+ return parsed;
39
140
  }
40
- entries.delete(ticket);
41
- if (record.expiresAt <= Date.now()) {
141
+ catch (_a) {
42
142
  return null;
43
143
  }
44
- return { token: record.token, codiceUtente: record.codiceUtente };
45
144
  }
46
- /** Solo per i test: azzera lo store in memoria. */
47
- function clearConsoleEntryTickets() {
48
- entries.clear();
145
+ /**
146
+ * Store Redis per ticket console.
147
+ *
148
+ * - il JWT viene cifrato con AES-256-GCM prima di essere scritto;
149
+ * - `SET ... PX ... NX` evita collisioni;
150
+ * - il consume usa `GET` + `DEL` atomici in Lua, quindi il ticket e monouso
151
+ * anche con piu istanze.
152
+ */
153
+ function createRedisConsoleEntryStore(options) {
154
+ var _a, _b;
155
+ if (!(options === null || options === void 0 ? void 0 : options.client) || typeof options.client.eval !== 'function') {
156
+ throw new Error('Redis console entry store: client.eval non configurato.');
157
+ }
158
+ const key = deriveEncryptionKey(options.encryptionKey);
159
+ const keyPrefix = ((_a = options.keyPrefix) !== null && _a !== void 0 ? _a : 'accessi:console-entry:').replace(/:+$/, ':');
160
+ const maxAttempts = Math.max(1, (_b = options.maxIssueAttempts) !== null && _b !== void 0 ? _b : 5);
161
+ return {
162
+ issue(record, ttlMs) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ const safeTtl = normalizeTtl(ttlMs);
165
+ const value = encryptRecord(record, key);
166
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
167
+ const ticket = createTicket();
168
+ const result = yield options.client.eval(SET_SCRIPT, 1, `${keyPrefix}${ticket}`, value, safeTtl);
169
+ if (result) {
170
+ return { ticket, expiresInSeconds: Math.ceil(safeTtl / 1000) };
171
+ }
172
+ }
173
+ throw new Error('Impossibile emettere un ticket console univoco.');
174
+ });
175
+ },
176
+ consume(ticket) {
177
+ return __awaiter(this, void 0, void 0, function* () {
178
+ if (typeof ticket !== 'string' || !TICKET_PATTERN.test(ticket)) {
179
+ return null;
180
+ }
181
+ const value = yield options.client.eval(CONSUME_SCRIPT, 1, `${keyPrefix}${ticket}`);
182
+ if (typeof value !== 'string')
183
+ return null;
184
+ return decryptRecord(value, key);
185
+ });
186
+ },
187
+ healthCheck() {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ try {
190
+ const result = yield options.client.eval("return redis.call('PING')", 0);
191
+ return String(result).toUpperCase() === 'PONG';
192
+ }
193
+ catch (_a) {
194
+ return false;
195
+ }
196
+ });
197
+ },
198
+ };
49
199
  }
50
200
  //# sourceMappingURL=consoleEntry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consoleEntry.js","sourceRoot":"","sources":["../../../src/accessi-module/security/consoleEntry.ts"],"names":[],"mappings":";;AAqCA,0DAiBC;AAGD,8DAeC;AAGD,4DAEC;AA7ED,mCAAqC;AAqBrC,MAAM,cAAc,GAAG,KAAM,CAAC;AAC9B,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;AAOtD,SAAS,KAAK,CAAC,GAAW;IACxB,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,SAAS,IAAI,GAAG;YAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,SAAgB,uBAAuB,CACrC,MAA+C,EAC/C,QAAgB,cAAc;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,KAAK,CAAC,GAAG,CAAC,CAAC;IAEX,sEAAsE;IACtE,OAAO,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC3C,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM;QAChC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,kCAAO,MAAM,KAAE,SAAS,EAAE,GAAG,GAAG,KAAK,IAAG,CAAC;IAC3D,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED,2GAA2G;AAC3G,SAAgB,yBAAyB,CACvC,MAAe;IAEf,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;AACpE,CAAC;AAED,mDAAmD;AACnD,SAAgB,wBAAwB;IACtC,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC","sourcesContent":["import { randomBytes } from 'crypto';\n\n/**\n * Ticket di ingresso alla console, monouso e a breve scadenza.\n *\n * Serve a passare una sessione gia autenticata (frontend che possiede un JWT Accessi)\n * alla console amministrativa senza reinserire le credenziali e senza mettere il JWT\n * nell'URL: il frontend scambia il proprio JWT con un ticket opaco, apre la console\n * con `#entry=<ticket>` e la console lo scambia con il token originale.\n *\n * Il ticket e' valido una sola volta, per pochi secondi, ed e' legato all'utente che\n * lo ha richiesto (che deve avere accesso alla console). Resta in memoria del processo:\n * con piu istanze del backend il ticket va consumato dalla stessa istanza che lo emette.\n */\n\ninterface ConsoleEntryRecord {\n token: string;\n codiceUtente: number;\n expiresAt: number;\n}\n\nconst DEFAULT_TTL_MS = 60_000;\nconst MAX_ENTRIES = 5000;\nconst entries = new Map<string, ConsoleEntryRecord>();\n\nexport interface ConsoleEntryTicket {\n ticket: string;\n expiresInSeconds: number;\n}\n\nfunction sweep(now: number): void {\n for (const [ticket, record] of entries) {\n if (record.expiresAt <= now) entries.delete(ticket);\n }\n}\n\n/** Emette un ticket monouso per l'utente indicato, conservando il suo JWT per lo scambio. */\nexport function issueConsoleEntryTicket(\n record: { token: string; codiceUtente: number },\n ttlMs: number = DEFAULT_TTL_MS,\n): ConsoleEntryTicket {\n const now = Date.now();\n sweep(now);\n\n // Limita la memoria anche sotto attacco: rimuove la voce piu vecchia.\n while (entries.size >= MAX_ENTRIES) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n\n const ticket = randomBytes(32).toString('hex');\n entries.set(ticket, { ...record, expiresAt: now + ttlMs });\n return { ticket, expiresInSeconds: Math.ceil(ttlMs / 1000) };\n}\n\n/** Consuma (monouso) un ticket e restituisce il JWT collegato, oppure null se scaduto/assente/invalido. */\nexport function consumeConsoleEntryTicket(\n ticket: unknown,\n): { token: string; codiceUtente: number } | null {\n if (typeof ticket !== 'string' || !/^[a-f0-9]{64}$/i.test(ticket)) {\n return null;\n }\n const record = entries.get(ticket);\n if (!record) {\n return null;\n }\n entries.delete(ticket);\n if (record.expiresAt <= Date.now()) {\n return null;\n }\n return { token: record.token, codiceUtente: record.codiceUtente };\n}\n\n/** Solo per i test: azzera lo store in memoria. */\nexport function clearConsoleEntryTickets(): void {\n entries.clear();\n}\n"]}
1
+ {"version":3,"file":"consoleEntry.js","sourceRoot":"","sources":["../../../src/accessi-module/security/consoleEntry.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'crypto';\n\n/**\n * Ticket di ingresso alla console, monouso e a breve scadenza.\n *\n * Serve a passare una sessione gia autenticata (frontend che possiede un JWT Accessi)\n * alla console amministrativa senza reinserire le credenziali e senza mettere il JWT\n * nell'URL: il frontend scambia il proprio JWT con un ticket opaco, apre la console\n * con `#entry=<ticket>` e la console lo scambia con il token originale.\n *\n * Il ticket e valido una sola volta, per pochi secondi, ed e legato all'utente che\n * lo ha richiesto (che deve avere accesso alla console). Lo store di default e in\n * memoria; in multi-istanza usare `createRedisConsoleEntryStore`.\n */\n\nexport interface ConsoleEntryRecord {\n token: string;\n codiceUtente: number;\n}\n\nexport interface ConsoleEntryTicket {\n ticket: string;\n expiresInSeconds: number;\n}\n\n/** Store pluggable: memoria per single instance, Redis per multi-istanza. */\nexport interface ConsoleEntryStore {\n issue(record: ConsoleEntryRecord, ttlMs: number): Promise<ConsoleEntryTicket>;\n consume(ticket: string): Promise<ConsoleEntryRecord | null>;\n /** Health check opzionale per readiness del servizio. */\n healthCheck?(): Promise<boolean>;\n}\n\nconst DEFAULT_TTL_MS = 60_000;\nconst DEFAULT_MAX_ENTRIES = 5000;\nconst TICKET_PATTERN = /^[a-f0-9]{64}$/i;\n\ninterface StoredEntry extends ConsoleEntryRecord {\n expiresAt: number;\n}\n\n/** Store in memoria con API sincrona per retrocompatibilita e API async per l'interfaccia. */\nexport interface MemoryConsoleEntryStore extends ConsoleEntryStore {\n issueSync(record: ConsoleEntryRecord, ttlMs?: number): ConsoleEntryTicket;\n consumeSync(ticket: string): ConsoleEntryRecord | null;\n clear(): void;\n}\n\nfunction createTicket(): string {\n return randomBytes(32).toString('hex');\n}\n\nfunction normalizeTtl(ttlMs: number): number {\n return Math.max(1, Math.trunc(ttlMs));\n}\n\n/** Store in memoria: adatto a sviluppo e a una singola istanza. */\nexport function createMemoryConsoleEntryStore(\n maxEntries: number = DEFAULT_MAX_ENTRIES,\n): MemoryConsoleEntryStore {\n const entries = new Map<string, StoredEntry>();\n\n const sweep = (now: number): void => {\n for (const [ticket, record] of entries) {\n if (record.expiresAt <= now) entries.delete(ticket);\n }\n };\n\n const issueSync = (record: ConsoleEntryRecord, ttlMs: number = DEFAULT_TTL_MS): ConsoleEntryTicket => {\n // Il TTL negativo resta supportato per test e per il comportamento storico:\n // il ticket e immediatamente scaduto.\n const ttl = Math.trunc(ttlMs);\n const now = Date.now();\n sweep(now);\n\n while (entries.size >= maxEntries) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n\n const ticket = createTicket();\n entries.set(ticket, { ...record, expiresAt: now + ttl });\n return { ticket, expiresInSeconds: Math.max(0, Math.ceil(ttl / 1000)) };\n };\n\n const consumeSync = (ticket: string): ConsoleEntryRecord | null => {\n if (typeof ticket !== 'string' || !TICKET_PATTERN.test(ticket)) {\n return null;\n }\n const record = entries.get(ticket);\n if (!record) return null;\n entries.delete(ticket);\n if (record.expiresAt <= Date.now()) return null;\n return { token: record.token, codiceUtente: record.codiceUtente };\n };\n\n return {\n issueSync,\n consumeSync,\n clear: () => entries.clear(),\n async issue(record, ttlMs) {\n return issueSync(record, ttlMs);\n },\n async consume(ticket) {\n return consumeSync(ticket);\n },\n async healthCheck() {\n return true;\n },\n };\n}\n\nconst defaultStore = createMemoryConsoleEntryStore();\n\n/** Store di default in memoria, usato quando `AccessiOptions.consoleEntry.store` non e configurato. */\nexport function getDefaultConsoleEntryStore(): ConsoleEntryStore {\n return defaultStore;\n}\n\n// Funzioni storiche sincrone: mantengono il comportamento per test e consumer esistenti.\nexport function issueConsoleEntryTicket(\n record: ConsoleEntryRecord,\n ttlMs: number = DEFAULT_TTL_MS,\n): ConsoleEntryTicket {\n return defaultStore.issueSync(record, ttlMs);\n}\n\nexport function consumeConsoleEntryTicket(ticket: unknown): ConsoleEntryRecord | null {\n return typeof ticket === 'string' ? defaultStore.consumeSync(ticket) : null;\n}\n\n/** Solo per i test: azzera lo store in memoria di default. */\nexport function clearConsoleEntryTickets(): void {\n defaultStore.clear();\n}\n\n/** Client Redis minimale: compatibile con ioredis (`redis.eval(script, numKeys, ...args)`). */\nexport interface ConsoleEntryRedisClient {\n eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;\n}\n\nexport interface RedisConsoleEntryStoreOptions {\n client: ConsoleEntryRedisClient;\n /** Segreto per cifrare il JWT nel Redis. Non usare il JWT in chiaro. */\n encryptionKey: string | Buffer;\n /** Default: `accessi:console-entry:`. */\n keyPrefix?: string;\n /** Tentativi in caso di collisione del ticket. Default: 5. */\n maxIssueAttempts?: number;\n}\n\nconst SET_SCRIPT = \"return redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2], 'NX')\";\nconst CONSUME_SCRIPT =\n \"local value = redis.call('GET', KEYS[1]) if value then redis.call('DEL', KEYS[1]) end return value\";\n\nfunction deriveEncryptionKey(secret: string | Buffer): Buffer {\n const source = Buffer.isBuffer(secret) ? secret : Buffer.from(secret, 'utf8');\n if (source.length === 0) {\n throw new Error('Redis console entry encryptionKey non configurata.');\n }\n return source.length === 32 ? source : createHash('sha256').update(source).digest();\n}\n\nfunction encryptRecord(record: ConsoleEntryRecord, key: Buffer): string {\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const ciphertext = Buffer.concat([\n cipher.update(JSON.stringify(record), 'utf8'),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n return [iv, tag, ciphertext].map((part) => part.toString('base64url')).join('.');\n}\n\nfunction decryptRecord(value: string, key: Buffer): ConsoleEntryRecord | null {\n const [ivPart, tagPart, ciphertextPart] = value.split('.');\n if (!ivPart || !tagPart || !ciphertextPart) return null;\n try {\n const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(ivPart, 'base64url'));\n decipher.setAuthTag(Buffer.from(tagPart, 'base64url'));\n const plaintext = Buffer.concat([\n decipher.update(Buffer.from(ciphertextPart, 'base64url')),\n decipher.final(),\n ]).toString('utf8');\n const parsed: unknown = JSON.parse(plaintext);\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n typeof (parsed as ConsoleEntryRecord).token !== 'string' ||\n typeof (parsed as ConsoleEntryRecord).codiceUtente !== 'number'\n ) {\n return null;\n }\n return parsed as ConsoleEntryRecord;\n } catch {\n return null;\n }\n}\n\n/**\n * Store Redis per ticket console.\n *\n * - il JWT viene cifrato con AES-256-GCM prima di essere scritto;\n * - `SET ... PX ... NX` evita collisioni;\n * - il consume usa `GET` + `DEL` atomici in Lua, quindi il ticket e monouso\n * anche con piu istanze.\n */\nexport function createRedisConsoleEntryStore(\n options: RedisConsoleEntryStoreOptions,\n): ConsoleEntryStore {\n if (!options?.client || typeof options.client.eval !== 'function') {\n throw new Error('Redis console entry store: client.eval non configurato.');\n }\n\n const key = deriveEncryptionKey(options.encryptionKey);\n const keyPrefix = (options.keyPrefix ?? 'accessi:console-entry:').replace(/:+$/, ':');\n const maxAttempts = Math.max(1, options.maxIssueAttempts ?? 5);\n\n return {\n async issue(record, ttlMs) {\n const safeTtl = normalizeTtl(ttlMs);\n const value = encryptRecord(record, key);\n\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n const ticket = createTicket();\n const result = await options.client.eval(\n SET_SCRIPT,\n 1,\n `${keyPrefix}${ticket}`,\n value,\n safeTtl,\n );\n if (result) {\n return { ticket, expiresInSeconds: Math.ceil(safeTtl / 1000) };\n }\n }\n\n throw new Error('Impossibile emettere un ticket console univoco.');\n },\n\n async consume(ticket) {\n if (typeof ticket !== 'string' || !TICKET_PATTERN.test(ticket)) {\n return null;\n }\n const value = await options.client.eval(\n CONSUME_SCRIPT,\n 1,\n `${keyPrefix}${ticket}`,\n );\n if (typeof value !== 'string') return null;\n return decryptRecord(value, key);\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"]}
@@ -0,0 +1,33 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+ import type { RateLimitStore } from './rateLimitStore';
3
+ /** Opzioni del limite grossolano per IP applicato davanti alle rotte Accessi. */
4
+ export interface AccessiEdgeRateLimitOptions {
5
+ /** Default: true quando l'oggetto viene configurato. */
6
+ enabled?: boolean;
7
+ /** Finestra del limite. Default: 60000 ms. */
8
+ windowMs?: number;
9
+ /** Richieste consentite per IP nella finestra. Default: 300. */
10
+ maxAttempts?: number;
11
+ /** Store condiviso. Default: memoria. */
12
+ store?: RateLimitStore;
13
+ /** Prefisso delle chiavi. Default: `edge`. */
14
+ keyPrefix?: string;
15
+ /** Audit strutturato degli eventi di blocco. Default: true. */
16
+ audit?: boolean;
17
+ /**
18
+ * Decide se applicare il limite alla richiesta.
19
+ *
20
+ * Default: `/api` e `/api/*`. Il bootstrap Accessi passa un matcher che include
21
+ * anche OpenAPI e JWKS.
22
+ */
23
+ match?: (path: string, req: Request) => boolean;
24
+ }
25
+ export type AccessiEdgeRateLimitMiddleware = (req: Request, res: Response, next: NextFunction) => Promise<void>;
26
+ /**
27
+ * Middleware edge per Accessi Service.
28
+ *
29
+ * Non sostituisce il rate limit applicativo per account: aggiunge una soglia
30
+ * grossolana per IP, utile contro bot e floods prima che la richiesta raggiunga
31
+ * controller, database o introspection.
32
+ */
33
+ export declare function createAccessiEdgeRateLimitMiddleware(options?: AccessiEdgeRateLimitOptions): AccessiEdgeRateLimitMiddleware | undefined;