metis-desk/docs/centralizacion-csrf-y-prop-drilling.md
2026-10-10 12:42:56 +02:00

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

  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.