@baruk/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +6 -0
  2. package/dist/audit/audit.d.ts +22 -0
  3. package/dist/audit/audit.d.ts.map +1 -0
  4. package/dist/audit/audit.js +28 -0
  5. package/dist/audit/audit.js.map +1 -0
  6. package/dist/audit/repository.d.ts +22 -0
  7. package/dist/audit/repository.d.ts.map +1 -0
  8. package/dist/audit/repository.js +29 -0
  9. package/dist/audit/repository.js.map +1 -0
  10. package/dist/auth/password-reset.d.ts +20 -0
  11. package/dist/auth/password-reset.d.ts.map +1 -0
  12. package/dist/auth/password-reset.js +56 -0
  13. package/dist/auth/password-reset.js.map +1 -0
  14. package/dist/auth/password.d.ts +21 -0
  15. package/dist/auth/password.d.ts.map +1 -0
  16. package/dist/auth/password.js +67 -0
  17. package/dist/auth/password.js.map +1 -0
  18. package/dist/auth/service.d.ts +82 -0
  19. package/dist/auth/service.d.ts.map +1 -0
  20. package/dist/auth/service.js +214 -0
  21. package/dist/auth/service.js.map +1 -0
  22. package/dist/auth/session-token.d.ts +8 -0
  23. package/dist/auth/session-token.d.ts.map +1 -0
  24. package/dist/auth/session-token.js +19 -0
  25. package/dist/auth/session-token.js.map +1 -0
  26. package/dist/clients/repository.d.ts +53 -0
  27. package/dist/clients/repository.d.ts.map +1 -0
  28. package/dist/clients/repository.js +113 -0
  29. package/dist/clients/repository.js.map +1 -0
  30. package/dist/dashboard/repository.d.ts +19 -0
  31. package/dist/dashboard/repository.d.ts.map +1 -0
  32. package/dist/dashboard/repository.js +36 -0
  33. package/dist/dashboard/repository.js.map +1 -0
  34. package/dist/db/client.d.ts +19 -0
  35. package/dist/db/client.d.ts.map +1 -0
  36. package/dist/db/client.js +21 -0
  37. package/dist/db/client.js.map +1 -0
  38. package/dist/db/context.d.ts +27 -0
  39. package/dist/db/context.d.ts.map +1 -0
  40. package/dist/db/context.js +36 -0
  41. package/dist/db/context.js.map +1 -0
  42. package/dist/db/load-env.d.ts +8 -0
  43. package/dist/db/load-env.d.ts.map +1 -0
  44. package/dist/db/load-env.js +44 -0
  45. package/dist/db/load-env.js.map +1 -0
  46. package/dist/db/migrate.d.ts +2 -0
  47. package/dist/db/migrate.d.ts.map +1 -0
  48. package/dist/db/migrate.js +139 -0
  49. package/dist/db/migrate.js.map +1 -0
  50. package/dist/db/seed.d.ts +2 -0
  51. package/dist/db/seed.d.ts.map +1 -0
  52. package/dist/db/seed.js +64 -0
  53. package/dist/db/seed.js.map +1 -0
  54. package/dist/import/clients-import.d.ts +39 -0
  55. package/dist/import/clients-import.d.ts.map +1 -0
  56. package/dist/import/clients-import.js +142 -0
  57. package/dist/import/clients-import.js.map +1 -0
  58. package/dist/index.d.ts +40 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +40 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/mail/resend.d.ts +13 -0
  63. package/dist/mail/resend.d.ts.map +1 -0
  64. package/dist/mail/resend.js +16 -0
  65. package/dist/mail/resend.js.map +1 -0
  66. package/dist/notifications/repository.d.ts +23 -0
  67. package/dist/notifications/repository.d.ts.map +1 -0
  68. package/dist/notifications/repository.js +43 -0
  69. package/dist/notifications/repository.js.map +1 -0
  70. package/dist/portal/repository.d.ts +11 -0
  71. package/dist/portal/repository.d.ts.map +1 -0
  72. package/dist/portal/repository.js +32 -0
  73. package/dist/portal/repository.js.map +1 -0
  74. package/dist/products/catalog.d.ts +14 -0
  75. package/dist/products/catalog.d.ts.map +1 -0
  76. package/dist/products/catalog.js +7 -0
  77. package/dist/products/catalog.js.map +1 -0
  78. package/dist/products/repository.d.ts +22 -0
  79. package/dist/products/repository.d.ts.map +1 -0
  80. package/dist/products/repository.js +56 -0
  81. package/dist/products/repository.js.map +1 -0
  82. package/dist/rbac/authorize.d.ts +18 -0
  83. package/dist/rbac/authorize.d.ts.map +1 -0
  84. package/dist/rbac/authorize.js +35 -0
  85. package/dist/rbac/authorize.js.map +1 -0
  86. package/dist/rbac/catalog.d.ts +21 -0
  87. package/dist/rbac/catalog.d.ts.map +1 -0
  88. package/dist/rbac/catalog.js +59 -0
  89. package/dist/rbac/catalog.js.map +1 -0
  90. package/dist/requests/repository.d.ts +66 -0
  91. package/dist/requests/repository.d.ts.map +1 -0
  92. package/dist/requests/repository.js +106 -0
  93. package/dist/requests/repository.js.map +1 -0
  94. package/dist/tenancy/provisioning.d.ts +25 -0
  95. package/dist/tenancy/provisioning.d.ts.map +1 -0
  96. package/dist/tenancy/provisioning.js +119 -0
  97. package/dist/tenancy/provisioning.js.map +1 -0
  98. package/dist/users/repository.d.ts +19 -0
  99. package/dist/users/repository.d.ts.map +1 -0
  100. package/dist/users/repository.js +78 -0
  101. package/dist/users/repository.js.map +1 -0
  102. package/package.json +41 -0
  103. package/src/db/migrations/0001_extensions.sql +5 -0
  104. package/src/db/migrations/0002_core_identity.sql +78 -0
  105. package/src/db/migrations/0003_rbac.sql +68 -0
  106. package/src/db/migrations/0004_directory.sql +35 -0
  107. package/src/db/migrations/0005_audit.sql +35 -0
  108. package/src/db/migrations/0006_rls_policies.sql +69 -0
  109. package/src/db/migrations/0007_requests_notifications_portal.sql +103 -0
  110. package/src/db/migrations/0008_client_deleted_by.sql +5 -0
  111. package/src/db/migrations/0009_client_scope_rls.sql +50 -0
  112. package/src/db/migrations/0010_product_entitlements.sql +43 -0
  113. package/src/db/migrations/0011_password_reset.sql +18 -0
package/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # @baruk/core — Motor Baruk (agnóstico de segmento)
2
+
3
+ Infraestrutura reutilizável: auth, tenancy, rbac, audit, vault, documents, communication, billing, notifications, events, search, ai-agents.
4
+
5
+ **Regra:** NÃO importa nenhuma vertical. Ver `docs/architecture/02-core-vs-verticals.md`.
6
+ Scaffold da Fase 06 — sem implementação de negócio ainda (roadmap: Fase 07+).
@@ -0,0 +1,22 @@
1
+ import { type ActorType } from '@baruk/shared';
2
+ import type { Tx } from '../db/client.js';
3
+ export interface AuditEntry {
4
+ actorType?: ActorType;
5
+ actorId?: string | null;
6
+ action: string;
7
+ resourceType: string;
8
+ resourceId?: string | null;
9
+ result?: 'success' | 'failure' | 'denied';
10
+ /** Antes/depois — a camada chamadora já deve redigir segredos. */
11
+ diff?: unknown;
12
+ metadata?: Record<string, unknown>;
13
+ ip?: string | null;
14
+ userAgent?: string | null;
15
+ }
16
+ /**
17
+ * Registra uma linha na trilha append-only. DEVE rodar dentro de um contexto de
18
+ * tenant (`withContext`): o `tenant_id` é derivado da GUC `app.current_tenant`,
19
+ * então a linha auditada sempre pertence ao tenant corrente e satisfaz a RLS.
20
+ */
21
+ export declare function appendAudit(tx: Tx, entry: AuditEntry): Promise<void>;
22
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/audit/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1C,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB1E"}
@@ -0,0 +1,28 @@
1
+ import { uuidv7 } from '@baruk/shared';
2
+ /**
3
+ * Registra uma linha na trilha append-only. DEVE rodar dentro de um contexto de
4
+ * tenant (`withContext`): o `tenant_id` é derivado da GUC `app.current_tenant`,
5
+ * então a linha auditada sempre pertence ao tenant corrente e satisfaz a RLS.
6
+ */
7
+ export async function appendAudit(tx, entry) {
8
+ await tx `
9
+ INSERT INTO audit_log (
10
+ id, tenant_id, actor_type, actor_id, action, resource_type,
11
+ resource_id, result, diff, metadata, ip, user_agent
12
+ ) VALUES (
13
+ ${uuidv7()},
14
+ current_setting('app.current_tenant')::uuid,
15
+ ${entry.actorType ?? 'user'},
16
+ ${entry.actorId ?? null},
17
+ ${entry.action},
18
+ ${entry.resourceType},
19
+ ${entry.resourceId ?? null},
20
+ ${entry.result ?? 'success'},
21
+ ${entry.diff === undefined ? null : tx.json(entry.diff)},
22
+ ${tx.json((entry.metadata ?? {}))},
23
+ ${entry.ip ?? null},
24
+ ${entry.userAgent ?? null}
25
+ )
26
+ `;
27
+ }
28
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/audit/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkB,MAAM,eAAe,CAAC;AAiBvD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAM,EAAE,KAAiB;IACzD,MAAM,EAAE,CAAA;;;;;QAKF,MAAM,EAAE;;QAER,KAAK,CAAC,SAAS,IAAI,MAAM;QACzB,KAAK,CAAC,OAAO,IAAI,IAAI;QACrB,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,YAAY;QAClB,KAAK,CAAC,UAAU,IAAI,IAAI;QACxB,KAAK,CAAC,MAAM,IAAI,SAAS;QACzB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAa,CAAC;QAC9D,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAU,CAAC;QACxC,KAAK,CAAC,EAAE,IAAI,IAAI;QAChB,KAAK,CAAC,SAAS,IAAI,IAAI;;GAE5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Tx } from '../db/client.js';
2
+ export interface AuditRow {
3
+ id: string;
4
+ actorType: string;
5
+ actorId: string | null;
6
+ action: string;
7
+ resourceType: string;
8
+ resourceId: string | null;
9
+ result: string;
10
+ occurredAt: string;
11
+ }
12
+ /** Lista a trilha de auditoria do tenant corrente (RLS já isola). Somente leitura. */
13
+ export declare function listAudit(tx: Tx, filter?: {
14
+ action?: string;
15
+ resourceType?: string;
16
+ limit?: number;
17
+ offset?: number;
18
+ }): Promise<{
19
+ items: AuditRow[];
20
+ total: number;
21
+ }>;
22
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/audit/repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,sFAAsF;AACtF,wBAAsB,SAAS,CAC7B,EAAE,EAAE,EAAE,EACN,MAAM,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACvF,OAAO,CAAC;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B/C"}
@@ -0,0 +1,29 @@
1
+ /** Lista a trilha de auditoria do tenant corrente (RLS já isola). Somente leitura. */
2
+ export async function listAudit(tx, filter = {}) {
3
+ const limit = Math.min(filter.limit ?? 30, 100);
4
+ const offset = filter.offset ?? 0;
5
+ const rows = await tx `
6
+ SELECT id, actor_type, actor_id, action, resource_type, resource_id, result,
7
+ occurred_at, count(*) OVER() AS total
8
+ FROM audit_log
9
+ WHERE (${filter.action ?? null}::text IS NULL OR action = ${filter.action ?? null})
10
+ AND (${filter.resourceType ?? null}::text IS NULL OR resource_type = ${filter.resourceType ?? null})
11
+ ORDER BY occurred_at DESC
12
+ LIMIT ${limit} OFFSET ${offset}
13
+ `;
14
+ const total = rows[0] ? Number(rows[0].total) : 0;
15
+ return {
16
+ total,
17
+ items: rows.map((r) => ({
18
+ id: r.id,
19
+ actorType: r.actor_type,
20
+ actorId: r.actor_id,
21
+ action: r.action,
22
+ resourceType: r.resource_type,
23
+ resourceId: r.resource_id,
24
+ result: r.result,
25
+ occurredAt: r.occurred_at.toISOString(),
26
+ })),
27
+ };
28
+ }
29
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/audit/repository.ts"],"names":[],"mappings":"AAaA,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAM,EACN,SAAsF,EAAE;IAExF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,EAAE,CAEpB;;;;aAIU,MAAM,CAAC,MAAM,IAAI,IAAI,8BAA8B,MAAM,CAAC,MAAM,IAAI,IAAI;aACxE,MAAM,CAAC,YAAY,IAAI,IAAI,qCAAqC,MAAM,CAAC,YAAY,IAAI,IAAI;;YAE5F,KAAK,WAAW,MAAM;GAC/B,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO;QACL,KAAK;QACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,OAAO,EAAE,CAAC,CAAC,QAAQ;YACnB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,YAAY,EAAE,CAAC,CAAC,aAAa;YAC7B,UAAU,EAAE,CAAC,CAAC,WAAW;YACzB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE;SACxC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Db } from '../db/client.js';
2
+ interface Nower {
3
+ now(): number;
4
+ }
5
+ /**
6
+ * Solicita reset. Retorna o token EM CLARO (para compor o link do e-mail) e o
7
+ * userId — ou null se o e-mail não existe (o chamador responde genericamente
8
+ * de qualquer forma, para não revelar existência de conta).
9
+ */
10
+ export declare function requestPasswordReset(db: Db, email: string, clock?: Nower): Promise<{
11
+ token: string;
12
+ userId: string;
13
+ } | null>;
14
+ /**
15
+ * Redefine a senha usando um token válido (não usado, não expirado). Marca o
16
+ * token como usado, limpa `must_change_password` e revoga sessões anteriores.
17
+ */
18
+ export declare function resetPassword(db: Db, token: string, newPassword: string, clock?: Nower): Promise<boolean>;
19
+ export {};
20
+ //# sourceMappingURL=password-reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-reset.d.ts","sourceRoot":"","sources":["../../src/auth/password-reset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAO1C,UAAU,KAAK;IACb,GAAG,IAAI,MAAM,CAAC;CACf;AAGD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,KAAkB,GACxB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAcnD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,KAAkB,GACxB,OAAO,CAAC,OAAO,CAAC,CAuBlB"}
@@ -0,0 +1,56 @@
1
+ import { uuidv7 } from '@baruk/shared';
2
+ // Convergência RFC-IDENTITY-003 §3.2: novos hashes via KDF canônico (variante async).
3
+ import { hashPasswordAsync } from './password.js';
4
+ import { generateSessionToken, hashToken } from './session-token.js';
5
+ const RESET_TTL_MS = 60 * 60 * 1000; // 1h
6
+ const REAL_CLOCK = { now: () => Date.now() };
7
+ /**
8
+ * Solicita reset. Retorna o token EM CLARO (para compor o link do e-mail) e o
9
+ * userId — ou null se o e-mail não existe (o chamador responde genericamente
10
+ * de qualquer forma, para não revelar existência de conta).
11
+ */
12
+ export async function requestPasswordReset(db, email, clock = REAL_CLOCK) {
13
+ const users = await db `
14
+ SELECT id, status FROM "user" WHERE email = ${email} AND deleted_at IS NULL
15
+ `;
16
+ const user = users[0];
17
+ if (!user || user.status === 'disabled')
18
+ return null;
19
+ const { token, tokenHash } = generateSessionToken();
20
+ const now = clock.now();
21
+ await db `
22
+ INSERT INTO password_reset (id, user_id, token_hash, expires_at)
23
+ VALUES (${uuidv7()}, ${user.id}, ${tokenHash}, ${new Date(now + RESET_TTL_MS)})
24
+ `;
25
+ return { token, userId: user.id };
26
+ }
27
+ /**
28
+ * Redefine a senha usando um token válido (não usado, não expirado). Marca o
29
+ * token como usado, limpa `must_change_password` e revoga sessões anteriores.
30
+ */
31
+ export async function resetPassword(db, token, newPassword, clock = REAL_CLOCK) {
32
+ if (newPassword.length < 8)
33
+ return false;
34
+ const th = hashToken(token);
35
+ const rows = await db `
36
+ SELECT id, user_id FROM password_reset
37
+ WHERE token_hash = ${th} AND used_at IS NULL AND expires_at > ${new Date(clock.now())}
38
+ `;
39
+ const reset = rows[0];
40
+ if (!reset)
41
+ return false;
42
+ const passwordHash = await hashPasswordAsync(newPassword);
43
+ await db.begin(async (tx) => {
44
+ await tx `
45
+ INSERT INTO user_credential (user_id, password_hash, must_change_password)
46
+ VALUES (${reset.user_id}, ${passwordHash}, false)
47
+ ON CONFLICT (user_id) DO UPDATE
48
+ SET password_hash = EXCLUDED.password_hash, must_change_password = false, updated_at = now()
49
+ `;
50
+ await tx `UPDATE password_reset SET used_at = now() WHERE id = ${reset.id}`;
51
+ // Revoga sessões anteriores (uso único de fato).
52
+ await tx `UPDATE user_session SET revoked_at = now() WHERE user_id = ${reset.user_id} AND revoked_at IS NULL`;
53
+ });
54
+ return true;
55
+ }
56
+ //# sourceMappingURL=password-reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-reset.js","sourceRoot":"","sources":["../../src/auth/password-reset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,sFAAsF;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAErE,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,KAAK;AAK1C,MAAM,UAAU,GAAU,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAM,EACN,KAAa,EACb,QAAe,UAAU;IAEzB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAkC;kDACR,KAAK;GACpD,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAErD,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,oBAAoB,EAAE,CAAC;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IACxB,MAAM,EAAE,CAAA;;cAEI,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,SAAS,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG,YAAY,CAAC;GAC9E,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAM,EACN,KAAa,EACb,WAAmB,EACnB,QAAe,UAAU;IAEzB,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAmC;;yBAEjC,EAAE,yCAAyC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;GACtF,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC1B,MAAM,EAAE,CAAA;;gBAEI,KAAK,CAAC,OAAO,KAAK,YAAY;;;KAGzC,CAAC;QACF,MAAM,EAAE,CAAA,wDAAwD,KAAK,CAAC,EAAE,EAAE,CAAC;QAC3E,iDAAiD;QACjD,MAAM,EAAE,CAAA,8DAA8D,KAAK,CAAC,OAAO,yBAAyB,CAAC;IAC/G,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * [contrato público — SÍNCRONO] Deriva um hash scrypt (keylen 64) com salt aleatório,
3
+ * no formato `scrypt$N$r$p$saltB64$hashB64`. Idêntico ao pré-R1.
4
+ */
5
+ export declare function hashPassword(password: string): string;
6
+ /**
7
+ * [contrato público — SÍNCRONO] Verifica a senha contra o hash armazenado, em tempo
8
+ * constante. Aceita o encoding legado (base64) e o canônico (base64url).
9
+ */
10
+ export declare function verifyPassword(password: string, stored: string): boolean;
11
+ /**
12
+ * [aditivo — convergência RFC-IDENTITY-003 §3.2] Deriva o hash via KDF CANÔNICO de
13
+ * @baruk/identity (scrypt keylen 64). Assíncrono (scrypt não-bloqueante). Emite base64url.
14
+ */
15
+ export declare function hashPasswordAsync(password: string): Promise<string>;
16
+ /**
17
+ * [aditivo — convergência RFC-IDENTITY-003 §3.2] Verifica via verify CANÔNICO de
18
+ * @baruk/identity — aceita os hashes legados (base64/keylen 64). Assíncrono.
19
+ */
20
+ export declare function verifyPasswordAsync(password: string, stored: string): Promise<boolean>;
21
+ //# sourceMappingURL=password.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/auth/password.ts"],"names":[],"mappings":"AA4BA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAYxE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEtF"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Senha/KDF do @baruk/core — contrato público SÍNCRONO preservado + convergência ADITIVA.
3
+ * ---------------------------------------------------------------------------
4
+ * RFC-IDENTITY-003 §3.2/§4 (remediação R1 — restauração de compatibilidade).
5
+ *
6
+ * Contrato público (barrel `@baruk/core`): `hashPassword`/`verifyPassword` seguem
7
+ * SÍNCRONOS (`string`/`boolean`), byte-a-byte idênticos ao pré-R1 — a superfície
8
+ * pública NÃO muda (SemVer preservado; RFC §2). O `verifyPassword` aceita tanto o
9
+ * encoding legado (base64) quanto o canônico (base64url), portanto não há gap de
10
+ * formato entre os hashes gravados pela via síncrona e pela via canônica.
11
+ *
12
+ * Convergência sobre @baruk/identity (RFC-IDENTITY-003 §3.2) é ADITIVA: exposta
13
+ * via `hashPasswordAsync`/`verifyPasswordAsync`, que consomem o KDF canônico
14
+ * (scrypt, params da plataforma keylen 64). Novos hashes canônicos saem em
15
+ * base64url (uniformização sancionada pela RFC §4); os legados seguem válidos.
16
+ */
17
+ import { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';
18
+ import { hashPassword as canonicalHash, verifyPassword as canonicalVerify } from '@baruk/identity';
19
+ /** Params da plataforma — idênticos ao KDF legado do core (keylen 64). RFC-IDENTITY-003 §3.2. */
20
+ const PLATFORM_SCRYPT = { N: 16384, r: 8, p: 1, keylen: 64 };
21
+ /** Decodifica base64 OU base64url (tolerante): o verify aceita hashes de ambas as vias. */
22
+ function decodeB64(s) {
23
+ return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64');
24
+ }
25
+ /**
26
+ * [contrato público — SÍNCRONO] Deriva um hash scrypt (keylen 64) com salt aleatório,
27
+ * no formato `scrypt$N$r$p$saltB64$hashB64`. Idêntico ao pré-R1.
28
+ */
29
+ export function hashPassword(password) {
30
+ const { N, r, p, keylen } = PLATFORM_SCRYPT;
31
+ const salt = randomBytes(16);
32
+ const derived = scryptSync(password, salt, keylen, { N, r, p });
33
+ return ['scrypt', N, r, p, salt.toString('base64'), derived.toString('base64')].join('$');
34
+ }
35
+ /**
36
+ * [contrato público — SÍNCRONO] Verifica a senha contra o hash armazenado, em tempo
37
+ * constante. Aceita o encoding legado (base64) e o canônico (base64url).
38
+ */
39
+ export function verifyPassword(password, stored) {
40
+ const parts = stored.split('$');
41
+ if (parts.length !== 6 || parts[0] !== 'scrypt')
42
+ return false;
43
+ const [, n, r, p, saltEnc, hashEnc] = parts;
44
+ const salt = decodeB64(saltEnc);
45
+ const expected = decodeB64(hashEnc);
46
+ const derived = scryptSync(password, salt, expected.length, {
47
+ N: Number(n),
48
+ r: Number(r),
49
+ p: Number(p),
50
+ });
51
+ return derived.length === expected.length && timingSafeEqual(derived, expected);
52
+ }
53
+ /**
54
+ * [aditivo — convergência RFC-IDENTITY-003 §3.2] Deriva o hash via KDF CANÔNICO de
55
+ * @baruk/identity (scrypt keylen 64). Assíncrono (scrypt não-bloqueante). Emite base64url.
56
+ */
57
+ export function hashPasswordAsync(password) {
58
+ return canonicalHash(password, PLATFORM_SCRYPT);
59
+ }
60
+ /**
61
+ * [aditivo — convergência RFC-IDENTITY-003 §3.2] Verifica via verify CANÔNICO de
62
+ * @baruk/identity — aceita os hashes legados (base64/keylen 64). Assíncrono.
63
+ */
64
+ export function verifyPasswordAsync(password, stored) {
65
+ return canonicalVerify(password, stored);
66
+ }
67
+ //# sourceMappingURL=password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/auth/password.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,YAAY,IAAI,aAAa,EAAE,cAAc,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGnG,iGAAiG;AACjG,MAAM,eAAe,GAAiB,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAE3E,2FAA2F;AAC3F,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC;IAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;IAC5C,MAAM,IAAI,GAAG,SAAS,CAAC,OAAQ,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAQ,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE;QAC1D,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACZ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACZ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;KACb,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAc;IAClE,OAAO,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { type UserType } from '@baruk/shared';
2
+ import type { AuditContext } from '@baruk/shared';
3
+ import type { Db } from '../db/client.js';
4
+ import { type ProductAccess } from '../products/repository.js';
5
+ import type { Principal, Membership } from '@baruk/identity';
6
+ export interface AuthUser {
7
+ id: string;
8
+ email: string;
9
+ fullName: string;
10
+ userType: UserType;
11
+ }
12
+ export interface MembershipView {
13
+ tenantId: string;
14
+ tenantCode: string;
15
+ tenantName: string;
16
+ status: string;
17
+ }
18
+ export interface LoginResult {
19
+ token: string;
20
+ user: AuthUser;
21
+ memberships: MembershipView[];
22
+ /** Preenchido quando o usuário tem exatamente um tenant (auto-seleção). */
23
+ activeTenantId: string | null;
24
+ }
25
+ /** Contexto autenticado e já resolvido a um tenant (o que a API injeta por request). */
26
+ export interface AuthContext {
27
+ userId: string;
28
+ userType: UserType;
29
+ tenantId: string;
30
+ permissions: Set<string>;
31
+ /** Para usuários de portal: o client ao qual estão restritos (row-scope). */
32
+ clientId?: string;
33
+ }
34
+ interface Nower {
35
+ now(): number;
36
+ }
37
+ /** Cria (ou atualiza) um usuário staff com senha local. Usado pelo seed. */
38
+ export declare function upsertUserWithPassword(db: Db, input: {
39
+ email: string;
40
+ fullName: string;
41
+ password: string;
42
+ userType?: UserType;
43
+ }): Promise<AuthUser>;
44
+ /** Login com e-mail/senha. Emite sessão; auto-seleciona tenant se houver só um. */
45
+ export declare function login(db: Db, input: {
46
+ email: string;
47
+ password: string;
48
+ } & AuditContext, clock?: Nower): Promise<LoginResult>;
49
+ /** Lista os tenants a que o usuário pertence (usa RLS self-clause de membership). */
50
+ export declare function listMemberships(db: Db, userId: string): Promise<MembershipView[]>;
51
+ /** Escolhe/troca o tenant ativo de uma sessão, validando a membership. */
52
+ export declare function selectTenant(db: Db, input: {
53
+ token: string;
54
+ tenantId: string;
55
+ } & AuditContext): Promise<AuthContext>;
56
+ /**
57
+ * Resolve uma sessão ativa em contexto autenticado (o que o middleware da API
58
+ * chama por request). Exige tenant ativo já selecionado.
59
+ */
60
+ export declare function authenticate(db: Db, token: string, clock?: Nower): Promise<AuthContext>;
61
+ /** Memberships da sessão atual (para o seletor de tenant, antes de escolher um). */
62
+ export declare function sessionMemberships(db: Db, token: string): Promise<MembershipView[]>;
63
+ /** Produtos que a sessão pode acessar (entitlements) + audiência. Roteamento do Hub. */
64
+ export declare function sessionProducts(db: Db, token: string): Promise<{
65
+ userType: UserType;
66
+ products: ProductAccess[];
67
+ }>;
68
+ /** Revoga a sessão (logout). */
69
+ export declare function logout(db: Db, token: string): Promise<void>;
70
+ /**
71
+ * Convergência de contratos (RFC-IDENTITY-003 §3.5) — adaptadores finos que
72
+ * projetam os tipos de DOMÍNIO da plataforma sobre os contratos CANÔNICOS de
73
+ * `@baruk/identity`. São ADITIVOS: não alteram o schema/DB nem os tipos que a
74
+ * API consome hoje (`AuthUser`/`MembershipView`/`AuthContext` permanecem os
75
+ * contratos de domínio multi-tenant). Habilitam a adoção incremental dos
76
+ * canônicos no cutover para o registry (C10). O `AuthContext` canônico usa
77
+ * `permissions` como mapa escopo→chaves e `activeScopeId = tenantId`.
78
+ */
79
+ export declare function toPrincipal(user: AuthUser, scopeId: string): Principal;
80
+ export declare function toMembership(view: MembershipView, principalId: string): Membership;
81
+ export {};
82
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/auth/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ import { ForbiddenError, UnauthorizedError, uuidv7 } from '@baruk/shared';
2
+ import { withContext } from '../db/context.js';
3
+ import { appendAudit } from '../audit/audit.js';
4
+ import { loadUserPermissions } from '../rbac/authorize.js';
5
+ import { getPortalClientId } from '../portal/repository.js';
6
+ import { listProductsForUser } from '../products/repository.js';
7
+ // Convergência RFC-IDENTITY-003 §3.2: o core adota o KDF canônico via as variantes
8
+ // async (o contrato público síncrono hashPassword/verifyPassword segue exportado no barrel).
9
+ import { hashPasswordAsync, verifyPasswordAsync } from './password.js';
10
+ import { generateSessionToken, hashToken } from './session-token.js';
11
+ const SESSION_TTL_MS = 12 * 60 * 60 * 1000; // 12h
12
+ const REAL_CLOCK = { now: () => Date.now() };
13
+ /** Cria (ou atualiza) um usuário staff com senha local. Usado pelo seed. */
14
+ export async function upsertUserWithPassword(db, input) {
15
+ const rows = await db `
16
+ INSERT INTO "user" (id, email, full_name, user_type, auth_provider)
17
+ VALUES (${uuidv7()}, ${input.email}, ${input.fullName}, ${input.userType ?? 'staff'}, 'password')
18
+ ON CONFLICT (email) WHERE deleted_at IS NULL
19
+ DO UPDATE SET full_name = EXCLUDED.full_name, updated_at = now()
20
+ RETURNING id
21
+ `;
22
+ const userId = rows[0].id;
23
+ const passwordHash = await hashPasswordAsync(input.password);
24
+ await db `
25
+ INSERT INTO user_credential (user_id, password_hash)
26
+ VALUES (${userId}, ${passwordHash})
27
+ ON CONFLICT (user_id) DO UPDATE SET password_hash = EXCLUDED.password_hash, updated_at = now()
28
+ `;
29
+ return {
30
+ id: userId,
31
+ email: input.email,
32
+ fullName: input.fullName,
33
+ userType: input.userType ?? 'staff',
34
+ };
35
+ }
36
+ /** Login com e-mail/senha. Emite sessão; auto-seleciona tenant se houver só um. */
37
+ export async function login(db, input, clock = REAL_CLOCK) {
38
+ const users = await db `
39
+ SELECT id, email, full_name, user_type, status
40
+ FROM "user" WHERE email = ${input.email} AND deleted_at IS NULL
41
+ `;
42
+ const user = users[0];
43
+ if (!user || user.status !== 'active') {
44
+ throw new UnauthorizedError('Credenciais inválidas');
45
+ }
46
+ const creds = await db `
47
+ SELECT password_hash FROM user_credential WHERE user_id = ${user.id}
48
+ `;
49
+ if (!creds[0] || !(await verifyPasswordAsync(input.password, creds[0].password_hash))) {
50
+ throw new UnauthorizedError('Credenciais inválidas');
51
+ }
52
+ const { token, tokenHash } = generateSessionToken();
53
+ const now = clock.now();
54
+ const sessionId = uuidv7();
55
+ const expiresAt = new Date(now + SESSION_TTL_MS);
56
+ await db `
57
+ INSERT INTO user_session (id, user_id, token_hash, active_tenant_id, expires_at)
58
+ VALUES (${sessionId}, ${user.id}, ${tokenHash}, NULL, ${expiresAt})
59
+ `;
60
+ await db `UPDATE "user" SET last_login_at = ${new Date(now)} WHERE id = ${user.id}`;
61
+ const memberships = await listMemberships(db, user.id);
62
+ let activeTenantId = null;
63
+ if (memberships.length === 1) {
64
+ activeTenantId = memberships[0].tenantId;
65
+ await db `UPDATE user_session SET active_tenant_id = ${activeTenantId} WHERE id = ${sessionId}`;
66
+ await auditWithinTenant(db, activeTenantId, user.id, {
67
+ action: 'login',
68
+ resourceType: 'user_session',
69
+ resourceId: sessionId,
70
+ ip: input.ip,
71
+ userAgent: input.userAgent,
72
+ });
73
+ }
74
+ return {
75
+ token,
76
+ user: { id: user.id, email: user.email, fullName: user.full_name, userType: user.user_type },
77
+ memberships,
78
+ activeTenantId,
79
+ };
80
+ }
81
+ /** Lista os tenants a que o usuário pertence (usa RLS self-clause de membership). */
82
+ export async function listMemberships(db, userId) {
83
+ return withContext(db, { userId }, async (tx) => {
84
+ const rows = await tx `
85
+ SELECT m.tenant_id, t.code, t.display_name, m.status
86
+ FROM membership m
87
+ JOIN tenant t ON t.id = m.tenant_id
88
+ WHERE m.user_id = ${userId} AND m.deleted_at IS NULL AND m.status = 'active'
89
+ ORDER BY t.display_name
90
+ `;
91
+ return rows.map((r) => ({
92
+ tenantId: r.tenant_id,
93
+ tenantCode: r.code,
94
+ tenantName: r.display_name,
95
+ status: r.status,
96
+ }));
97
+ });
98
+ }
99
+ /** Escolhe/troca o tenant ativo de uma sessão, validando a membership. */
100
+ export async function selectTenant(db, input) {
101
+ const session = await resolveSessionRow(db, input.token);
102
+ const memberships = await listMemberships(db, session.user_id);
103
+ const allowed = memberships.some((m) => m.tenantId === input.tenantId);
104
+ if (!allowed) {
105
+ throw new ForbiddenError('Usuário não pertence a este tenant');
106
+ }
107
+ await db `
108
+ UPDATE user_session SET active_tenant_id = ${input.tenantId} WHERE id = ${session.id}
109
+ `;
110
+ return withContext(db, { tenantId: input.tenantId, userId: session.user_id }, async (tx) => {
111
+ const permissions = await loadUserPermissions(tx, session.user_id);
112
+ await appendAudit(tx, {
113
+ actorId: session.user_id,
114
+ action: 'select_tenant',
115
+ resourceType: 'tenant',
116
+ resourceId: input.tenantId,
117
+ ip: input.ip,
118
+ userAgent: input.userAgent,
119
+ });
120
+ return {
121
+ userId: session.user_id,
122
+ userType: session.user_type,
123
+ tenantId: input.tenantId,
124
+ permissions,
125
+ };
126
+ });
127
+ }
128
+ /**
129
+ * Resolve uma sessão ativa em contexto autenticado (o que o middleware da API
130
+ * chama por request). Exige tenant ativo já selecionado.
131
+ */
132
+ export async function authenticate(db, token, clock = REAL_CLOCK) {
133
+ const session = await resolveSessionRow(db, token, clock);
134
+ if (!session.active_tenant_id) {
135
+ throw new UnauthorizedError('Nenhum tenant selecionado para esta sessão');
136
+ }
137
+ const tenantId = session.active_tenant_id;
138
+ return withContext(db, { tenantId, userId: session.user_id }, async (tx) => {
139
+ const permissions = await loadUserPermissions(tx, session.user_id);
140
+ const ctx = {
141
+ userId: session.user_id,
142
+ userType: session.user_type,
143
+ tenantId,
144
+ permissions,
145
+ };
146
+ if (session.user_type === 'client_portal') {
147
+ ctx.clientId = await getPortalClientId(tx, session.user_id);
148
+ }
149
+ return ctx;
150
+ });
151
+ }
152
+ async function resolveSessionRow(db, token, clock = REAL_CLOCK) {
153
+ const rows = await db `
154
+ SELECT s.id, s.user_id, u.user_type, s.active_tenant_id
155
+ FROM user_session s
156
+ JOIN "user" u ON u.id = s.user_id
157
+ WHERE s.token_hash = ${hashToken(token)}
158
+ AND s.revoked_at IS NULL
159
+ AND s.expires_at > ${new Date(clock.now())}
160
+ AND u.deleted_at IS NULL
161
+ `;
162
+ if (!rows[0])
163
+ throw new UnauthorizedError('Sessão inválida ou expirada');
164
+ return rows[0];
165
+ }
166
+ /** Memberships da sessão atual (para o seletor de tenant, antes de escolher um). */
167
+ export async function sessionMemberships(db, token) {
168
+ const session = await resolveSessionRow(db, token);
169
+ return listMemberships(db, session.user_id);
170
+ }
171
+ /** Produtos que a sessão pode acessar (entitlements) + audiência. Roteamento do Hub. */
172
+ export async function sessionProducts(db, token) {
173
+ const session = await resolveSessionRow(db, token);
174
+ const products = await withContext(db, { userId: session.user_id }, (tx) => listProductsForUser(tx, session.user_id));
175
+ return { userType: session.user_type, products };
176
+ }
177
+ /** Revoga a sessão (logout). */
178
+ export async function logout(db, token) {
179
+ await db `UPDATE user_session SET revoked_at = now() WHERE token_hash = ${hashToken(token)}`;
180
+ }
181
+ async function auditWithinTenant(db, tenantId, actorId, entry) {
182
+ await withContext(db, { tenantId, userId: actorId }, (tx) => appendAudit(tx, entry));
183
+ }
184
+ /**
185
+ * Convergência de contratos (RFC-IDENTITY-003 §3.5) — adaptadores finos que
186
+ * projetam os tipos de DOMÍNIO da plataforma sobre os contratos CANÔNICOS de
187
+ * `@baruk/identity`. São ADITIVOS: não alteram o schema/DB nem os tipos que a
188
+ * API consome hoje (`AuthUser`/`MembershipView`/`AuthContext` permanecem os
189
+ * contratos de domínio multi-tenant). Habilitam a adoção incremental dos
190
+ * canônicos no cutover para o registry (C10). O `AuthContext` canônico usa
191
+ * `permissions` como mapa escopo→chaves e `activeScopeId = tenantId`.
192
+ */
193
+ export function toPrincipal(user, scopeId) {
194
+ return {
195
+ id: user.id,
196
+ email: user.email,
197
+ kind: user.userType === 'client_portal' ? 'client' : 'staff',
198
+ scopeId,
199
+ status: 'active',
200
+ sessionVersion: 0,
201
+ };
202
+ }
203
+ export function toMembership(view, principalId) {
204
+ return {
205
+ id: `${principalId}:${view.tenantId}`,
206
+ principalId,
207
+ scopeId: view.tenantId,
208
+ scopeSlug: view.tenantCode,
209
+ scopeName: view.tenantName,
210
+ roleKeys: [],
211
+ status: view.status === 'active' ? 'active' : 'suspended',
212
+ };
213
+ }
214
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/auth/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AAGzF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAsB,MAAM,2BAA2B,CAAC;AACpF,mFAAmF;AACnF,6FAA6F;AAC7F,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGrE,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,MAAM;AAqClD,MAAM,UAAU,GAAU,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AAEpD,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAM,EACN,KAAiF;IAEjF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAkB;;cAE3B,MAAM,EAAE,KAAK,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,OAAO;;;;GAIpF,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;IAC3B,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAA;;cAEI,MAAM,KAAK,YAAY;;GAElC,CAAC;IACF,OAAO;QACL,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAO;KACpC,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,EAAM,EACN,KAAyD,EACzD,QAAe,UAAU;IAEzB,MAAM,KAAK,GAAG,MAAM,EAAE,CAErB;;gCAE6B,KAAK,CAAC,KAAK;GACxC,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACtC,MAAM,IAAI,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAA6B;gEACW,IAAI,CAAC,EAAE;GACpE,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,mBAAmB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,oBAAoB,EAAE,CAAC;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IACxB,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,cAAc,CAAC,CAAC;IAEjD,MAAM,EAAE,CAAA;;cAEI,SAAS,KAAK,IAAI,CAAC,EAAE,KAAK,SAAS,WAAW,SAAS;GAClE,CAAC;IACF,MAAM,EAAE,CAAA,qCAAqC,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,EAAE,EAAE,CAAC;IAEnF,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAEvD,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,cAAc,GAAG,WAAW,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC;QAC1C,MAAM,EAAE,CAAA,8CAA8C,cAAc,eAAe,SAAS,EAAE,CAAC;QAC/F,MAAM,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;YACnD,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,cAAc;YAC5B,UAAU,EAAE,SAAS;YACrB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,KAAK;QACL,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE;QAC5F,WAAW;QACX,cAAc;KACf,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAM,EAAE,MAAc;IAC1D,OAAO,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,MAAM,EAAE,CAEpB;;;;0BAIqB,MAAM;;KAE3B,CAAC;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,QAAQ,EAAE,CAAC,CAAC,SAAS;YACrB,UAAU,EAAE,CAAC,CAAC,IAAI;YAClB,UAAU,EAAE,CAAC,CAAC,YAAY;YAC1B,MAAM,EAAE,CAAC,CAAC,MAAM;SACjB,CAAC,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAM,EACN,KAAyD;IAEzD,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,cAAc,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,CAAA;iDACuC,KAAK,CAAC,QAAQ,eAAe,OAAO,CAAC,EAAE;GACrF,CAAC;IACF,OAAO,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QACzF,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,WAAW,CAAC,EAAE,EAAE;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,eAAe;YACvB,YAAY,EAAE,QAAQ;YACtB,UAAU,EAAE,KAAK,CAAC,QAAQ;YAC1B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;QACH,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,OAAO;YACvB,QAAQ,EAAE,OAAO,CAAC,SAAS;YAC3B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,WAAW;SACZ,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAM,EACN,KAAa,EACb,QAAe,UAAU;IAEzB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1D,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC9B,MAAM,IAAI,iBAAiB,CAAC,4CAA4C,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC1C,OAAO,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QACzE,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,GAAG,GAAgB;YACvB,MAAM,EAAE,OAAO,CAAC,OAAO;YACvB,QAAQ,EAAE,OAAO,CAAC,SAAS;YAC3B,QAAQ;YACR,WAAW;SACZ,CAAC;QACF,IAAI,OAAO,CAAC,SAAS,KAAK,eAAe,EAAE,CAAC;YAC1C,GAAG,CAAC,QAAQ,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC;AASD,KAAK,UAAU,iBAAiB,CAC9B,EAAM,EACN,KAAa,EACb,QAAe,UAAU;IAEzB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAc;;;;2BAIV,SAAS,CAAC,KAAK,CAAC;;2BAEhB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;;GAE7C,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,6BAA6B,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAM,EAAE,KAAa;IAC5D,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACnD,OAAO,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAM,EACN,KAAa;IAEb,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CACzE,mBAAmB,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CACzC,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC;AACnD,CAAC;AAED,gCAAgC;AAChC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAM,EAAE,KAAa;IAChD,MAAM,EAAE,CAAA,iEAAiE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,EAAM,EACN,QAAgB,EAChB,OAAe,EACf,KAAwC;IAExC,MAAM,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,IAAc,EAAE,OAAe;IACzD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,QAAQ,KAAK,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;QAC5D,OAAO;QACP,MAAM,EAAE,QAAQ;QAChB,cAAc,EAAE,CAAC;KAClB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAoB,EAAE,WAAmB;IACpE,OAAO;QACL,EAAE,EAAE,GAAG,WAAW,IAAI,IAAI,CAAC,QAAQ,EAAE;QACrC,WAAW;QACX,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,SAAS,EAAE,IAAI,CAAC,UAAU;QAC1B,SAAS,EAAE,IAAI,CAAC,UAAU;QAC1B,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW;KAC1D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Gera o token opaco entregue ao cliente + o hash a persistir (nunca o token cru). */
2
+ export declare function generateSessionToken(): {
3
+ token: string;
4
+ tokenHash: string;
5
+ };
6
+ /** Hash determinístico (SHA-256 hex) para armazenar/resolver a sessão. */
7
+ export declare function hashToken(token: string): string;
8
+ //# sourceMappingURL=session-token.d.ts.map