0001. Microservicio de identidad y acceso (svc-identidad)¶
Estado: Propuesto Fecha: 2026-08-31 Autor: Duval Alcivar
Contexto¶
El ADR 0003 asigna a svc-identidad los dominios de usuarios, empresas,
sucursales, permisos y SSO ERP+CRM, y lo ubica dentro del core de
plataforma (core_sigfa), común a los dos productos. El ADR 0006 ya define
cómo se autentican los 14 servicios (JWT emitido por identidad). Las
funciones de identidad están hoy dispersas: el ERP duplica la gestión de
usuarios/permisos y el CRM su propio login, cada uno contra su propia base,
lo que produce usuarios y roles inconsistentes entre productos.
Es necesario definir qué es exactamente svc-identidad, qué domina y qué
fronteras respeta frente a los dominios de negocio.
Decisión¶
Se crea el microservicio svc-identidad como dueño único de la identidad y el acceso en todo el ecosistema ERP+CRM.
Stack tecnológico (consistente con el resto del backend — ADR 0001 y 0005):
- Lenguaje: Python 3.12+.
- Framework: FastAPI — estándar del proyecto; es quien emite el JWT
(ADR 0006) y expone
GET /.well-known/jwks.jsoncon las claves públicas. - Despliegue: contenedor Docker independiente, con su propio pipeline, panel de salud y runbook (regla 9 del ADR 0003).
Dominios que posee (cada uno con dueño de entidad, ADR 0003):
| Dominio | Qué cubre | Dueño de |
|---|---|---|
| Usuarios | cuentas, credenciales, sesiones, refresh con rotación y revocación (ADR 0006) | mae_usuarios |
| Empresas | el multiempresario: mae_empresas y su ciclo de vida; alimenta empresa_id del token (ADR 0002) |
mae_empresas |
| Sucursales | unidades de la empresa: mae_sucursales, ubicación, vigencia |
mae_sucursales |
| Permisos | tipos de usuario (usuario/root/externo), permisos finos y asignación individual usuario → permiso/módulo; scopes/aud del token |
mae_usuarios.tipo_usuario_id, cat_tipos_usuario, cat_permisos |
| SSO ERP+CRM | login único que actúa a la vez sobre erp_sigfa y crm_sigfapro; vive dentro de permisos/ |
sesión/logon entre productos |
Reglas de frontera:
svc-identidades el único que emite tokens (ADR 0006); ningún otro servicio firma JWT.- Identifica al usuario y la empresa, y viajan en el token (
sub,empresa_id,aud,jti). No decide qué hace el usuario dentro de un dominio de negocio: eso lo resuelve el servicio dueño de la regla. - Correlaciona "quién es el usuario" por UUID hacia los demás
servicios; las empresas/sucursales (catálogos) se correlacionan por
BIGINT (
empresa_id) — sin FK ni consultas cruzadas (ADR 0003 global, ADR 0011). - El aislamiento por empresa (RLS, ADR 0002) aplica dentro de este servicio sobre sus propias tablas.
Estructura interna — monolito modular por dominio (vertical slice):
El servicio se organiza por dominio de negocio, no por capa técnica:
router, service, repository, models y schemas de un mismo dominio viven
juntos en su propia carpeta (ver 0003-organizacion-por-dominio.md).
svc-identidad/
├── app/
│ ├── main.py # arranque FastAPI; registra los routers de cada dominio
│ ├── core/
│ │ ├── config.py # variables de entorno (Pydantic Settings)
│ │ ├── security.py # emite y firma el JWT (RS256, ADR 0006)
│ │ ├── permissions.py # require_permission() por endpoint
│ │ ├── exceptions.py # excepciones de negocio -> HTTP errors
│ │ └── logging.py # logging estructurado (JSON)
│ ├── api/
│ │ └── deps.py # get_db(), get_current_user() — transversal
│ ├── domains/
│ │ ├── usuarios/
│ │ │ ├── router.py # /auth, /users, /sessions
│ │ │ ├── service.py # login, tokens, credenciales
│ │ │ ├── repository.py # único lugar que toca SQL de usuarios
│ │ │ ├── models.py # entidades SQLAlchemy
│ │ │ └── schemas.py # contratos Pydantic
│ │ ├── empresas/
│ │ │ └── ... (misma forma) # empresas, empresa_id del multiempresario
│ │ ├── sucursales/
│ │ │ └── ... (misma forma) # sucursales, ubicación, vigencia
│ │ └── permisos/
│ │ └── ... (misma forma) # tipos de usuario y permisos, SSO
│ ├── shared/ # SOLO lo que cruza 2+ dominios de ESTE servicio
│ └── events/
│ ├── publisher.py # outbox: emite eventos de usuarios/empresas
│ └── subscriber.py # consume eventos de otros servicios (si aplica)
├── alembic/
│ ├── versions/ # migraciones versionadas (una por cambio)
│ └── env.py
├── tests/
│ ├── unit/<dominio>/ # mismo árbol que domains/, por dominio
│ └── integration/<dominio>/
├── docs/
│ └── adr/ # 0001-titulo.md — decisiones de ESTE servicio
├── pyproject.toml
├── .env.example # nunca .env real committeado
└── Dockerfile
Dominios de svc-identidad:
app/domains/
├── usuarios/ # login, tokens, credenciales
├── empresas/
├── sucursales/
└── permisos/ # tipos de usuario y permisos, SSO
Regla dura de frontera entre dominios: un archivo dentro de
domains/usuarios/ no importa directo de domains/permisos/. Si dos
dominios necesitan comunicarse, es a través de shared/ (síncrono, dentro
del mismo servicio) o de events/ (asíncrono, entre servicios). Si la única
forma de resolver algo es importar cruzado entre dominios, esa es la señal de
que en realidad son un solo dominio (o de que falta un evento).
Alternativas descartadas¶
- Mantener identidad duplicada dentro de ERP y CRM — descartado: dos fuentes de verdad para usuarios/roles crean cuentas y permisos inconsistentes entre productos, y rompe la regla "un servicio dueño por entidad" (regla 6 del ADR 0003).
- Separar cada dominio en su propio servicio (usuarios, empresas, permisos por separado) — descartado: fragmentar estos dominios acoplados exige transacciones distribuidas para operaciones triviales (crear usuario + asignar rol + enlazar empresa); se deja como evolución solo si demuestra necesitarlo.
- Usar un IdP de terceros (Auth0, Keycloak SaaS) — descartado por depender de un externo en el núcleo de tenencia y seguridad, por el control de datos sensibles de identidad, y por integrarse difícilmente con la base multiempresa propia (ADR 0002).
- Autenticación intra-servicio (cada servicio autentica su propia sesión) — descartado: duplica mecanismos y rompe el SSO ERP+CRM; ya lo resuelve el ADR 0006.
Consecuencias¶
- Ganancia: una sola fuente de verdad para usuarios, empresas,
sucursales y permisos, compartida por ERP y CRM; el SSO es natural porque
ambos autentican contra el mismo dueño. Es el servicio que centraliza
cómo cada microservicio identifica al usuario y la empresa seleccionada,
su tipo (
usuario/root) y sus permisos (ADR 0007/0009/0010). - Ganancia: la rotación de claves y la revocación de sesiones viven en un solo lugar; los demás servicios solo validan el token (ADR 0006).
- Ganancia: toda la tenencia (
mae_empresas,mae_sucursales) se centraliza y nutreempresa_id, reforzando el aislamiento RLS del ADR 0002. - Resuelto: el modelo de tipos de usuario y de permisos individuales está en el ADR 0007; el catálogo inicial de permisos por módulo/acción en el ADR 0014 y las acciones finas por página en el 0015.
- Resuelto: el flujo de alta de empresa que propaga el
empresa_idy sus sucursales a los demás servicios (evento de outbox) está en el ADR 0008. - Modelo de datos: el esquema completo de las tablas que posee este
servicio está repartido en los ADRs de svc-identidad: 0003 (catálogos
y maestros), 0004 (empresas y sucursales:
mae_empresas,mae_sucursales,rel_empresa_aplicacion), 0005 (usuarios, sesiones y credenciales:mae_usuarios,mae_personas,rel_usuario_empresa,rel_usuario_sucursal,trx_refresh_tokens,log_sesiones), 0006 (navegación:cat_aplicaciones,cfg_modulos,cfg_menu,cfg_menu_acciones), 0007 (permisos:cat_permisos,rel_usuario_permiso,rel_usuario_menu) y 0022 (patrón party:mae_entidades,mae_personas_juridicas,cat_tipos_usuarioymae_usuarios.entidad_id/tipo_usuario_id).
Microservicio dentro de core/ porque es la capacidad de identidad que
ningún producto posee en exclusiva: ERP y CRM se autentican contra el mismo
dueño de usuarios, empresas, sucursales y permisos.