# Centralización de CSRF y revisión de prop drilling ## Cambio aplicado El token CSRF pertenece a la sesión HTTP, no a los formularios ni a las entidades. `apps/web/src/main.tsx` configura una función que consulta la sesión actual en la caché de TanStack Query (`identityKeys.session`). `modules/core/src/web/http/shared/api/csrf-fetch.ts` lee esa función en cada petición de escritura y añade `x-csrf-token`. No existe una segunda copia del token en React, localStorage o un contexto. La caché de sesión sigue siendo la fuente única. GET/HEAD/OPTIONS permiten obtener la sesión sin token; POST y las demás escrituras requieren el token hexadecimal de 64 caracteres que genera el servidor. Un token ausente o inválido impide enviar la petición. El transporte solo acepta rutas locales `/api/` y utiliza credenciales del mismo origen. Se han eliminado `csrfToken` de props, controladores, callbacks y firmas de los clientes HTTP de identity, companies, assets y resources. Las peticiones de fotos y logotipos usan el mismo transporte, conservando su cuerpo binario. El contrato HTTP, las validaciones de respuestas y errores de cada módulo y la protección del backend se mantienen. La renovación de sesión tras login, configuración inicial, cambio de contraseña y logout continúa utilizando las invalidaciones existentes. La siguiente petición lee el token que haya en la caché en ese momento; no captura el token de un render anterior. Un 403 no se reintenta automáticamente, para no repetir escrituras. Las pruebas aisladas configuran y restauran su lector mediante la función de limpieza de `configureCsrfTokenReader`. ## Criterio de análisis Se revisaron las props y su recorrido entre workspace, páginas, diálogos, formularios, controles y hooks de los cinco módulos y la aplicación web. Una prop transmitida a un hijo directo no constituye por sí sola un problema: importa si las capas intermedias solo la retransmiten, si tiene una fuente compartida y si su alcance es local o global. La corrección de las demás propiedades queda pendiente: esta tarea implementa CSRF y analiza el resto, sin modificar sus contratos funcionales. ## Casos encontrados y recomendaciones | Propiedades | Recorrido real representativo | Valoración y siguiente paso | | --- | --- | --- | | `company` / `companyId` | `CompanyDetail` → `CompanySection` → `WorkProfilesPage` → `WorkProfileEditorDialog` → `WorkProfileForm`; también las páginas de configuraciones y sus formularios | Prioridad alta. Un contexto limitado al espacio de la compañía podría aportar el identificador y los datos ya consultados. No crear estado duplicado ni hacer que formularios reutilizables dependan obligatoriamente de ese contexto. Conservar el identificador explícito en los clientes HTTP y en los listados globales. | | `lookups` | `CompanyInventory` → `AssetInventoryPage` → `AssetEditorDialog` → `AssetForm`; también `WorkstationsPage` → `WorkstationEditorDialog` → `WorkstationForm` → `WorkstationAssetCreateDialog` | Prioridad media. Mezcla datos de sedes/personas con callbacks de alta rápida entre companies y assets. Separar ambos contratos antes de reducir su paso. Un proveedor limitado al inventario puede compartir datos; las acciones de alta deben seguir siendo aportadas por el módulo propietario. | | `userName` | `App` → `CompaniesWorkspace` → `WorkspaceHeader` | Prioridad baja. Dato de sesión pasado a través de una capa. Un hook público de identidad puede ofrecer el usuario actual a la cabecera si aumenta su uso. Hoy solo hay una capa intermediaria: no justifica por sí sola un contexto adicional. | | `scope`, `items`, plantillas y catálogos | Página de catálogo/inventario → diálogo de edición → formulario/controles | Datos y ámbito de la operación, en su mayoría consumidos también por las capas intermedias. Mantener explícitos; si varios consumidores consultan lo mismo, reutilizar hooks y claves de Query existentes. `scope` puede ser global o compañía: no convertirlo en una compañía implícita. | | `onSaved`, `onClose`, `onCreated`, `onChange` | Página → diálogo → formulario/control | Contratos de comunicación local. Mantener: controlan cierre, selección del registro recién creado e invalidación. No reemplazarlos por un bus global. | | `allowQuickCreate` | Formulario → control de marca/plantilla; formularios anidados establecen `false` | Opción local deliberada para limitar las altas anidadas. Mantener explícita para que el diálogo hijo no abra niveles adicionales por herencia accidental. | | `accountNavigation`, `languageSwitcher`, `children`, `relatedContent` | `App` → `CompaniesWorkspace` → cabecera/configuración; render props de secciones | Composición de interfaz entre módulos. Son slots, no estado duplicado. Mantener para preservar independencia de módulos y evitar importar identity desde companies. | | `initialId`, `initialCreate`, navegación y filtros iniciales | Workspace → sección/página → controlador | Traducción de ruta a estado de pantalla. La capa intermedia suele interpretar la ruta. Mantener la responsabilidad del routing; reducir solo retransmisiones innecesarias al trabajar en cada pantalla. | ## Orden sugerido 1. Revisar el alcance de compañía dentro de `CompanyDetail`, sin afectar pantallas globales. 2. Separar datos y acciones de `lookups` y evaluar un proveedor de inventario acotado. 3. Extraer un hook de usuario actual únicamente si más consumidores necesitan la sesión. No crear un contexto global con todas las props: ocultaría dependencias de formularios, complicaría las pruebas y podría mezclar ámbitos de compañías distintas. ## Verificación - Pruebas del transporte: token leído de Query, rotación, borrado de caché, ausencia/invalidez, lectura sin sesión, destino local, cuerpos JSON/binarios, headers, cancelación y ausencia de reintentos de escrituras fallidas. - Pruebas de contratos de identity, accesos, buzones y marcas adaptadas al transporte. - Suite completa del monorepo: 277 pruebas correctas, incluida integración HTTP con MariaDB. - Pruebas de espaciado: 3 correctas. Los reexports consecutivos pueden mantenerse agrupados para que ESLint y la ordenación de exports de Biome no se contradigan. - Build de producción: correcto. - Biome y reglas de espaciado sobre ficheros modificados: correctos. - Typecheck del módulo core: correcto. Las comprobaciones generales de tipos/lint/check encuentran errores previos fuera del cambio: `calendar.tsx` de vendor, `personId` antiguo en el fixture web y reglas de llaves en código de fotos. No se modificó vendor ni se amplió el alcance del refactor para ellos.