6.6 KiB
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
- Revisar el alcance de compañía dentro de
CompanyDetail, sin afectar pantallas globales. - Separar datos y acciones de
lookupsy evaluar un proveedor de inventario acotado. - 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.tsxde vendor,personIdantiguo 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.