emilsoftware-utilities 2.0.0-dev.76 → 2.0.0-dev.77
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.
- package/dist/accessi-module/AccessiModule.d.ts +6 -0
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/accessiFetch.d.ts +7 -0
- package/dist/accessi-module/Console/accessiFetch.js +45 -7
- package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
- package/dist/accessi-module/Console/console.js +82 -22
- package/dist/accessi-module/Console/console.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/getUserByTokenRequest.d.ts +2 -2
- package/dist/accessi-module/Console/generated/model/getUserByTokenRequest.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.d.ts +2 -2
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js.map +1 -1
- package/dist/accessi-module/Console/wiki.js +1 -0
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.js +10 -2
- package/dist/accessi-module/Controllers/AccessiConsoleController.js.map +1 -1
- package/dist/accessi-module/Controllers/AuthController.js +35 -7
- package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
- package/dist/accessi-module/Controllers/ConsoleEntryController.js +8 -0
- package/dist/accessi-module/Controllers/ConsoleEntryController.js.map +1 -1
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.d.ts +1 -1
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.js +3 -2
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.js.map +1 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.d.ts +1 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js +4 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js.map +1 -1
- package/dist/accessi-module/index.d.ts +2 -0
- package/dist/accessi-module/index.js +17 -2
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/accessi-module/security/authCookies.d.ts +104 -0
- package/dist/accessi-module/security/authCookies.js +240 -0
- package/dist/accessi-module/security/authCookies.js.map +1 -0
- package/dist/accessi-module/security/validateOptions.js +34 -0
- package/dist/accessi-module/security/validateOptions.js.map +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.77.tgz +0 -0
- package/dist/initEmilsoftwareModule.js +7 -0
- package/dist/initEmilsoftwareModule.js.map +1 -1
- package/docs/auth-cookies.md +123 -0
- package/package.json +3 -3
- package/dist/emilsoftware-utilities-2.0.0-dev.76.tgz +0 -0
|
@@ -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';\nimport { createAccessiSecurityHeadersMiddleware } from './security/securityHeaders';\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 const securityHeaders = createAccessiSecurityHeadersMiddleware(\n options.http?.securityHeaders,\n isAccessiRoute,\n { isOpenApiUi: (path) => isOpenApiRequestPath(path, resolvedOpenApi) },\n );\n if (securityHeaders) {\n app.use(securityHeaders);\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 {\n applyAccessiBaseSecurityHeaders,\n applyAccessiContentSecurityPolicy,\n buildAccessiContentSecurityPolicy,\n createAccessiSecurityHeadersMiddleware,\n resolveAccessiSecurityHeaders\n} from './security/securityHeaders';\nexport type {\n AccessiCspDirectives,\n AccessiCspOptions,\n AccessiCspSurface,\n AccessiSecurityHeadersOptions,\n AccessiSecurityHeadersSetting,\n ResolvedAccessiCsp,\n ResolvedAccessiSecurityHeaders\n} from './security/securityHeaders';\nexport {\n getAccessiAccessTokenSigningConfig,\n getAccessiAccessTokenVerificationConfig,\n getAccessiRefreshTokenSigningConfig,\n getAccessiRefreshTokenVerificationConfig,\n resolveAccessiVerificationKeys,\n verifyAccessiJwt\n} from './security/jwtTokenOptions';\nexport type {\n AccessiJwtVerificationConfig,\n AccessiUnknownKeyIdEvent\n} from './security/jwtTokenOptions';\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\nexport {\n checkAccessiReadiness,\n checkAccessiDatabase,\n collectAccessiHealthStores,\n createAccessiLiveReport,\n readinessHttpStatus\n} from './health/readiness';\nexport type {\n AccessiHealthCheckResult,\n AccessiHealthStore,\n AccessiLiveReport,\n AccessiReadinessCheck,\n AccessiReadinessDependencies,\n AccessiReadinessReport\n} from './health/readiness';\nexport { createAccessiHealthModule } from './health/nest';\nexport type { AccessiHealthModuleOptions } from './health/nest';\n\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';\nimport { createAccessiSecurityHeadersMiddleware } from './security/securityHeaders';\nimport { createAccessiAuthCookieMiddleware } from './security/authCookies';\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 const authCookie = createAccessiAuthCookieMiddleware(options, isAccessiRoute);\n if (authCookie) {\n app.use(authCookie);\n }\n if (options.http?.securityHeaders !== false) {\n const securityHeaders = createAccessiSecurityHeadersMiddleware(\n options.http?.securityHeaders,\n isAccessiRoute,\n { isOpenApiUi: (path) => isOpenApiRequestPath(path, resolvedOpenApi) },\n );\n if (securityHeaders) {\n app.use(securityHeaders);\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', 'X-CSRF-Token'] });\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 {\n applyAccessiBaseSecurityHeaders,\n applyAccessiContentSecurityPolicy,\n buildAccessiContentSecurityPolicy,\n createAccessiSecurityHeadersMiddleware,\n resolveAccessiSecurityHeaders\n} from './security/securityHeaders';\nexport type {\n AccessiCspDirectives,\n AccessiCspOptions,\n AccessiCspSurface,\n AccessiSecurityHeadersOptions,\n AccessiSecurityHeadersSetting,\n ResolvedAccessiCsp,\n ResolvedAccessiSecurityHeaders\n} from './security/securityHeaders';\nexport {\n createAccessiAuthCookieMiddleware,\n resolveAccessiAuthCookieOptions,\n getAccessiAccessTokenFromCookie,\n getAccessiRefreshTokenFromCookie,\n setAccessiAuthCookies,\n clearAccessiAuthCookies,\n applyAccessiAuthCookies,\n parseAccessiCookieHeader,\n isAccessiCsrfExemptPath\n} from './security/authCookies';\nexport type {\n AccessiAuthCookieOptions,\n AccessiAuthCookieSetting,\n AccessiAuthTokenPair,\n ResolvedAccessiAuthCookieOptions\n} from './security/authCookies';\nexport {\n getAccessiAccessTokenSigningConfig,\n getAccessiAccessTokenVerificationConfig,\n getAccessiRefreshTokenSigningConfig,\n getAccessiRefreshTokenVerificationConfig,\n resolveAccessiVerificationKeys,\n verifyAccessiJwt\n} from './security/jwtTokenOptions';\nexport type {\n AccessiJwtVerificationConfig,\n AccessiUnknownKeyIdEvent\n} from './security/jwtTokenOptions';\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\nexport {\n checkAccessiReadiness,\n checkAccessiDatabase,\n collectAccessiHealthStores,\n createAccessiLiveReport,\n readinessHttpStatus\n} from './health/readiness';\nexport type {\n AccessiHealthCheckResult,\n AccessiHealthStore,\n AccessiLiveReport,\n AccessiReadinessCheck,\n AccessiReadinessDependencies,\n AccessiReadinessReport\n} from './health/readiness';\nexport { createAccessiHealthModule } from './health/nest';\nexport type { AccessiHealthModuleOptions } from './health/nest';\n\n"]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import type { AccessiOptions } from '../AccessiModule';
|
|
3
|
+
/** Modalita di autenticazione via cookie httpOnly. */
|
|
4
|
+
export interface AccessiAuthCookieOptions {
|
|
5
|
+
/** Default `true`: la sola presenza dell'oggetto abilita la modalita cookie. */
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
/** `Secure` sui cookie. Default `true`; disattivare solo in sviluppo locale HTTP. */
|
|
8
|
+
secure?: boolean;
|
|
9
|
+
/** Default `lax`. Per SPA su altro dominio serve `none` insieme a `secure: true`. */
|
|
10
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
11
|
+
/** Dominio dei cookie, opzionale. */
|
|
12
|
+
domain?: string;
|
|
13
|
+
/** Nome del cookie con l'access token. Default `accessi_access`. */
|
|
14
|
+
accessTokenName?: string;
|
|
15
|
+
/** Nome del cookie con il refresh token. Default `accessi_refresh`. */
|
|
16
|
+
refreshTokenName?: string;
|
|
17
|
+
/** Nome del cookie CSRF leggibile dal client. Default `accessi_csrf`. */
|
|
18
|
+
csrfTokenName?: string;
|
|
19
|
+
/** Path del cookie access. Default `/api/accessi`. */
|
|
20
|
+
accessPath?: string;
|
|
21
|
+
/** Path del cookie refresh. Default `/api/accessi/auth`. */
|
|
22
|
+
refreshPath?: string;
|
|
23
|
+
/** Path del cookie CSRF. Default `/`. */
|
|
24
|
+
csrfPath?: string;
|
|
25
|
+
/** Header CSRF richiesto. Default `x-csrf-token`. */
|
|
26
|
+
csrfHeaderName?: string;
|
|
27
|
+
/** `double-submit` (default) oppure `off` per host che gestiscono CSRF altrove. */
|
|
28
|
+
csrf?: 'double-submit' | 'off';
|
|
29
|
+
/**
|
|
30
|
+
* Restituisce comunque i token nel body JSON. Default `false`: in modalita
|
|
31
|
+
* cookie i token restano solo negli header httpOnly.
|
|
32
|
+
*/
|
|
33
|
+
exposeTokens?: boolean;
|
|
34
|
+
/** Legge il refresh token dal cookie quando il body non lo contiene. Default `true`. */
|
|
35
|
+
refreshFromCookie?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export type AccessiAuthCookieSetting = AccessiAuthCookieOptions;
|
|
38
|
+
export interface ResolvedAccessiAuthCookieOptions {
|
|
39
|
+
secure: boolean;
|
|
40
|
+
sameSite: 'strict' | 'lax' | 'none';
|
|
41
|
+
domain?: string;
|
|
42
|
+
accessTokenName: string;
|
|
43
|
+
refreshTokenName: string;
|
|
44
|
+
csrfTokenName: string;
|
|
45
|
+
accessPath: string;
|
|
46
|
+
refreshPath: string;
|
|
47
|
+
csrfPath: string;
|
|
48
|
+
csrfHeaderName: string;
|
|
49
|
+
csrf: 'double-submit' | 'off';
|
|
50
|
+
exposeTokens: boolean;
|
|
51
|
+
refreshFromCookie: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface AccessiAuthTokenPair {
|
|
54
|
+
access?: string;
|
|
55
|
+
refresh?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Risolve la configurazione dei cookie; `undefined` quando la modalita e disattivata. */
|
|
58
|
+
export declare function resolveAccessiAuthCookieOptions(options: Pick<AccessiOptions, 'authCookie'>): ResolvedAccessiAuthCookieOptions | undefined;
|
|
59
|
+
/** Legge i cookie senza aggiungere la dipendenza `cookie-parser`. */
|
|
60
|
+
export declare function parseAccessiCookieHeader(header: string | undefined): Record<string, string>;
|
|
61
|
+
/** True per richieste che modificano stato (dove serve il CSRF). */
|
|
62
|
+
export declare function isAccessiUnsafeMethod(method: string): boolean;
|
|
63
|
+
/** True per gli endpoint pubblici esenti da CSRF. */
|
|
64
|
+
export declare function isAccessiCsrfExemptPath(path: string): boolean;
|
|
65
|
+
/** Imposta i cookie access, refresh e (se assente) CSRF. */
|
|
66
|
+
export declare function setAccessiAuthCookies(res: Pick<Response, 'append'>, config: ResolvedAccessiAuthCookieOptions, tokens: AccessiAuthTokenPair): void;
|
|
67
|
+
/** Cancella i cookie di sessione (logout/revoca). */
|
|
68
|
+
export declare function clearAccessiAuthCookies(res: Pick<Response, 'append'>, config: ResolvedAccessiAuthCookieOptions): void;
|
|
69
|
+
export interface AccessiAuthTokenValue {
|
|
70
|
+
value?: string;
|
|
71
|
+
}
|
|
72
|
+
/** Forma comune dei risultati di auth: stringa o `TokenResult` di Accessi. */
|
|
73
|
+
export interface AccessiAuthResultLike {
|
|
74
|
+
token?: string | AccessiAuthTokenValue;
|
|
75
|
+
refreshToken?: string | AccessiAuthTokenValue;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Prepara la risposta di login/refresh: imposta i cookie e rimuove i token dal
|
|
79
|
+
* body quando la modalita cookie non prevede `exposeTokens`.
|
|
80
|
+
*/
|
|
81
|
+
export declare function applyAccessiAuthCookies<T extends AccessiAuthResultLike>(res: Pick<Response, 'append'>, config: ResolvedAccessiAuthCookieOptions | undefined, result: T): T;
|
|
82
|
+
/** Rimuove i token dal `LoginResult` quando la modalita cookie li nasconde. */
|
|
83
|
+
export declare function hideAccessiLoginTokens(config: ResolvedAccessiAuthCookieOptions | undefined, result: {
|
|
84
|
+
token?: unknown;
|
|
85
|
+
refreshToken?: unknown;
|
|
86
|
+
}): void;
|
|
87
|
+
/** Legge l'access token dal cookie (per test e per host senza middleware). */
|
|
88
|
+
export declare function getAccessiAccessTokenFromCookie(header: string | undefined, config: ResolvedAccessiAuthCookieOptions): string | undefined;
|
|
89
|
+
/** Legge il refresh token dal cookie. */
|
|
90
|
+
export declare function getAccessiRefreshTokenFromCookie(header: string | undefined, config: ResolvedAccessiAuthCookieOptions): string | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Middleware della modalita cookie.
|
|
93
|
+
*
|
|
94
|
+
* - copia l'access token dal cookie nell'header `Authorization`, cosi guard,
|
|
95
|
+
* middleware e controller esistenti funzionano senza modifiche;
|
|
96
|
+
* - garantisce un cookie CSRF double-submit leggibile dal client;
|
|
97
|
+
* - blocca le richieste autenticate via cookie che modificano stato senza
|
|
98
|
+
* l'header CSRF, salvo endpoint pubblici di bootstrap della sessione.
|
|
99
|
+
*/
|
|
100
|
+
export declare function createAccessiAuthCookieMiddleware(options: Pick<AccessiOptions, 'authCookie'>, isAccessiRoute: (path: string) => boolean): ((req: Request, res: Response, next: NextFunction) => void) | undefined;
|
|
101
|
+
/** True se il nome cookie e valido per l'header `Set-Cookie`. */
|
|
102
|
+
export declare function isValidAccessiCookieName(name: string): boolean;
|
|
103
|
+
/** Helpers per le combinazioni di cookie supportate (usati dalla validazione). */
|
|
104
|
+
export declare function accessiCookieNames(config: ResolvedAccessiAuthCookieOptions): string[];
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAccessiAuthCookieOptions = resolveAccessiAuthCookieOptions;
|
|
4
|
+
exports.parseAccessiCookieHeader = parseAccessiCookieHeader;
|
|
5
|
+
exports.isAccessiUnsafeMethod = isAccessiUnsafeMethod;
|
|
6
|
+
exports.isAccessiCsrfExemptPath = isAccessiCsrfExemptPath;
|
|
7
|
+
exports.setAccessiAuthCookies = setAccessiAuthCookies;
|
|
8
|
+
exports.clearAccessiAuthCookies = clearAccessiAuthCookies;
|
|
9
|
+
exports.applyAccessiAuthCookies = applyAccessiAuthCookies;
|
|
10
|
+
exports.hideAccessiLoginTokens = hideAccessiLoginTokens;
|
|
11
|
+
exports.getAccessiAccessTokenFromCookie = getAccessiAccessTokenFromCookie;
|
|
12
|
+
exports.getAccessiRefreshTokenFromCookie = getAccessiRefreshTokenFromCookie;
|
|
13
|
+
exports.createAccessiAuthCookieMiddleware = createAccessiAuthCookieMiddleware;
|
|
14
|
+
exports.isValidAccessiCookieName = isValidAccessiCookieName;
|
|
15
|
+
exports.accessiCookieNames = accessiCookieNames;
|
|
16
|
+
const crypto_1 = require("crypto");
|
|
17
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
18
|
+
const COOKIE_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
19
|
+
const DEFAULTS = {
|
|
20
|
+
secure: true,
|
|
21
|
+
sameSite: 'lax',
|
|
22
|
+
accessTokenName: 'accessi_access',
|
|
23
|
+
refreshTokenName: 'accessi_refresh',
|
|
24
|
+
csrfTokenName: 'accessi_csrf',
|
|
25
|
+
accessPath: '/api/accessi',
|
|
26
|
+
refreshPath: '/api/accessi/auth',
|
|
27
|
+
csrfPath: '/',
|
|
28
|
+
csrfHeaderName: 'x-csrf-token',
|
|
29
|
+
csrf: 'double-submit',
|
|
30
|
+
exposeTokens: false,
|
|
31
|
+
refreshFromCookie: true,
|
|
32
|
+
};
|
|
33
|
+
/** Percorsi pubblici che non richiedono il token CSRF (non hanno ancora una sessione). */
|
|
34
|
+
const CSRF_EXEMPT_PATHS = [
|
|
35
|
+
'/api/accessi/auth/login',
|
|
36
|
+
'/api/accessi/auth/refresh',
|
|
37
|
+
'/api/accessi/auth/confirm-reset-password',
|
|
38
|
+
'/api/accessi/auth/two-factor/verify',
|
|
39
|
+
'/api/accessi/auth/two-factor/resend',
|
|
40
|
+
'/api/accessi/auth/bootstrap-admin',
|
|
41
|
+
'/api/accessi/email/',
|
|
42
|
+
'/api/accessi/federated-auth/exchange',
|
|
43
|
+
'/api/accessi/user/register',
|
|
44
|
+
];
|
|
45
|
+
const UNSAFE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
46
|
+
/** Risolve la configurazione dei cookie; `undefined` quando la modalita e disattivata. */
|
|
47
|
+
function resolveAccessiAuthCookieOptions(options) {
|
|
48
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
49
|
+
const raw = options.authCookie;
|
|
50
|
+
if (!raw || raw.enabled === false)
|
|
51
|
+
return undefined;
|
|
52
|
+
return Object.assign(Object.assign({ secure: raw.secure !== false, sameSite: (_a = raw.sameSite) !== null && _a !== void 0 ? _a : DEFAULTS.sameSite }, (raw.domain ? { domain: raw.domain } : {})), { accessTokenName: (_b = raw.accessTokenName) !== null && _b !== void 0 ? _b : DEFAULTS.accessTokenName, refreshTokenName: (_c = raw.refreshTokenName) !== null && _c !== void 0 ? _c : DEFAULTS.refreshTokenName, csrfTokenName: (_d = raw.csrfTokenName) !== null && _d !== void 0 ? _d : DEFAULTS.csrfTokenName, accessPath: (_e = raw.accessPath) !== null && _e !== void 0 ? _e : DEFAULTS.accessPath, refreshPath: (_f = raw.refreshPath) !== null && _f !== void 0 ? _f : DEFAULTS.refreshPath, csrfPath: (_g = raw.csrfPath) !== null && _g !== void 0 ? _g : DEFAULTS.csrfPath, csrfHeaderName: ((_h = raw.csrfHeaderName) !== null && _h !== void 0 ? _h : DEFAULTS.csrfHeaderName).toLowerCase(), csrf: (_j = raw.csrf) !== null && _j !== void 0 ? _j : DEFAULTS.csrf, exposeTokens: raw.exposeTokens === true, refreshFromCookie: raw.refreshFromCookie !== false });
|
|
53
|
+
}
|
|
54
|
+
/** Legge i cookie senza aggiungere la dipendenza `cookie-parser`. */
|
|
55
|
+
function parseAccessiCookieHeader(header) {
|
|
56
|
+
const cookies = {};
|
|
57
|
+
if (!header)
|
|
58
|
+
return cookies;
|
|
59
|
+
for (const part of header.split(';')) {
|
|
60
|
+
const separator = part.indexOf('=');
|
|
61
|
+
if (separator < 0)
|
|
62
|
+
continue;
|
|
63
|
+
const name = part.slice(0, separator).trim();
|
|
64
|
+
if (!name)
|
|
65
|
+
continue;
|
|
66
|
+
const rawValue = part.slice(separator + 1).trim();
|
|
67
|
+
try {
|
|
68
|
+
cookies[name] = decodeURIComponent(rawValue);
|
|
69
|
+
}
|
|
70
|
+
catch (_a) {
|
|
71
|
+
cookies[name] = rawValue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return cookies;
|
|
75
|
+
}
|
|
76
|
+
/** True per richieste che modificano stato (dove serve il CSRF). */
|
|
77
|
+
function isAccessiUnsafeMethod(method) {
|
|
78
|
+
return UNSAFE_METHODS.has(method.toUpperCase());
|
|
79
|
+
}
|
|
80
|
+
/** True per gli endpoint pubblici esenti da CSRF. */
|
|
81
|
+
function isAccessiCsrfExemptPath(path) {
|
|
82
|
+
return CSRF_EXEMPT_PATHS.some((exempt) => exempt.endsWith('/') ? path.startsWith(exempt) : path === exempt);
|
|
83
|
+
}
|
|
84
|
+
function tokenMaxAgeSeconds(token) {
|
|
85
|
+
if (!token)
|
|
86
|
+
return undefined;
|
|
87
|
+
const decoded = (0, jsonwebtoken_1.decode)(token);
|
|
88
|
+
if (!decoded || typeof decoded === 'string')
|
|
89
|
+
return undefined;
|
|
90
|
+
const exp = Number(decoded.exp);
|
|
91
|
+
if (!Number.isFinite(exp))
|
|
92
|
+
return undefined;
|
|
93
|
+
return Math.max(0, Math.floor(exp - Date.now() / 1000));
|
|
94
|
+
}
|
|
95
|
+
function serializeCookie(name, value, options) {
|
|
96
|
+
const parts = [`${name}=${encodeURIComponent(value)}`, `Path=${options.path}`];
|
|
97
|
+
if (options.domain)
|
|
98
|
+
parts.push(`Domain=${options.domain}`);
|
|
99
|
+
if (options.maxAgeSeconds !== undefined)
|
|
100
|
+
parts.push(`Max-Age=${options.maxAgeSeconds}`);
|
|
101
|
+
if (options.httpOnly)
|
|
102
|
+
parts.push('HttpOnly');
|
|
103
|
+
if (options.secure)
|
|
104
|
+
parts.push('Secure');
|
|
105
|
+
const sameSite = { strict: 'Strict', lax: 'Lax', none: 'None' }[options.sameSite];
|
|
106
|
+
parts.push(`SameSite=${sameSite}`);
|
|
107
|
+
return parts.join('; ');
|
|
108
|
+
}
|
|
109
|
+
function appendCookie(res, cookie) {
|
|
110
|
+
res.append('Set-Cookie', cookie);
|
|
111
|
+
}
|
|
112
|
+
/** Imposta i cookie access, refresh e (se assente) CSRF. */
|
|
113
|
+
function setAccessiAuthCookies(res, config, tokens) {
|
|
114
|
+
if (tokens.access) {
|
|
115
|
+
appendCookie(res, serializeCookie(config.accessTokenName, tokens.access, Object.assign(Object.assign(Object.assign({ path: config.accessPath }, (tokenMaxAgeSeconds(tokens.access) !== undefined
|
|
116
|
+
? { maxAgeSeconds: tokenMaxAgeSeconds(tokens.access) }
|
|
117
|
+
: {})), { httpOnly: true, secure: config.secure, sameSite: config.sameSite }), (config.domain ? { domain: config.domain } : {}))));
|
|
118
|
+
}
|
|
119
|
+
if (tokens.refresh) {
|
|
120
|
+
appendCookie(res, serializeCookie(config.refreshTokenName, tokens.refresh, Object.assign(Object.assign(Object.assign({ path: config.refreshPath }, (tokenMaxAgeSeconds(tokens.refresh) !== undefined
|
|
121
|
+
? { maxAgeSeconds: tokenMaxAgeSeconds(tokens.refresh) }
|
|
122
|
+
: {})), { httpOnly: true, secure: config.secure, sameSite: config.sameSite }), (config.domain ? { domain: config.domain } : {}))));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Cancella i cookie di sessione (logout/revoca). */
|
|
126
|
+
function clearAccessiAuthCookies(res, config) {
|
|
127
|
+
for (const [name, path] of [
|
|
128
|
+
[config.accessTokenName, config.accessPath],
|
|
129
|
+
[config.refreshTokenName, config.refreshPath],
|
|
130
|
+
[config.csrfTokenName, config.csrfPath],
|
|
131
|
+
]) {
|
|
132
|
+
appendCookie(res, serializeCookie(name, '', Object.assign({ path, maxAgeSeconds: 0, httpOnly: name !== config.csrfTokenName, secure: config.secure, sameSite: config.sameSite }, (config.domain ? { domain: config.domain } : {}))));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function accessiTokenValue(token) {
|
|
136
|
+
if (typeof token === 'string')
|
|
137
|
+
return token === '' ? undefined : token;
|
|
138
|
+
return token === null || token === void 0 ? void 0 : token.value;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Prepara la risposta di login/refresh: imposta i cookie e rimuove i token dal
|
|
142
|
+
* body quando la modalita cookie non prevede `exposeTokens`.
|
|
143
|
+
*/
|
|
144
|
+
function applyAccessiAuthCookies(res, config, result) {
|
|
145
|
+
if (!config)
|
|
146
|
+
return result;
|
|
147
|
+
setAccessiAuthCookies(res, config, {
|
|
148
|
+
access: accessiTokenValue(result.token),
|
|
149
|
+
refresh: accessiTokenValue(result.refreshToken),
|
|
150
|
+
});
|
|
151
|
+
hideAccessiLoginTokens(config, result);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
/** Rimuove i token dal `LoginResult` quando la modalita cookie li nasconde. */
|
|
155
|
+
function hideAccessiLoginTokens(config, result) {
|
|
156
|
+
if (!config || config.exposeTokens)
|
|
157
|
+
return;
|
|
158
|
+
delete result.token;
|
|
159
|
+
delete result.refreshToken;
|
|
160
|
+
}
|
|
161
|
+
/** Legge l'access token dal cookie (per test e per host senza middleware). */
|
|
162
|
+
function getAccessiAccessTokenFromCookie(header, config) {
|
|
163
|
+
return parseAccessiCookieHeader(header)[config.accessTokenName];
|
|
164
|
+
}
|
|
165
|
+
/** Legge il refresh token dal cookie. */
|
|
166
|
+
function getAccessiRefreshTokenFromCookie(header, config) {
|
|
167
|
+
return parseAccessiCookieHeader(header)[config.refreshTokenName];
|
|
168
|
+
}
|
|
169
|
+
function safeEqual(left, right) {
|
|
170
|
+
const a = Buffer.from(left);
|
|
171
|
+
const b = Buffer.from(right);
|
|
172
|
+
return a.length === b.length && (0, crypto_1.timingSafeEqual)(a, b);
|
|
173
|
+
}
|
|
174
|
+
function csrfError(res) {
|
|
175
|
+
res.status(403).json({
|
|
176
|
+
severity: 'error',
|
|
177
|
+
status: 403,
|
|
178
|
+
statusCode: 2,
|
|
179
|
+
code: 'AUTH_CSRF_INVALID',
|
|
180
|
+
error: 'AUTH_CSRF_INVALID',
|
|
181
|
+
message: 'Token CSRF mancante o non valido.',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Middleware della modalita cookie.
|
|
186
|
+
*
|
|
187
|
+
* - copia l'access token dal cookie nell'header `Authorization`, cosi guard,
|
|
188
|
+
* middleware e controller esistenti funzionano senza modifiche;
|
|
189
|
+
* - garantisce un cookie CSRF double-submit leggibile dal client;
|
|
190
|
+
* - blocca le richieste autenticate via cookie che modificano stato senza
|
|
191
|
+
* l'header CSRF, salvo endpoint pubblici di bootstrap della sessione.
|
|
192
|
+
*/
|
|
193
|
+
function createAccessiAuthCookieMiddleware(options, isAccessiRoute) {
|
|
194
|
+
const config = resolveAccessiAuthCookieOptions(options);
|
|
195
|
+
if (!config)
|
|
196
|
+
return undefined;
|
|
197
|
+
return (req, res, next) => {
|
|
198
|
+
if (!isAccessiRoute(req.path)) {
|
|
199
|
+
next();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const cookies = parseAccessiCookieHeader(req.headers.cookie);
|
|
203
|
+
let csrfToken = cookies[config.csrfTokenName];
|
|
204
|
+
if (!csrfToken && config.csrf === 'double-submit') {
|
|
205
|
+
csrfToken = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
206
|
+
appendCookie(res, serializeCookie(config.csrfTokenName, csrfToken, Object.assign({ path: config.csrfPath, httpOnly: false, secure: config.secure, sameSite: config.sameSite }, (config.domain ? { domain: config.domain } : {}))));
|
|
207
|
+
}
|
|
208
|
+
if (config.csrf === 'double-submit' && csrfToken) {
|
|
209
|
+
// Rende il token leggibile anche a una SPA su altra origine (via CORS,
|
|
210
|
+
// con `Access-Control-Expose-Headers: X-CSRF-Token`).
|
|
211
|
+
res.setHeader('X-CSRF-Token', csrfToken);
|
|
212
|
+
}
|
|
213
|
+
const accessToken = cookies[config.accessTokenName];
|
|
214
|
+
const hasAuthorization = typeof req.headers.authorization === 'string'
|
|
215
|
+
&& req.headers.authorization.trim() !== '';
|
|
216
|
+
if (accessToken && !hasAuthorization) {
|
|
217
|
+
req.headers.authorization = `Bearer ${accessToken}`;
|
|
218
|
+
if (config.csrf === 'double-submit'
|
|
219
|
+
&& isAccessiUnsafeMethod(req.method)
|
|
220
|
+
&& !isAccessiCsrfExemptPath(req.path)) {
|
|
221
|
+
const headerValue = req.headers[config.csrfHeaderName];
|
|
222
|
+
const provided = Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
223
|
+
if (!csrfToken || !provided || !safeEqual(provided, csrfToken)) {
|
|
224
|
+
csrfError(res);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
next();
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** True se il nome cookie e valido per l'header `Set-Cookie`. */
|
|
233
|
+
function isValidAccessiCookieName(name) {
|
|
234
|
+
return COOKIE_NAME.test(name);
|
|
235
|
+
}
|
|
236
|
+
/** Helpers per le combinazioni di cookie supportate (usati dalla validazione). */
|
|
237
|
+
function accessiCookieNames(config) {
|
|
238
|
+
return [config.accessTokenName, config.refreshTokenName, config.csrfTokenName];
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=authCookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authCookies.js","sourceRoot":"","sources":["../../../src/accessi-module/security/authCookies.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, timingSafeEqual } from 'crypto';\nimport type { NextFunction, Request, Response } from 'express';\nimport type * as jwt from 'jsonwebtoken';\nimport { decode as decodeJwt } from 'jsonwebtoken';\nimport type { AccessiOptions } from '../AccessiModule';\n\n/** Modalita di autenticazione via cookie httpOnly. */\nexport interface AccessiAuthCookieOptions {\n /** Default `true`: la sola presenza dell'oggetto abilita la modalita cookie. */\n enabled?: boolean;\n /** `Secure` sui cookie. Default `true`; disattivare solo in sviluppo locale HTTP. */\n secure?: boolean;\n /** Default `lax`. Per SPA su altro dominio serve `none` insieme a `secure: true`. */\n sameSite?: 'strict' | 'lax' | 'none';\n /** Dominio dei cookie, opzionale. */\n domain?: string;\n /** Nome del cookie con l'access token. Default `accessi_access`. */\n accessTokenName?: string;\n /** Nome del cookie con il refresh token. Default `accessi_refresh`. */\n refreshTokenName?: string;\n /** Nome del cookie CSRF leggibile dal client. Default `accessi_csrf`. */\n csrfTokenName?: string;\n /** Path del cookie access. Default `/api/accessi`. */\n accessPath?: string;\n /** Path del cookie refresh. Default `/api/accessi/auth`. */\n refreshPath?: string;\n /** Path del cookie CSRF. Default `/`. */\n csrfPath?: string;\n /** Header CSRF richiesto. Default `x-csrf-token`. */\n csrfHeaderName?: string;\n /** `double-submit` (default) oppure `off` per host che gestiscono CSRF altrove. */\n csrf?: 'double-submit' | 'off';\n /**\n * Restituisce comunque i token nel body JSON. Default `false`: in modalita\n * cookie i token restano solo negli header httpOnly.\n */\n exposeTokens?: boolean;\n /** Legge il refresh token dal cookie quando il body non lo contiene. Default `true`. */\n refreshFromCookie?: boolean;\n}\n\nexport type AccessiAuthCookieSetting = AccessiAuthCookieOptions;\n\nexport interface ResolvedAccessiAuthCookieOptions {\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n domain?: string;\n accessTokenName: string;\n refreshTokenName: string;\n csrfTokenName: string;\n accessPath: string;\n refreshPath: string;\n csrfPath: string;\n csrfHeaderName: string;\n csrf: 'double-submit' | 'off';\n exposeTokens: boolean;\n refreshFromCookie: boolean;\n}\n\nexport interface AccessiAuthTokenPair {\n access?: string;\n refresh?: string;\n}\n\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\nconst DEFAULTS = {\n secure: true,\n sameSite: 'lax' as const,\n accessTokenName: 'accessi_access',\n refreshTokenName: 'accessi_refresh',\n csrfTokenName: 'accessi_csrf',\n accessPath: '/api/accessi',\n refreshPath: '/api/accessi/auth',\n csrfPath: '/',\n csrfHeaderName: 'x-csrf-token',\n csrf: 'double-submit' as const,\n exposeTokens: false,\n refreshFromCookie: true,\n};\n\n/** Percorsi pubblici che non richiedono il token CSRF (non hanno ancora una sessione). */\nconst CSRF_EXEMPT_PATHS = [\n '/api/accessi/auth/login',\n '/api/accessi/auth/refresh',\n '/api/accessi/auth/confirm-reset-password',\n '/api/accessi/auth/two-factor/verify',\n '/api/accessi/auth/two-factor/resend',\n '/api/accessi/auth/bootstrap-admin',\n '/api/accessi/email/',\n '/api/accessi/federated-auth/exchange',\n '/api/accessi/user/register',\n];\n\nconst UNSAFE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);\n\n/** Risolve la configurazione dei cookie; `undefined` quando la modalita e disattivata. */\nexport function resolveAccessiAuthCookieOptions(\n options: Pick<AccessiOptions, 'authCookie'>,\n): ResolvedAccessiAuthCookieOptions | undefined {\n const raw = options.authCookie;\n if (!raw || raw.enabled === false) return undefined;\n\n return {\n secure: raw.secure !== false,\n sameSite: raw.sameSite ?? DEFAULTS.sameSite,\n ...(raw.domain ? { domain: raw.domain } : {}),\n accessTokenName: raw.accessTokenName ?? DEFAULTS.accessTokenName,\n refreshTokenName: raw.refreshTokenName ?? DEFAULTS.refreshTokenName,\n csrfTokenName: raw.csrfTokenName ?? DEFAULTS.csrfTokenName,\n accessPath: raw.accessPath ?? DEFAULTS.accessPath,\n refreshPath: raw.refreshPath ?? DEFAULTS.refreshPath,\n csrfPath: raw.csrfPath ?? DEFAULTS.csrfPath,\n csrfHeaderName: (raw.csrfHeaderName ?? DEFAULTS.csrfHeaderName).toLowerCase(),\n csrf: raw.csrf ?? DEFAULTS.csrf,\n exposeTokens: raw.exposeTokens === true,\n refreshFromCookie: raw.refreshFromCookie !== false,\n };\n}\n\n/** Legge i cookie senza aggiungere la dipendenza `cookie-parser`. */\nexport function parseAccessiCookieHeader(header: string | undefined): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!header) return cookies;\n for (const part of header.split(';')) {\n const separator = part.indexOf('=');\n if (separator < 0) continue;\n const name = part.slice(0, separator).trim();\n if (!name) continue;\n const rawValue = part.slice(separator + 1).trim();\n try {\n cookies[name] = decodeURIComponent(rawValue);\n } catch {\n cookies[name] = rawValue;\n }\n }\n return cookies;\n}\n\n/** True per richieste che modificano stato (dove serve il CSRF). */\nexport function isAccessiUnsafeMethod(method: string): boolean {\n return UNSAFE_METHODS.has(method.toUpperCase());\n}\n\n/** True per gli endpoint pubblici esenti da CSRF. */\nexport function isAccessiCsrfExemptPath(path: string): boolean {\n return CSRF_EXEMPT_PATHS.some((exempt) =>\n exempt.endsWith('/') ? path.startsWith(exempt) : path === exempt,\n );\n}\n\nfunction tokenMaxAgeSeconds(token: string | undefined): number | undefined {\n if (!token) return undefined;\n const decoded = decodeJwt(token) as jwt.JwtPayload | string | null;\n if (!decoded || typeof decoded === 'string') return undefined;\n const exp = Number(decoded.exp);\n if (!Number.isFinite(exp)) return undefined;\n return Math.max(0, Math.floor(exp - Date.now() / 1000));\n}\n\nfunction serializeCookie(\n name: string,\n value: string,\n options: {\n path: string;\n maxAgeSeconds?: number;\n httpOnly?: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n domain?: string;\n },\n): string {\n const parts = [`${name}=${encodeURIComponent(value)}`, `Path=${options.path}`];\n if (options.domain) parts.push(`Domain=${options.domain}`);\n if (options.maxAgeSeconds !== undefined) parts.push(`Max-Age=${options.maxAgeSeconds}`);\n if (options.httpOnly) parts.push('HttpOnly');\n if (options.secure) parts.push('Secure');\n const sameSite = { strict: 'Strict', lax: 'Lax', none: 'None' }[options.sameSite];\n parts.push(`SameSite=${sameSite}`);\n return parts.join('; ');\n}\n\nfunction appendCookie(res: Pick<Response, 'append'>, cookie: string): void {\n res.append('Set-Cookie', cookie);\n}\n\n/** Imposta i cookie access, refresh e (se assente) CSRF. */\nexport function setAccessiAuthCookies(\n res: Pick<Response, 'append'>,\n config: ResolvedAccessiAuthCookieOptions,\n tokens: AccessiAuthTokenPair,\n): void {\n if (tokens.access) {\n appendCookie(res, serializeCookie(config.accessTokenName, tokens.access, {\n path: config.accessPath,\n ...(tokenMaxAgeSeconds(tokens.access) !== undefined\n ? { maxAgeSeconds: tokenMaxAgeSeconds(tokens.access) }\n : {}),\n httpOnly: true,\n secure: config.secure,\n sameSite: config.sameSite,\n ...(config.domain ? { domain: config.domain } : {}),\n }));\n }\n if (tokens.refresh) {\n appendCookie(res, serializeCookie(config.refreshTokenName, tokens.refresh, {\n path: config.refreshPath,\n ...(tokenMaxAgeSeconds(tokens.refresh) !== undefined\n ? { maxAgeSeconds: tokenMaxAgeSeconds(tokens.refresh) }\n : {}),\n httpOnly: true,\n secure: config.secure,\n sameSite: config.sameSite,\n ...(config.domain ? { domain: config.domain } : {}),\n }));\n }\n}\n\n/** Cancella i cookie di sessione (logout/revoca). */\nexport function clearAccessiAuthCookies(\n res: Pick<Response, 'append'>,\n config: ResolvedAccessiAuthCookieOptions,\n): void {\n for (const [name, path] of [\n [config.accessTokenName, config.accessPath],\n [config.refreshTokenName, config.refreshPath],\n [config.csrfTokenName, config.csrfPath],\n ] as const) {\n appendCookie(res, serializeCookie(name, '', {\n path,\n maxAgeSeconds: 0,\n httpOnly: name !== config.csrfTokenName,\n secure: config.secure,\n sameSite: config.sameSite,\n ...(config.domain ? { domain: config.domain } : {}),\n }));\n }\n}\n\nexport interface AccessiAuthTokenValue {\n value?: string;\n}\n\n/** Forma comune dei risultati di auth: stringa o `TokenResult` di Accessi. */\nexport interface AccessiAuthResultLike {\n token?: string | AccessiAuthTokenValue;\n refreshToken?: string | AccessiAuthTokenValue;\n}\n\nfunction accessiTokenValue(token: string | AccessiAuthTokenValue | undefined): string | undefined {\n if (typeof token === 'string') return token === '' ? undefined : token;\n return token?.value;\n}\n\n/**\n * Prepara la risposta di login/refresh: imposta i cookie e rimuove i token dal\n * body quando la modalita cookie non prevede `exposeTokens`.\n */\nexport function applyAccessiAuthCookies<T extends AccessiAuthResultLike>(\n res: Pick<Response, 'append'>,\n config: ResolvedAccessiAuthCookieOptions | undefined,\n result: T,\n): T {\n if (!config) return result;\n setAccessiAuthCookies(res, config, {\n access: accessiTokenValue(result.token),\n refresh: accessiTokenValue(result.refreshToken),\n });\n hideAccessiLoginTokens(config, result);\n return result;\n}\n\n/** Rimuove i token dal `LoginResult` quando la modalita cookie li nasconde. */\nexport function hideAccessiLoginTokens(\n config: ResolvedAccessiAuthCookieOptions | undefined,\n result: { token?: unknown; refreshToken?: unknown },\n): void {\n if (!config || config.exposeTokens) return;\n delete result.token;\n delete result.refreshToken;\n}\n\n/** Legge l'access token dal cookie (per test e per host senza middleware). */\nexport function getAccessiAccessTokenFromCookie(\n header: string | undefined,\n config: ResolvedAccessiAuthCookieOptions,\n): string | undefined {\n return parseAccessiCookieHeader(header)[config.accessTokenName];\n}\n\n/** Legge il refresh token dal cookie. */\nexport function getAccessiRefreshTokenFromCookie(\n header: string | undefined,\n config: ResolvedAccessiAuthCookieOptions,\n): string | undefined {\n return parseAccessiCookieHeader(header)[config.refreshTokenName];\n}\n\nfunction safeEqual(left: string, right: string): boolean {\n const a = Buffer.from(left);\n const b = Buffer.from(right);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction csrfError(res: Response): void {\n res.status(403).json({\n severity: 'error',\n status: 403,\n statusCode: 2,\n code: 'AUTH_CSRF_INVALID',\n error: 'AUTH_CSRF_INVALID',\n message: 'Token CSRF mancante o non valido.',\n });\n}\n\n/**\n * Middleware della modalita cookie.\n *\n * - copia l'access token dal cookie nell'header `Authorization`, cosi guard,\n * middleware e controller esistenti funzionano senza modifiche;\n * - garantisce un cookie CSRF double-submit leggibile dal client;\n * - blocca le richieste autenticate via cookie che modificano stato senza\n * l'header CSRF, salvo endpoint pubblici di bootstrap della sessione.\n */\nexport function createAccessiAuthCookieMiddleware(\n options: Pick<AccessiOptions, 'authCookie'>,\n isAccessiRoute: (path: string) => boolean,\n): ((req: Request, res: Response, next: NextFunction) => void) | undefined {\n const config = resolveAccessiAuthCookieOptions(options);\n if (!config) return undefined;\n\n return (req: Request, res: Response, next: NextFunction): void => {\n if (!isAccessiRoute(req.path)) {\n next();\n return;\n }\n\n const cookies = parseAccessiCookieHeader(req.headers.cookie);\n let csrfToken = cookies[config.csrfTokenName];\n if (!csrfToken && config.csrf === 'double-submit') {\n csrfToken = randomBytes(32).toString('base64url');\n appendCookie(res, serializeCookie(config.csrfTokenName, csrfToken, {\n path: config.csrfPath,\n httpOnly: false,\n secure: config.secure,\n sameSite: config.sameSite,\n ...(config.domain ? { domain: config.domain } : {}),\n }));\n }\n if (config.csrf === 'double-submit' && csrfToken) {\n // Rende il token leggibile anche a una SPA su altra origine (via CORS,\n // con `Access-Control-Expose-Headers: X-CSRF-Token`).\n res.setHeader('X-CSRF-Token', csrfToken);\n }\n\n const accessToken = cookies[config.accessTokenName];\n const hasAuthorization = typeof req.headers.authorization === 'string'\n && req.headers.authorization.trim() !== '';\n if (accessToken && !hasAuthorization) {\n req.headers.authorization = `Bearer ${accessToken}`;\n\n if (\n config.csrf === 'double-submit'\n && isAccessiUnsafeMethod(req.method)\n && !isAccessiCsrfExemptPath(req.path)\n ) {\n const headerValue = req.headers[config.csrfHeaderName];\n const provided = Array.isArray(headerValue) ? headerValue[0] : headerValue;\n if (!csrfToken || !provided || !safeEqual(provided, csrfToken)) {\n csrfError(res);\n return;\n }\n }\n }\n\n next();\n };\n}\n\n/** True se il nome cookie e valido per l'header `Set-Cookie`. */\nexport function isValidAccessiCookieName(name: string): boolean {\n return COOKIE_NAME.test(name);\n}\n\n/** Helpers per le combinazioni di cookie supportate (usati dalla validazione). */\nexport function accessiCookieNames(config: ResolvedAccessiAuthCookieOptions): string[] {\n return [config.accessTokenName, config.refreshTokenName, config.csrfTokenName];\n}\n"]}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validateAccessiOptions = validateAccessiOptions;
|
|
4
4
|
exports.assertValidAccessiOptions = assertValidAccessiOptions;
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
|
+
const authCookies_1 = require("./authCookies");
|
|
6
7
|
function isHttpUrl(value) {
|
|
7
8
|
if (!value)
|
|
8
9
|
return false;
|
|
@@ -128,6 +129,39 @@ function validateAccessiOptions(options) {
|
|
|
128
129
|
if (((_e = options === null || options === void 0 ? void 0 : options.consoleEntry) === null || _e === void 0 ? void 0 : _e.ttlMs) !== undefined && !positive(options.consoleEntry.ttlMs)) {
|
|
129
130
|
addError('ACCESSI_CONSOLE_ENTRY_TTL_INVALID', 'consoleEntry.ttlMs deve essere positivo.');
|
|
130
131
|
}
|
|
132
|
+
const authCookie = (0, authCookies_1.resolveAccessiAuthCookieOptions)(options !== null && options !== void 0 ? options : {});
|
|
133
|
+
if (authCookie) {
|
|
134
|
+
const names = (0, authCookies_1.accessiCookieNames)(authCookie);
|
|
135
|
+
names.forEach((name, index) => {
|
|
136
|
+
if (!(0, authCookies_1.isValidAccessiCookieName)(name)) {
|
|
137
|
+
addError('ACCESSI_AUTH_COOKIE_NAME_INVALID', `Il nome cookie "${names[index]}" non e valido.`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (new Set(names).size !== names.length) {
|
|
141
|
+
addError('ACCESSI_AUTH_COOKIE_NAME_DUPLICATE', 'I nomi dei cookie access, refresh e CSRF devono essere diversi.');
|
|
142
|
+
}
|
|
143
|
+
if (authCookie.sameSite === 'none' && !authCookie.secure) {
|
|
144
|
+
addError('ACCESSI_AUTH_COOKIE_SAMESITE_NONE_INSECURE', 'SameSite=None richiede secure: true.');
|
|
145
|
+
}
|
|
146
|
+
if (!authCookie.accessPath.startsWith('/')) {
|
|
147
|
+
addError('ACCESSI_AUTH_COOKIE_PATH_INVALID', 'accessPath deve iniziare con "/".');
|
|
148
|
+
}
|
|
149
|
+
if (!authCookie.refreshPath.startsWith('/')) {
|
|
150
|
+
addError('ACCESSI_AUTH_COOKIE_PATH_INVALID', 'refreshPath deve iniziare con "/".');
|
|
151
|
+
}
|
|
152
|
+
if (!authCookie.csrfPath.startsWith('/')) {
|
|
153
|
+
addError('ACCESSI_AUTH_COOKIE_PATH_INVALID', 'csrfPath deve iniziare con "/".');
|
|
154
|
+
}
|
|
155
|
+
if (authCookie.csrf === 'off') {
|
|
156
|
+
addWarning('ACCESSI_AUTH_COOKIE_CSRF_DISABLED', 'CSRF disattivato in modalita cookie: l host deve applicare una propria protezione.');
|
|
157
|
+
}
|
|
158
|
+
if (authCookie.exposeTokens) {
|
|
159
|
+
addWarning('ACCESSI_AUTH_COOKIE_TOKENS_EXPOSED', 'exposeTokens restituisce i token nel body: riduce il beneficio di httpOnly.');
|
|
160
|
+
}
|
|
161
|
+
if (!(jwt === null || jwt === void 0 ? void 0 : jwt.refreshExpiresIn) && authCookie.refreshFromCookie) {
|
|
162
|
+
addWarning('ACCESSI_AUTH_COOKIE_REFRESH_DISABLED', 'refreshFromCookie e attivo ma jwtOptions.refreshExpiresIn non e configurato.');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
131
165
|
return { errors, warnings };
|
|
132
166
|
}
|
|
133
167
|
/** Lancia un errore aggregato se la configurazione non e valida. */
|