3.5 KiB
Identity Backend Architecture
Objetivo del módulo identity
identity sustituye progresivamente al módulo legacy auth.
Responsabilidades previstas:
- cuentas autenticables
- login/password
- access token
- refresh token rotado
- logout
- sesión actual
- empresas accesibles
- memberships
- roles
- permisos
- autorización futura
Dominio mínimo creado
Agregados principales:
Account
Company
CompanyMembership
Role
RefreshToken
Value Objects reutilizados
Desde @repo/rdx-ddd:
UniqueID
LanguageCode
Name
TextValue
UtcDate
EmailAddress
URLAddress
VOs específicos de identity:
PasswordHash
RefreshTokenHash
RoleCode
AccountStatus
CompanyStatus
CompanyMembershipStatus
Seguridad
Servicios definidos:
IPasswordHasher
IAccessTokenIssuer
IAccessTokenVerifier
IRefreshTokenGenerator
IRefreshTokenHasher
Implementaciones actuales:
BcryptPasswordHasher
JsonWebTokenAccessTokenIssuer
JsonWebTokenAccessTokenVerifier
CryptoRefreshTokenGenerator
Sha256RefreshTokenHasher
Decisiones:
- password hashing con
bcrypten V1. - access token JWT con
accountIdyemail. - refresh token aleatorio con
crypto.randomBytes. - refresh token persistido solo como hash SHA-256.
- no incluir
companyId, roles ni permisos en access token.
Persistencia mínima
Tablas/modelos creados:
identity_accounts
identity_refresh_tokens
Repositorios:
IAccountRepository
IRefreshTokenRepository
SequelizeAccountRepository
SequelizeRefreshTokenRepository
Servicios públicos del módulo
identity registra un servicio público:
identity:general
Incluye:
auth.authenticatedOnly(): RequestHandler[];
auth.tenantRequired(): RequestHandler[];
Pero los consumidores no deberían usar directamente identity:general en routers.
Deben usar los builders públicos:
requireIdentityAuthenticated(params: StartParams): RequestHandler[];
requireIdentityTenant(params: StartParams): RequestHandler[];
Uso correcto en routers
import { requireIdentityTenant } from "@erp/identity/api";
export function buildSomeRoutes(params: StartParams) {
const router = Router();
router.use(...requireIdentityTenant(params));
return router;
}
Shape actual de req.user
{
userId: UniqueID;
email?: EmailAddress;
companyId?: UniqueID;
roles?: string[];
}
Compatibilidad provisional:
rolespuede ser[].companyIdviene deX-Company-Id.companySlugno debe depender de auth.
Prohibiciones de diseño
No usar en módulos consumidores:
IdentityInternalDeps;
authenticateUserDependencies;
getInternal("identity");
No crear:
<module>-auth-middlewares.ts
como patrón estable.
No añadir a access token:
companyId
roles
permissions
companySlug
Permisos
Cada módulo define sus propios permisos.
core contiene contratos transversales mínimos:
export type PermissionCode = string;
export type PermissionDefinition = {
code: PermissionCode;
module: string;
description: string;
};
identity define solo sus permisos propios:
identity:companies:update
identity:roles:read
identity:roles:create
identity:roles:update
identity:roles:delete
identity:company-users:read
identity:company-users:update-roles
identity:company-users:disable
identity:company-users:enable
identity:permissions:read
Role.permissionCodes usa PermissionCode[], no IdentityPermissionCode[].
La existencia real de permisos se valida en Application con IPermissionCatalog.