Uecko_ERP/docs/architecture/tenant-context.md

116 lines
2.3 KiB
Markdown
Raw Normal View History

2026-07-06 12:04:47 +00:00
# Tenant Context
## Objetivo
Documentar cómo se resuelve el contexto tenant actual del ERP mediante `X-Company-Id` y `requireIdentityTenant(params)`.
## Header tenant
Las rutas tenant-scoped deben recibir:
```http
Authorization: Bearer <access_token>
X-Company-Id: <uuid-company>
```
`X-Company-Id` es el identificador de la company activa en la request.
No usar:
- `X-Company-Slug`
- `companySlug` en token
- `req.user.companySlug`
## Middleware público
Para rutas tenant-scoped:
```ts
import { requireIdentityTenant } from "@erp/identity/api";
router.use(...requireIdentityTenant(params));
```
Para rutas solo autenticadas:
```ts
import { requireIdentityAuthenticated } from "@erp/identity/api";
router.use(...requireIdentityAuthenticated(params));
```
## Validación real de `requireIdentityTenant(params)`
Actualmente ya valida:
1. access token válido
2. account autenticable
3. `X-Company-Id` presente
4. `X-Company-Id` con formato UUID válido
5. membership `active` en `identity_company_memberships`
6. company `active` en `companies`
7. solo entonces rellena `req.user.companyId`
## Semántica esperada de errores
```txt
sin Authorization -> 401
token inválido -> 401
cuenta inexistente/no autenticable -> 401
sin X-Company-Id -> 403
X-Company-Id inválido -> 400
company inexistente -> 403
company disabled -> 403
membership inexistente -> 403
membership disabled/invited -> 403
membership active + company active -> entra al controller
```
## Shape mínimo de `req.user`
```ts
{
userId: UniqueID;
email?: EmailAddress;
companyId?: UniqueID;
roles?: string[];
}
```
No añadir:
- `companySlug`
- `company`
- `permissions` enriquecidos
- `companyStatus`
## Módulos tenant-scoped migrados
Actualmente usan `requireIdentityTenant(params)`:
- `modules/catalogs`
- `modules/customers`
- `modules/customer-invoices`
- `modules/factuges`
`modules/supplier` queda fuera de esta migración por ahora.
## Qué no debe ir en el token
El access token no debe incluir:
- `companyId`
- `companySlug`
- roles
- permisos
El contexto tenant se resuelve por request con `X-Company-Id`.
## Document context
Si un flujo documental necesita `companySlug`, debe resolverlo desde `companies`, por ejemplo:
```txt
companyId -> companies:general.finder.findById(...) -> slug
```