Auth completo (Argon2id + JWT/refresh rotativo), permissões, Companies e OpenAPI
- Auth (apps/api/src/auth/): Argon2id com parâmetros versionados
(ADR-0005), access token JWT de 15min, refresh token opaco de 256 bits
com rotação e detecção de replay (reuso de token já rotacionado revoga
toda a cadeia de sessões do usuário). Endpoints /api/v1/auth/
{login,refresh,logout,me}.
- Permissões resource+action+scope (ADR-0004) via PermissionGuard,
resolvidas do banco a cada request (nunca cacheadas no JWT) — mudança
de papel tem efeito imediato. super_admin com bypass hardcoded,
espelhando a trava já existente no banco.
- Companies (migration + CRUD): leitura aberta a qualquer autenticado,
escrita restrita a companies:manage; validação de CNPJ via
packages/domain-shared (dígito verificador).
- SeedService: cria o primeiro super_admin a partir de
EDEN_SUPERADMIN_EMAIL/PASSWORD só quando `users` está vazia.
- OpenAPI/Swagger em /api/docs; ValidationPipe global com
whitelist+forbidNonWhitelisted (proteção contra mass-assignment).
- packages/database: repositórios (roles/users/sessions/audit/companies)
reutilizáveis por api e worker.
Validado manualmente contra a stack real (não só typecheck): login com
senha errada → 401 genérico; refresh rotaciona e invalida o token
anterior; reuso do token antigo → replay detectado, cadeia revogada;
logout idempotente; audit log com a sequência completa de eventos;
CNPJ inválido rejeitado; mass-assignment rejeitado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,2 @@
|
||||
/**
|
||||
* Domain types/constants shared across bounded contexts (e.g. the
|
||||
* resource/action/scope permission vocabulary from
|
||||
* docs/adr/0004-permission-model.md). Placeholder until a second
|
||||
* bounded context needs to share a type with the first.
|
||||
*/
|
||||
export const DOMAIN_SHARED_PACKAGE_VERSION = "0.1.0";
|
||||
export { onlyDigits, isValidCPF, isValidCNPJ } from "./validators/br-documents.js";
|
||||
|
||||
49
packages/domain-shared/src/validators/br-documents.ts
Normal file
49
packages/domain-shared/src/validators/br-documents.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* CPF/CNPJ format validation (digit-check only — never confirms a document
|
||||
* actually exists or belongs to whoever claims it). Ported from the
|
||||
* standard Brazilian algorithm, matching the legacy's isValidCPF/
|
||||
* isValidCNPJ (eden.md §1 companies, §3 client registration).
|
||||
*/
|
||||
export function onlyDigits(value: string): string {
|
||||
return value.replace(/\D/g, "");
|
||||
}
|
||||
|
||||
export function isValidCPF(value: string): boolean {
|
||||
const cpf = onlyDigits(value);
|
||||
if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false;
|
||||
|
||||
const digits = cpf.split("").map(Number);
|
||||
const checkDigit = (length: number): number => {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
sum += digits[i]! * (length + 1 - i);
|
||||
}
|
||||
const remainder = (sum * 10) % 11;
|
||||
return remainder === 10 ? 0 : remainder;
|
||||
};
|
||||
|
||||
return checkDigit(9) === digits[9] && checkDigit(10) === digits[10];
|
||||
}
|
||||
|
||||
export function isValidCNPJ(value: string): boolean {
|
||||
const cnpj = onlyDigits(value);
|
||||
if (cnpj.length !== 14 || /^(\d)\1{13}$/.test(cnpj)) return false;
|
||||
|
||||
const digits = cnpj.split("").map(Number);
|
||||
// Standard CNPJ weight sequences — the second is the first with a
|
||||
// leading 6, not a generic slice (easy to get subtly wrong otherwise).
|
||||
const WEIGHTS_12 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
const WEIGHTS_13 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
||||
const checkDigit = (length: number, weights: number[]): number => {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
sum += digits[i]! * weights[i]!;
|
||||
}
|
||||
const remainder = sum % 11;
|
||||
return remainder < 2 ? 0 : 11 - remainder;
|
||||
};
|
||||
|
||||
return (
|
||||
checkDigit(12, WEIGHTS_12) === digits[12] && checkDigit(13, WEIGHTS_13) === digits[13]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user