@revealui/auth 0.4.9 → 0.5.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.
- package/dist/react/csrf.d.ts +5 -12
- package/dist/react/csrf.d.ts.map +1 -1
- package/dist/react/csrf.js +5 -32
- package/dist/server/audit-bridge.d.ts.map +1 -1
- package/dist/server/audit-bridge.js +12 -10
- package/dist/server/audit-storage.d.ts +119 -0
- package/dist/server/audit-storage.d.ts.map +1 -0
- package/dist/server/audit-storage.js +295 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +4 -3
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -1
- package/dist/server/mfa-enforcement.d.ts +29 -0
- package/dist/server/mfa-enforcement.d.ts.map +1 -1
- package/dist/server/mfa-enforcement.js +27 -1
- package/dist/server/security-alerts.d.ts +17 -0
- package/dist/server/security-alerts.d.ts.map +1 -0
- package/dist/server/security-alerts.js +55 -0
- package/dist/server/session.d.ts +2 -0
- package/dist/server/session.d.ts.map +1 -1
- package/dist/server/session.js +1 -0
- package/dist/utils/database.d.ts +1 -1
- package/dist/utils/database.d.ts.map +1 -1
- package/package.json +16 -8
package/dist/react/csrf.d.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CSRF Double-Submit Token Helper
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Re-exports the canonical browser cookie reader from
|
|
5
|
+
* `@revealui/core/admin/utils/csrf` (fleet-redundancy P2-C). Shared by the
|
|
6
|
+
* react auth hooks (`usePasskey`, `useMFA`, `useSignOut`) whose POSTs target
|
|
7
|
+
* the RevealUI admin proxy's CSRF-gated endpoints.
|
|
6
8
|
*/
|
|
7
|
-
|
|
8
|
-
* Read the JS-readable `revealui-csrf` cookie. The RevealUI admin proxy
|
|
9
|
-
* (`apps/admin/src/proxy.ts`) requires it as an `X-CSRF-Token` header on any
|
|
10
|
-
* session-cookie-bearing unsafe request, and may re-issue the cookie on any
|
|
11
|
-
* response — so call this immediately before each fetch rather than once per
|
|
12
|
-
* flow. Returns undefined outside the browser or when the cookie is
|
|
13
|
-
* absent/empty. Mirrors the cookie-read in `@revealui/core`'s admin APIClient
|
|
14
|
-
* (`request()`) and `@revealui/ai`'s `useAgentStream`.
|
|
15
|
-
*/
|
|
16
|
-
export declare function readCsrfToken(): string | undefined;
|
|
9
|
+
export { readCsrfToken } from '@revealui/core/admin/utils/csrf';
|
|
17
10
|
//# sourceMappingURL=csrf.d.ts.map
|
package/dist/react/csrf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/react/csrf.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/react/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC"}
|
package/dist/react/csrf.js
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CSRF Double-Submit Token Helper
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Re-exports the canonical browser cookie reader from
|
|
5
|
+
* `@revealui/core/admin/utils/csrf` (fleet-redundancy P2-C). Shared by the
|
|
6
|
+
* react auth hooks (`usePasskey`, `useMFA`, `useSignOut`) whose POSTs target
|
|
7
|
+
* the RevealUI admin proxy's CSRF-gated endpoints.
|
|
6
8
|
*/
|
|
7
|
-
|
|
8
|
-
* Read the JS-readable `revealui-csrf` cookie. The RevealUI admin proxy
|
|
9
|
-
* (`apps/admin/src/proxy.ts`) requires it as an `X-CSRF-Token` header on any
|
|
10
|
-
* session-cookie-bearing unsafe request, and may re-issue the cookie on any
|
|
11
|
-
* response — so call this immediately before each fetch rather than once per
|
|
12
|
-
* flow. Returns undefined outside the browser or when the cookie is
|
|
13
|
-
* absent/empty. Mirrors the cookie-read in `@revealui/core`'s admin APIClient
|
|
14
|
-
* (`request()`) and `@revealui/ai`'s `useAgentStream`.
|
|
15
|
-
*/
|
|
16
|
-
export function readCsrfToken() {
|
|
17
|
-
if (typeof document === 'undefined')
|
|
18
|
-
return undefined;
|
|
19
|
-
for (const part of document.cookie.split(';')) {
|
|
20
|
-
const eqIdx = part.indexOf('=');
|
|
21
|
-
if (eqIdx === -1)
|
|
22
|
-
continue;
|
|
23
|
-
if (part.slice(0, eqIdx).trim() === 'revealui-csrf') {
|
|
24
|
-
const raw = part.slice(eqIdx + 1).trim();
|
|
25
|
-
if (!raw)
|
|
26
|
-
return undefined;
|
|
27
|
-
try {
|
|
28
|
-
return decodeURIComponent(raw);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return raw;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
9
|
+
export { readCsrfToken } from '@revealui/core/admin/utils/csrf';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-bridge.d.ts","sourceRoot":"","sources":["../../src/server/audit-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"audit-bridge.d.ts","sourceRoot":"","sources":["../../src/server/audit-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASnF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/E;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAShF;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* avoid circular dependency issues at module load time.
|
|
7
7
|
*/
|
|
8
8
|
import { logger } from '@revealui/core/observability/logger';
|
|
9
|
+
import { evaluateSecurityAlert } from './security-alerts.js';
|
|
9
10
|
/**
|
|
10
11
|
* Lazily resolve the global audit system from @revealui/security/server.
|
|
11
12
|
* Returns null if the module cannot be loaded (e.g. in test environments
|
|
@@ -28,17 +29,18 @@ async function getAudit() {
|
|
|
28
29
|
*/
|
|
29
30
|
async function logAuditEvent(event) {
|
|
30
31
|
const auditSystem = await getAudit();
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
32
|
+
if (auditSystem) {
|
|
33
|
+
try {
|
|
34
|
+
await auditSystem.log(event);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.error('Failed to write audit event', error instanceof Error ? error : undefined, {
|
|
38
|
+
type: event.type,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
41
|
}
|
|
42
|
+
// Threshold alerts (failed logins, MFA disable, lockouts, …) — C11 WIRE
|
|
43
|
+
await evaluateSecurityAlert(event);
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
46
|
* Record a successful login to the audit trail.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit storage boundary + signer composition — the ONE shared home (GAP-338).
|
|
3
|
+
*
|
|
4
|
+
* Moved here from `apps/server/src/lib/{audit-storage,audit-signer}.ts` so BOTH
|
|
5
|
+
* server processes (Hono api + Next.js admin) can swap the process-wide
|
|
6
|
+
* `@revealui/security` AuditSystem onto persistent storage. Before this move the
|
|
7
|
+
* boundary was stranded in apps/server, so every admin-process audit emit
|
|
8
|
+
* (including the GAP-334 login receipts wired through `audit-bridge.ts` in this
|
|
9
|
+
* package) landed in that process's default `InMemoryAuditStorage` and
|
|
10
|
+
* evaporated on restart. `@revealui/auth` is the home because it already sits
|
|
11
|
+
* above `@revealui/security`, `@revealui/db`, and `@revealui/core`, is consumed
|
|
12
|
+
* by both apps, and owns the auth→audit bridge whose emits this rescues.
|
|
13
|
+
*
|
|
14
|
+
* This is THE boundary between two audit models that intentionally differ
|
|
15
|
+
* (see docs/decisions/2026-07-12-audit-receipt-architecture.md §5):
|
|
16
|
+
*
|
|
17
|
+
* - `@revealui/security` emits `AuditEvent` with severity
|
|
18
|
+
* `low | medium | high | critical` (the request-middleware vocabulary).
|
|
19
|
+
* - `@revealui/db` / `@revealui/ai` own `audit_log`, whose CHECK constraint
|
|
20
|
+
* accepts only `info | warn | critical`. That model wins; the security
|
|
21
|
+
* vocabulary is mapped here, at the boundary, so every emitted event lands
|
|
22
|
+
* instead of being silently rejected by the constraint.
|
|
23
|
+
*
|
|
24
|
+
* Signing (GAP-355 Stage 3): the `DrizzleAuditStore` is built via
|
|
25
|
+
* `createAuditStore`, which injects the process-wide Ed25519 row signer. On a
|
|
26
|
+
* signing deployment every row lands with a `v1.ed25519` signature verifiable
|
|
27
|
+
* offline from the published public key; in dev/test (no key) rows stay honestly
|
|
28
|
+
* unsigned. `previous_signature` is never written (the hash chain is abandoned),
|
|
29
|
+
* which is what makes the two-writer topology (api + admin processes appending
|
|
30
|
+
* to one `audit_log`) safe: rows are independent, each signed at its own door
|
|
31
|
+
* with the same env-derived key. Ruled for GAP-338 — see the gap file.
|
|
32
|
+
*/
|
|
33
|
+
import type { AuditRowSignerFn, DrizzleAuditStore as DrizzleAuditStoreType } from '@revealui/db';
|
|
34
|
+
import type { Database } from '@revealui/db/client';
|
|
35
|
+
import type { AuditEvent, AuditQuery, AuditSeverity, AuditStorage, AuditSystem } from '@revealui/security/server';
|
|
36
|
+
/**
|
|
37
|
+
* The process-wide audit-row signer (composed once from `process.env`), or
|
|
38
|
+
* `undefined` in unsigned mode. Cached so the mode is resolved and logged
|
|
39
|
+
* exactly once per process (GAP-355 Stage 3, spec D5/D6: key present → SIGNING;
|
|
40
|
+
* absent → UNSIGNED, legal only in dev/test — a production signing deployment
|
|
41
|
+
* refuses to boot without the key).
|
|
42
|
+
*/
|
|
43
|
+
export declare function getAuditRowSigner(): AuditRowSignerFn | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Construct a `DrizzleAuditStore` wired to the process-wide signer. Every audit
|
|
46
|
+
* writer builds its store through this helper so a row written through the one
|
|
47
|
+
* door on a signing deployment always carries a signature.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createAuditStore(db: Database): DrizzleAuditStoreType;
|
|
50
|
+
/** Test-only reset of the cached signer (re-reads env on next `getAuditRowSigner`). */
|
|
51
|
+
export declare function __resetAuditSignerForTest(): void;
|
|
52
|
+
/** DB-side severity vocabulary — matches the `audit_log_severity_check` constraint. */
|
|
53
|
+
type DbSeverity = 'info' | 'warn' | 'critical';
|
|
54
|
+
export declare function mapSeverityToDb(severity: AuditSeverity): DbSeverity;
|
|
55
|
+
/**
|
|
56
|
+
* `AuditStorage` implementation backed by `DrizzleAuditStore`. Re-homes the
|
|
57
|
+
* row-storing responsibility onto `DrizzleAuditStore.append()` and keeps the
|
|
58
|
+
* security-model boundary mapping (severity + columns) here, so `@revealui/db`
|
|
59
|
+
* stays free of a dependency on the security package.
|
|
60
|
+
*/
|
|
61
|
+
export declare class DrizzleBackedAuditStorage implements AuditStorage {
|
|
62
|
+
private readonly store;
|
|
63
|
+
constructor(db: Database);
|
|
64
|
+
write(event: AuditEvent): Promise<void>;
|
|
65
|
+
query(query: AuditQuery): Promise<AuditEvent[]>;
|
|
66
|
+
count(query: AuditQuery): Promise<number>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Synchronous env-parity assertion for the audit write path — GAP-355 Stage 1
|
|
70
|
+
* closure, owner-ruled 2026-07-17.
|
|
71
|
+
*
|
|
72
|
+
* A serving process installs audit storage but cannot always run the async
|
|
73
|
+
* round-trip self-test (serverless has no clean "refuse to serve" for a
|
|
74
|
+
* boot-time DB round trip). This asserts the audit path's ENV preconditions
|
|
75
|
+
* SYNCHRONOUSLY at the install point, so a deploy whose audit-critical env has
|
|
76
|
+
* diverged (a required var missing or empty) FAILS THE DEPLOY rather than
|
|
77
|
+
* serving with an audit sink that silently drops every row.
|
|
78
|
+
*
|
|
79
|
+
* Scope, honestly: this catches env-var ABSENCE/emptiness synchronously — which
|
|
80
|
+
* `installAuditStorage()` itself does not, because `getClient()` is a lazy pool
|
|
81
|
+
* factory, so a missing connection URL would otherwise surface only on the
|
|
82
|
+
* first write, at request time. It does NOT, and on serverless cannot, catch
|
|
83
|
+
* migration-state divergence (the `audit_log` table missing or misshapen on the
|
|
84
|
+
* target DB); that needs the write-read round trip of `auditStorageSelfTest`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function assertAuditStorageEnv(env?: NodeJS.ProcessEnv): void;
|
|
87
|
+
/**
|
|
88
|
+
* Swap the process-wide `audit` system onto persistent Postgres storage.
|
|
89
|
+
* Synchronous and side-effect-free at call time: `getClient()` is a lazy pool
|
|
90
|
+
* factory (no connection opened here), so this is safe to run on a serverless
|
|
91
|
+
* cold-start path. WITHOUT this call, audit events fall into the default
|
|
92
|
+
* `InMemoryAuditStorage` and evaporate on restart — which is exactly what
|
|
93
|
+
* happened to every admin-process emit before GAP-338.
|
|
94
|
+
*
|
|
95
|
+
* Call `assertAuditStorageEnv()` before this at each install site so a
|
|
96
|
+
* diverged-env deploy fails loudly instead of installing a store that can
|
|
97
|
+
* never write.
|
|
98
|
+
*/
|
|
99
|
+
export declare function installAuditStorage(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Boot-time round-trip self-test. Writes a synthetic audit event through the
|
|
102
|
+
* REAL installed path (AuditSystem → boundary → DrizzleAuditStore → audit_log)
|
|
103
|
+
* and reads it back. Throws if the round trip fails, so a runtime that cannot
|
|
104
|
+
* record agent actions REFUSES TO SERVE rather than silently dropping them
|
|
105
|
+
* (fail-closed integrity — ADR §2a).
|
|
106
|
+
*
|
|
107
|
+
* Uses `severity: 'low'` deliberately: that value would be rejected by the DB
|
|
108
|
+
* CHECK constraint if the boundary mapping regressed, so this exercises the
|
|
109
|
+
* exact defect Stage 1 fixes. A healthy audit path lets startup proceed.
|
|
110
|
+
*
|
|
111
|
+
* Runs on the long-running worker (once per deploy) and the dev boot path,
|
|
112
|
+
* where an async boot chain already exists and `process.exit(1)` gives
|
|
113
|
+
* "refuse to serve" clean semantics. It is NOT run on serverless cold-start
|
|
114
|
+
* paths, which stay free of per-invocation DB round-trips (storage is still
|
|
115
|
+
* installed there synchronously by `installAuditStorage()`).
|
|
116
|
+
*/
|
|
117
|
+
export declare function auditStorageSelfTest(auditSystem?: AuditSystem): Promise<void>;
|
|
118
|
+
export {};
|
|
119
|
+
//# sourceMappingURL=audit-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-storage.d.ts","sourceRoot":"","sources":["../../src/server/audit-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,IAAI,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAWnC;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,gBAAgB,GAAG,SAAS,CAgBhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,qBAAqB,CAEpE;AAED,uFAAuF;AACvF,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAED,uFAAuF;AACvF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAe/C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,UAAU,CAEnE;AASD;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,YAAY;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;gBAElC,EAAE,EAAE,QAAQ;IAOlB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IA4B/C,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAMhD;AAgCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CA0ChF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,oBAAoB,CAAC,WAAW,GAAE,WAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB1F"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit storage boundary + signer composition — the ONE shared home (GAP-338).
|
|
3
|
+
*
|
|
4
|
+
* Moved here from `apps/server/src/lib/{audit-storage,audit-signer}.ts` so BOTH
|
|
5
|
+
* server processes (Hono api + Next.js admin) can swap the process-wide
|
|
6
|
+
* `@revealui/security` AuditSystem onto persistent storage. Before this move the
|
|
7
|
+
* boundary was stranded in apps/server, so every admin-process audit emit
|
|
8
|
+
* (including the GAP-334 login receipts wired through `audit-bridge.ts` in this
|
|
9
|
+
* package) landed in that process's default `InMemoryAuditStorage` and
|
|
10
|
+
* evaporated on restart. `@revealui/auth` is the home because it already sits
|
|
11
|
+
* above `@revealui/security`, `@revealui/db`, and `@revealui/core`, is consumed
|
|
12
|
+
* by both apps, and owns the auth→audit bridge whose emits this rescues.
|
|
13
|
+
*
|
|
14
|
+
* This is THE boundary between two audit models that intentionally differ
|
|
15
|
+
* (see docs/decisions/2026-07-12-audit-receipt-architecture.md §5):
|
|
16
|
+
*
|
|
17
|
+
* - `@revealui/security` emits `AuditEvent` with severity
|
|
18
|
+
* `low | medium | high | critical` (the request-middleware vocabulary).
|
|
19
|
+
* - `@revealui/db` / `@revealui/ai` own `audit_log`, whose CHECK constraint
|
|
20
|
+
* accepts only `info | warn | critical`. That model wins; the security
|
|
21
|
+
* vocabulary is mapped here, at the boundary, so every emitted event lands
|
|
22
|
+
* instead of being silently rejected by the constraint.
|
|
23
|
+
*
|
|
24
|
+
* Signing (GAP-355 Stage 3): the `DrizzleAuditStore` is built via
|
|
25
|
+
* `createAuditStore`, which injects the process-wide Ed25519 row signer. On a
|
|
26
|
+
* signing deployment every row lands with a `v1.ed25519` signature verifiable
|
|
27
|
+
* offline from the published public key; in dev/test (no key) rows stay honestly
|
|
28
|
+
* unsigned. `previous_signature` is never written (the hash chain is abandoned),
|
|
29
|
+
* which is what makes the two-writer topology (api + admin processes appending
|
|
30
|
+
* to one `audit_log`) safe: rows are independent, each signed at its own door
|
|
31
|
+
* with the same env-derived key. Ruled for GAP-338 — see the gap file.
|
|
32
|
+
*/
|
|
33
|
+
import { randomUUID } from 'node:crypto';
|
|
34
|
+
import { logger } from '@revealui/core/observability/logger';
|
|
35
|
+
import { DrizzleAuditStore, getClient, hasDatabaseConnectionEnv } from '@revealui/db';
|
|
36
|
+
import { audit, classifyAuditWriteFailure, createAuditRowSignerFromEnv, recordAuditWriteResult, } from '@revealui/security/server';
|
|
37
|
+
let cachedSigner;
|
|
38
|
+
let resolved = false;
|
|
39
|
+
/**
|
|
40
|
+
* The process-wide audit-row signer (composed once from `process.env`), or
|
|
41
|
+
* `undefined` in unsigned mode. Cached so the mode is resolved and logged
|
|
42
|
+
* exactly once per process (GAP-355 Stage 3, spec D5/D6: key present → SIGNING;
|
|
43
|
+
* absent → UNSIGNED, legal only in dev/test — a production signing deployment
|
|
44
|
+
* refuses to boot without the key).
|
|
45
|
+
*/
|
|
46
|
+
export function getAuditRowSigner() {
|
|
47
|
+
if (!resolved) {
|
|
48
|
+
const { signer, mode, kid } = createAuditRowSignerFromEnv(process.env);
|
|
49
|
+
if (mode === 'signed') {
|
|
50
|
+
logger.info(`AUDIT SIGNING: ENABLED (alg=ed25519, kid=${kid})`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
logger.warn('AUDIT SIGNING: DISABLED — audit rows will be written UNSIGNED (no ' +
|
|
54
|
+
'REVEALUI_AUDIT_SIGNING_KEY). Legal only in dev/test; a production signing ' +
|
|
55
|
+
'deployment refuses to boot without the key.');
|
|
56
|
+
}
|
|
57
|
+
cachedSigner = signer;
|
|
58
|
+
resolved = true;
|
|
59
|
+
}
|
|
60
|
+
return cachedSigner;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Construct a `DrizzleAuditStore` wired to the process-wide signer. Every audit
|
|
64
|
+
* writer builds its store through this helper so a row written through the one
|
|
65
|
+
* door on a signing deployment always carries a signature.
|
|
66
|
+
*/
|
|
67
|
+
export function createAuditStore(db) {
|
|
68
|
+
return new DrizzleAuditStore(db, getAuditRowSigner());
|
|
69
|
+
}
|
|
70
|
+
/** Test-only reset of the cached signer (re-reads env on next `getAuditRowSigner`). */
|
|
71
|
+
export function __resetAuditSignerForTest() {
|
|
72
|
+
cachedSigner = undefined;
|
|
73
|
+
resolved = false;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Severity mapping AT THE BOUNDARY (ADR §5). Total over the security
|
|
77
|
+
* vocabulary; every value lands in the DB CHECK vocabulary. Declared as a
|
|
78
|
+
* `Record<AuditSeverity, ...>` so the compiler rejects any future severity
|
|
79
|
+
* that is not mapped.
|
|
80
|
+
*/
|
|
81
|
+
const SEVERITY_TO_DB = {
|
|
82
|
+
low: 'info',
|
|
83
|
+
medium: 'warn',
|
|
84
|
+
high: 'critical',
|
|
85
|
+
critical: 'critical',
|
|
86
|
+
};
|
|
87
|
+
export function mapSeverityToDb(severity) {
|
|
88
|
+
return SEVERITY_TO_DB[severity];
|
|
89
|
+
}
|
|
90
|
+
/** Reverse map for reconstructing legacy rows written without a full payload. */
|
|
91
|
+
const DB_TO_SEVERITY = {
|
|
92
|
+
info: 'low',
|
|
93
|
+
warn: 'medium',
|
|
94
|
+
critical: 'critical',
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* `AuditStorage` implementation backed by `DrizzleAuditStore`. Re-homes the
|
|
98
|
+
* row-storing responsibility onto `DrizzleAuditStore.append()` and keeps the
|
|
99
|
+
* security-model boundary mapping (severity + columns) here, so `@revealui/db`
|
|
100
|
+
* stays free of a dependency on the security package.
|
|
101
|
+
*/
|
|
102
|
+
export class DrizzleBackedAuditStorage {
|
|
103
|
+
store;
|
|
104
|
+
constructor(db) {
|
|
105
|
+
// createAuditStore injects the process-wide Ed25519 signer (GAP-355 Stage 3),
|
|
106
|
+
// so rows land signed on a signing deployment and NULL-signature in unsigned
|
|
107
|
+
// (dev/test) mode. The severity/column boundary mapping stays here.
|
|
108
|
+
this.store = createAuditStore(db);
|
|
109
|
+
}
|
|
110
|
+
async write(event) {
|
|
111
|
+
try {
|
|
112
|
+
// The injected signer (createAuditStore) signs the row at the door on a
|
|
113
|
+
// signing deployment; a signer failure makes append THROW (fail-closed),
|
|
114
|
+
// caught below and routed through the write-result rails.
|
|
115
|
+
await this.store.append({
|
|
116
|
+
id: event.id,
|
|
117
|
+
timestamp: new Date(event.timestamp),
|
|
118
|
+
eventType: event.type,
|
|
119
|
+
severity: mapSeverityToDb(event.severity),
|
|
120
|
+
agentId: event.actor.id,
|
|
121
|
+
payload: event,
|
|
122
|
+
policyViolations: [],
|
|
123
|
+
});
|
|
124
|
+
recordAuditWriteResult({ ok: true, eventId: event.id, eventType: event.type });
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
recordAuditWriteResult({
|
|
128
|
+
ok: false,
|
|
129
|
+
reason: classifyAuditWriteFailure(err),
|
|
130
|
+
eventId: event.id,
|
|
131
|
+
eventType: event.type,
|
|
132
|
+
});
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async query(query) {
|
|
137
|
+
const dbSeverities = query.severity && query.severity.length > 0
|
|
138
|
+
? [...new Set(query.severity.map(mapSeverityToDb))]
|
|
139
|
+
: undefined;
|
|
140
|
+
const entries = await this.store.query({
|
|
141
|
+
agentId: query.actorId,
|
|
142
|
+
eventTypes: query.types ? [...query.types] : undefined,
|
|
143
|
+
severity: dbSeverities,
|
|
144
|
+
startTime: query.startDate,
|
|
145
|
+
endTime: query.endDate,
|
|
146
|
+
limit: query.limit ?? 100,
|
|
147
|
+
offset: query.offset ?? 0,
|
|
148
|
+
});
|
|
149
|
+
return entries
|
|
150
|
+
.map((entry) => reconstructEvent(entry))
|
|
151
|
+
.filter((event) => {
|
|
152
|
+
if (query.resourceType && event.resource?.type !== query.resourceType)
|
|
153
|
+
return false;
|
|
154
|
+
if (query.resourceId && event.resource?.id !== query.resourceId)
|
|
155
|
+
return false;
|
|
156
|
+
if (query.result && query.result.length > 0 && !query.result.includes(event.result)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async count(query) {
|
|
163
|
+
// Count ignores pagination (matches InMemoryAuditStorage semantics). Query
|
|
164
|
+
// with an unbounded limit and count the post-filtered results.
|
|
165
|
+
const events = await this.query({ ...query, limit: Number.MAX_SAFE_INTEGER, offset: 0 });
|
|
166
|
+
return events.length;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Reconstruct the full `AuditEvent` from a stored row. The complete event is
|
|
171
|
+
* persisted in the `payload` column, so it round-trips exactly. Rows written
|
|
172
|
+
* before this adapter (or by another writer without a full-event payload) fall
|
|
173
|
+
* back to a column-derived event.
|
|
174
|
+
*/
|
|
175
|
+
function reconstructEvent(entry) {
|
|
176
|
+
const stored = entry.payload;
|
|
177
|
+
if (stored && typeof stored === 'object' && 'type' in stored) {
|
|
178
|
+
return stored;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
id: entry.id,
|
|
182
|
+
timestamp: entry.timestamp.toISOString(),
|
|
183
|
+
type: entry.eventType,
|
|
184
|
+
severity: DB_TO_SEVERITY[entry.severity] ?? 'low',
|
|
185
|
+
actor: { id: entry.agentId, type: 'system' },
|
|
186
|
+
action: entry.eventType,
|
|
187
|
+
result: 'success',
|
|
188
|
+
metadata: stored ?? undefined,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Synchronous env-parity assertion for the audit write path — GAP-355 Stage 1
|
|
193
|
+
* closure, owner-ruled 2026-07-17.
|
|
194
|
+
*
|
|
195
|
+
* A serving process installs audit storage but cannot always run the async
|
|
196
|
+
* round-trip self-test (serverless has no clean "refuse to serve" for a
|
|
197
|
+
* boot-time DB round trip). This asserts the audit path's ENV preconditions
|
|
198
|
+
* SYNCHRONOUSLY at the install point, so a deploy whose audit-critical env has
|
|
199
|
+
* diverged (a required var missing or empty) FAILS THE DEPLOY rather than
|
|
200
|
+
* serving with an audit sink that silently drops every row.
|
|
201
|
+
*
|
|
202
|
+
* Scope, honestly: this catches env-var ABSENCE/emptiness synchronously — which
|
|
203
|
+
* `installAuditStorage()` itself does not, because `getClient()` is a lazy pool
|
|
204
|
+
* factory, so a missing connection URL would otherwise surface only on the
|
|
205
|
+
* first write, at request time. It does NOT, and on serverless cannot, catch
|
|
206
|
+
* migration-state divergence (the `audit_log` table missing or misshapen on the
|
|
207
|
+
* target DB); that needs the write-read round trip of `auditStorageSelfTest`.
|
|
208
|
+
*/
|
|
209
|
+
export function assertAuditStorageEnv(env = process.env) {
|
|
210
|
+
// GAP-417 item 5: the predicate is OWNED by @revealui/db and matches
|
|
211
|
+
// getClient()'s resolution exactly (config url, then POSTGRES_URL /
|
|
212
|
+
// DATABASE_URL). The previous local triple also accepted DATABASE_HOST,
|
|
213
|
+
// which getClient() never consults — so the assert passed, the install then
|
|
214
|
+
// threw, and production silently kept the in-memory sink (proven in the
|
|
215
|
+
// #2161 re-review). Never re-inline this check.
|
|
216
|
+
if (!hasDatabaseConnectionEnv(env)) {
|
|
217
|
+
throw new Error('AUDIT STORAGE ENV PARITY FAILED: no usable database connection is configured ' +
|
|
218
|
+
'(set POSTGRES_URL or DATABASE_URL, or provide @revealui/config database.url), ' +
|
|
219
|
+
'so the audit write path cannot persist rows. Refusing to serve — an agent ' +
|
|
220
|
+
'action that cannot be recorded must not execute (fail-closed integrity, ' +
|
|
221
|
+
'docs/decisions/2026-07-12-audit-receipt-architecture.md §2a).');
|
|
222
|
+
}
|
|
223
|
+
// Signing key (GAP-355 Stage 3): a production signing deployment must have the
|
|
224
|
+
// Ed25519 key so rows land signed. Absence is a diverged-env deploy — fail it
|
|
225
|
+
// synchronously here (the serverless serving process's refuse-to-serve), the
|
|
226
|
+
// same rail the DB-connection check above uses. Full Ed25519 PKCS#8 parsing is
|
|
227
|
+
// done by validate-startup; this is the audit-owned presence parity check, so
|
|
228
|
+
// the contract cannot silently drift on one serving process. Dev/test
|
|
229
|
+
// (NODE_ENV !== 'production') runs unsigned by design and is exempt.
|
|
230
|
+
//
|
|
231
|
+
// GAP-417 items 1-2 (owner-countersigned 2026-07-25): SKIP_ENV_VALIDATION no
|
|
232
|
+
// longer exempts this check. The audit path has NO escape hatch — a
|
|
233
|
+
// production process that cannot sign must not boot, because unsigned rows
|
|
234
|
+
// are indistinguishable from tampering AND permanently stall anchor
|
|
235
|
+
// contiguity once the sweep filters them. SKIP still covers non-audit env
|
|
236
|
+
// validation elsewhere; it never buys an unsigned production audit log.
|
|
237
|
+
if (env.NODE_ENV === 'production') {
|
|
238
|
+
if (!env.REVEALUI_AUDIT_SIGNING_KEY) {
|
|
239
|
+
throw new Error('AUDIT STORAGE ENV PARITY FAILED: REVEALUI_AUDIT_SIGNING_KEY is not set on a ' +
|
|
240
|
+
'production deployment, so the audit write path cannot sign rows. Refusing to ' +
|
|
241
|
+
'serve — an unsigned row in the post-Stage-3 era is indistinguishable from ' +
|
|
242
|
+
'tampering (docs/decisions/2026-07-12-audit-receipt-architecture.md §2a). ' +
|
|
243
|
+
'SKIP_ENV_VALIDATION does not exempt the audit path (GAP-417).');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Swap the process-wide `audit` system onto persistent Postgres storage.
|
|
249
|
+
* Synchronous and side-effect-free at call time: `getClient()` is a lazy pool
|
|
250
|
+
* factory (no connection opened here), so this is safe to run on a serverless
|
|
251
|
+
* cold-start path. WITHOUT this call, audit events fall into the default
|
|
252
|
+
* `InMemoryAuditStorage` and evaporate on restart — which is exactly what
|
|
253
|
+
* happened to every admin-process emit before GAP-338.
|
|
254
|
+
*
|
|
255
|
+
* Call `assertAuditStorageEnv()` before this at each install site so a
|
|
256
|
+
* diverged-env deploy fails loudly instead of installing a store that can
|
|
257
|
+
* never write.
|
|
258
|
+
*/
|
|
259
|
+
export function installAuditStorage() {
|
|
260
|
+
audit.setStorage(new DrizzleBackedAuditStorage(getClient()));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Boot-time round-trip self-test. Writes a synthetic audit event through the
|
|
264
|
+
* REAL installed path (AuditSystem → boundary → DrizzleAuditStore → audit_log)
|
|
265
|
+
* and reads it back. Throws if the round trip fails, so a runtime that cannot
|
|
266
|
+
* record agent actions REFUSES TO SERVE rather than silently dropping them
|
|
267
|
+
* (fail-closed integrity — ADR §2a).
|
|
268
|
+
*
|
|
269
|
+
* Uses `severity: 'low'` deliberately: that value would be rejected by the DB
|
|
270
|
+
* CHECK constraint if the boundary mapping regressed, so this exercises the
|
|
271
|
+
* exact defect Stage 1 fixes. A healthy audit path lets startup proceed.
|
|
272
|
+
*
|
|
273
|
+
* Runs on the long-running worker (once per deploy) and the dev boot path,
|
|
274
|
+
* where an async boot chain already exists and `process.exit(1)` gives
|
|
275
|
+
* "refuse to serve" clean semantics. It is NOT run on serverless cold-start
|
|
276
|
+
* paths, which stay free of per-invocation DB round-trips (storage is still
|
|
277
|
+
* installed there synchronously by `installAuditStorage()`).
|
|
278
|
+
*/
|
|
279
|
+
export async function auditStorageSelfTest(auditSystem = audit) {
|
|
280
|
+
const marker = `__audit-self-test__:${randomUUID()}`;
|
|
281
|
+
const written = await auditSystem.log({
|
|
282
|
+
type: 'security.audit_self_test',
|
|
283
|
+
severity: 'low',
|
|
284
|
+
actor: { id: marker, type: 'system' },
|
|
285
|
+
action: 'audit-storage-self-test',
|
|
286
|
+
result: 'success',
|
|
287
|
+
metadata: { synthetic: true },
|
|
288
|
+
});
|
|
289
|
+
const found = await auditSystem.query({ actorId: marker, limit: 5 });
|
|
290
|
+
if (!found.some((event) => event.id === written.id)) {
|
|
291
|
+
throw new Error('AUDIT STORAGE SELF-TEST FAILED: wrote a synthetic audit event but could not read it ' +
|
|
292
|
+
'back. Refusing to serve — a runtime that cannot record agent actions must not accept ' +
|
|
293
|
+
'traffic (fail-closed integrity, docs/decisions/2026-07-12-audit-receipt-architecture.md §2a).');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAC;AAUpE;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,YAAY,CAAC,CAyLvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAC;AAUpE;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,YAAY,CAAC,CAyLvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,YAAY,CAAC,CAqOvB"}
|
package/dist/server/auth.js
CHANGED
|
@@ -398,9 +398,10 @@ export async function signUp(email, password, name, options) {
|
|
|
398
398
|
// exists from signup. Best-effort: the Stripe billing webhook
|
|
399
399
|
// (ensureHostedAccount) is idempotent on an active membership and backfills
|
|
400
400
|
// this if it fails here, so a transient error never blocks signup. Skipped on
|
|
401
|
-
// self-hosted (Forge) deployments — single-tenant, no per-user accounts
|
|
402
|
-
//
|
|
403
|
-
|
|
401
|
+
// self-hosted (Forge) deployments — single-tenant, no per-user accounts
|
|
402
|
+
// (GAP-260 P4-1: REVEALUI_DEPLOYMENT_MODE, fallback private-key presence).
|
|
403
|
+
const { isHostedDeployment } = await import('@revealui/core/deployment-mode');
|
|
404
|
+
if (isHostedDeployment(process.env)) {
|
|
404
405
|
try {
|
|
405
406
|
const accountId = crypto.randomUUID();
|
|
406
407
|
const now = new Date();
|
package/dist/server/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { configureMagicLink, createMagicLink, resetMagicLinkConfig, verifyMagicL
|
|
|
14
14
|
export type { MFAConfig, MFADisableProof, MFASetupResult } from './mfa.js';
|
|
15
15
|
export { configureMFA, disableMFA, initiateMFASetup, isMFAEnabled, regenerateBackupCodes, resetMFAConfig, verifyBackupCode, verifyMFACode, verifyMFASetup, } from './mfa.js';
|
|
16
16
|
export type { MfaCheckResult, MfaEnforcementOptions, MfaErrorResponse, MfaRequest, MfaSession, MfaSessionUser, } from './mfa-enforcement.js';
|
|
17
|
-
export { requireMfa } from './mfa-enforcement.js';
|
|
17
|
+
export { checkSessionMfa, requireMfa, toMfaSession } from './mfa-enforcement.js';
|
|
18
18
|
export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, type ProviderUser, type UpsertOAuthOptions, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
|
|
19
19
|
export type { PasskeyConfig } from './passkey.js';
|
|
20
20
|
export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenticationChallenge, generateRegistrationChallenge, listPasskeys, renamePasskey, resetPasskeyConfig, storePasskey, verifyAuthentication, verifyRegistration, } from './passkey.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,+BAA+B,EAC/B,6BAA6B,EAC7B,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -6,12 +6,18 @@
|
|
|
6
6
|
*/
|
|
7
7
|
// Audit bridge
|
|
8
8
|
export { auditAccountLocked, auditLoginFailure, auditLoginSuccess, auditMfaDisabled, auditMfaEnabled, auditPasswordChange, auditPasswordReset, auditSessionRevoked, } from './audit-bridge.js';
|
|
9
|
+
// NOTE (GAP-338): the audit storage boundary lives at the DEDICATED subpath
|
|
10
|
+
// `@revealui/auth/audit-storage`, deliberately NOT re-exported from this
|
|
11
|
+
// barrel. `@revealui/auth/server` is the fleet's most-mocked module (route
|
|
12
|
+
// tests bare-mock it for getSession), and a bare vi.mock of this barrel must
|
|
13
|
+
// never swallow the audit write path (it broke the GAP-352 + mcp-endpoint
|
|
14
|
+
// integration suites when the boundary briefly lived here).
|
|
9
15
|
export { isSignupAllowed, signIn, signUp } from './auth.js';
|
|
10
16
|
export { clearFailedAttempts, configureBruteForce, getFailedAttemptCount, isAccountLocked, recordFailedAttempt, resetBruteForceConfig, } from './brute-force.js';
|
|
11
17
|
export { AuthError, AuthenticationError, DatabaseError, OAuthAccountConflictError, SessionError, TokenError, } from './errors.js';
|
|
12
18
|
export { configureMagicLink, createMagicLink, resetMagicLinkConfig, verifyMagicLink, } from './magic-link.js';
|
|
13
19
|
export { configureMFA, disableMFA, initiateMFASetup, isMFAEnabled, regenerateBackupCodes, resetMFAConfig, verifyBackupCode, verifyMFACode, verifyMFASetup, } from './mfa.js';
|
|
14
|
-
export { requireMfa } from './mfa-enforcement.js';
|
|
20
|
+
export { checkSessionMfa, requireMfa, toMfaSession } from './mfa-enforcement.js';
|
|
15
21
|
export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
|
|
16
22
|
export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenticationChallenge, generateRegistrationChallenge, listPasskeys, renamePasskey, resetPasskeyConfig, storePasskey, verifyAuthentication, verifyRegistration, } from './passkey.js';
|
|
17
23
|
export { changePassword, generatePasswordResetToken, invalidatePasswordResetToken, resetPasswordWithToken, validatePasswordResetToken, } from './password-reset.js';
|
|
@@ -73,4 +73,33 @@ export interface MfaCheckResult {
|
|
|
73
73
|
* ```
|
|
74
74
|
*/
|
|
75
75
|
export declare function requireMfa(options?: MfaEnforcementOptions): (request: MfaRequest) => MfaCheckResult;
|
|
76
|
+
/**
|
|
77
|
+
* Build MFA session shape from getSession / rotateSession results.
|
|
78
|
+
* `mfaVerified` comes from session.metadata set after MFA login (verify/backup).
|
|
79
|
+
*/
|
|
80
|
+
export declare function toMfaSession(sessionData: {
|
|
81
|
+
user: {
|
|
82
|
+
id: string;
|
|
83
|
+
role: string;
|
|
84
|
+
mfaEnabled?: boolean | null;
|
|
85
|
+
};
|
|
86
|
+
session: {
|
|
87
|
+
metadata?: Record<string, unknown> | null;
|
|
88
|
+
};
|
|
89
|
+
}): MfaSession;
|
|
90
|
+
/**
|
|
91
|
+
* One-shot MFA check for a SessionData-like object (Next.js / Hono handlers).
|
|
92
|
+
*/
|
|
93
|
+
export declare function checkSessionMfa(sessionData: {
|
|
94
|
+
user: {
|
|
95
|
+
id: string;
|
|
96
|
+
role: string;
|
|
97
|
+
mfaEnabled?: boolean | null;
|
|
98
|
+
};
|
|
99
|
+
session: {
|
|
100
|
+
metadata?: Record<string, unknown> | null;
|
|
101
|
+
};
|
|
102
|
+
}, options?: MfaEnforcementOptions & {
|
|
103
|
+
operation?: string;
|
|
104
|
+
}): MfaCheckResult;
|
|
76
105
|
//# sourceMappingURL=mfa-enforcement.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mfa-enforcement.d.ts","sourceRoot":"","sources":["../../src/server/mfa-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,GAAG,qBAAqB,CAAC;CAC9C;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"mfa-enforcement.d.ts","sourceRoot":"","sources":["../../src/server/mfa-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,GAAG,qBAAqB,CAAC;CAC9C;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AASD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,UAAU,CACxB,OAAO,GAAE,qBAA0B,GAClC,CAAC,OAAO,EAAE,UAAU,KAAK,cAAc,CAgDzC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE;IACxC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IAChE,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD,GAAG,UAAU,CAUb;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE;IACX,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IAChE,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CACxD,EACD,OAAO,CAAC,EAAE,qBAAqB,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,cAAc,CAMhB"}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// =============================================================================
|
|
8
8
|
// Middleware
|
|
9
9
|
// =============================================================================
|
|
10
|
-
|
|
10
|
+
/** Admin-level DB roles that require MFA for sensitive operations (C11). */
|
|
11
|
+
const DEFAULT_ROLES = ['admin', 'owner'];
|
|
11
12
|
/**
|
|
12
13
|
* Create an MFA enforcement checker.
|
|
13
14
|
*
|
|
@@ -74,3 +75,28 @@ export function requireMfa(options = {}) {
|
|
|
74
75
|
return { allowed: true };
|
|
75
76
|
};
|
|
76
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Build MFA session shape from getSession / rotateSession results.
|
|
80
|
+
* `mfaVerified` comes from session.metadata set after MFA login (verify/backup).
|
|
81
|
+
*/
|
|
82
|
+
export function toMfaSession(sessionData) {
|
|
83
|
+
const meta = sessionData.session.metadata ?? null;
|
|
84
|
+
return {
|
|
85
|
+
user: {
|
|
86
|
+
id: sessionData.user.id,
|
|
87
|
+
role: sessionData.user.role,
|
|
88
|
+
mfaEnabled: sessionData.user.mfaEnabled === true,
|
|
89
|
+
mfaVerified: meta?.mfaVerified === true,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One-shot MFA check for a SessionData-like object (Next.js / Hono handlers).
|
|
95
|
+
*/
|
|
96
|
+
export function checkSessionMfa(sessionData, options) {
|
|
97
|
+
const check = requireMfa(options);
|
|
98
|
+
return check({
|
|
99
|
+
session: toMfaSession(sessionData),
|
|
100
|
+
operation: options?.operation,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecurityAlertService bridge (fleet-redundancy C11).
|
|
3
|
+
*
|
|
4
|
+
* Lazy singleton: evaluates auth/security audit events against DEFAULT_THRESHOLDS
|
|
5
|
+
* and logs via LogAlertHandler. Best-effort — never throws into the login path.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuditEvent } from '@revealui/security/server';
|
|
8
|
+
type AuditEventInput = Omit<AuditEvent, 'id' | 'timestamp'>;
|
|
9
|
+
/** Reset singleton (tests only). */
|
|
10
|
+
export declare function resetSecurityAlertService(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Feed an audit-shaped event into SecurityAlertService.evaluateEvent.
|
|
13
|
+
* Never throws; failures are logged and swallowed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function evaluateSecurityAlert(event: AuditEventInput): Promise<void>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=security-alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-alerts.d.ts","sourceRoot":"","sources":["../../src/server/security-alerts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,KAAK,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;AA6B5D,oCAAoC;AACpC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAejF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecurityAlertService bridge (fleet-redundancy C11).
|
|
3
|
+
*
|
|
4
|
+
* Lazy singleton: evaluates auth/security audit events against DEFAULT_THRESHOLDS
|
|
5
|
+
* and logs via LogAlertHandler. Best-effort — never throws into the login path.
|
|
6
|
+
*/
|
|
7
|
+
import { logger } from '@revealui/core/observability/logger';
|
|
8
|
+
let service;
|
|
9
|
+
/**
|
|
10
|
+
* Lazily construct the process-wide SecurityAlertService, or null if the
|
|
11
|
+
* security package cannot be loaded (e.g. partial test mocks).
|
|
12
|
+
*/
|
|
13
|
+
async function getAlertService() {
|
|
14
|
+
if (service !== undefined) {
|
|
15
|
+
return service;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const { SecurityAlertService: AlertService, DEFAULT_THRESHOLDS, LogAlertHandler, } = await import('@revealui/security');
|
|
19
|
+
service = new AlertService({
|
|
20
|
+
thresholds: DEFAULT_THRESHOLDS,
|
|
21
|
+
handlers: [new LogAlertHandler()],
|
|
22
|
+
});
|
|
23
|
+
return service;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
service = null;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Reset singleton (tests only). */
|
|
31
|
+
export function resetSecurityAlertService() {
|
|
32
|
+
service = undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Feed an audit-shaped event into SecurityAlertService.evaluateEvent.
|
|
36
|
+
* Never throws; failures are logged and swallowed.
|
|
37
|
+
*/
|
|
38
|
+
export async function evaluateSecurityAlert(event) {
|
|
39
|
+
try {
|
|
40
|
+
const alerts = await getAlertService();
|
|
41
|
+
if (!alerts)
|
|
42
|
+
return;
|
|
43
|
+
const full = {
|
|
44
|
+
id: crypto.randomUUID(),
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
...event,
|
|
47
|
+
};
|
|
48
|
+
await alerts.evaluateEvent(full);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.error('Failed to evaluate security alert', error instanceof Error ? error : undefined, {
|
|
52
|
+
type: event.type,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/server/session.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export declare function rotateSession(userId: string, options?: {
|
|
|
86
86
|
persistent?: boolean;
|
|
87
87
|
userAgent?: string;
|
|
88
88
|
ipAddress?: string;
|
|
89
|
+
/** Session metadata (e.g. `{ mfaVerified: true }` after MFA login). */
|
|
90
|
+
metadata?: Record<string, unknown>;
|
|
89
91
|
}): Promise<{
|
|
90
92
|
token: string;
|
|
91
93
|
session: Session;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;CACzB;AAaD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAEtF;AAED,qCAAqC;AACrC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAMD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CA0B3F;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgI7B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAmE9C;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;CACzB;AAaD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAEtF;AAED,qCAAqC;AACrC,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAMD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CA0B3F;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgI7B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAmE9C;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAqC9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BtE;AAED;;;;GAIG;AACH;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBzE"}
|
package/dist/server/session.js
CHANGED
package/dist/utils/database.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare function getTestDatabaseUrl(): string;
|
|
|
18
18
|
/**
|
|
19
19
|
* Creates a test database client
|
|
20
20
|
*/
|
|
21
|
-
export declare function createTestDatabaseClient(): import("@revealui/db
|
|
21
|
+
export declare function createTestDatabaseClient(): import("@revealui/db").DatabaseClient;
|
|
22
22
|
/**
|
|
23
23
|
* Cleans up test data from database
|
|
24
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/utils/database.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAU3C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/utils/database.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAU3C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,0CAMvC;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBtE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B7E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CASxE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAStF;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,aAAa,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,EAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAClC,OAAO,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAI3C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Database-backed session auth for Hono and Next.js — bcrypt, OAuth, brute-force protection, rate limiting, password reset. Ships with RevealUI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"drizzle-orm": "^0.45.2",
|
|
16
16
|
"zod": "^4.4.3",
|
|
17
17
|
"@revealui/config": "0.6.0",
|
|
18
|
-
"@revealui/contracts": "0.8.
|
|
19
|
-
"@revealui/core": "0.12.
|
|
20
|
-
"@revealui/db": "0.
|
|
21
|
-
"@revealui/security": "0.
|
|
18
|
+
"@revealui/contracts": "0.8.1",
|
|
19
|
+
"@revealui/core": "0.12.2",
|
|
20
|
+
"@revealui/db": "0.10.0",
|
|
21
|
+
"@revealui/security": "0.6.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@simplewebauthn/browser": "^13.3.0",
|
|
@@ -38,15 +38,23 @@
|
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|
|
40
40
|
"types": "./dist/index.d.ts",
|
|
41
|
-
"import": "./dist/index.js"
|
|
41
|
+
"import": "./dist/index.js",
|
|
42
|
+
"default": "./dist/index.js"
|
|
42
43
|
},
|
|
43
44
|
"./server": {
|
|
44
45
|
"types": "./dist/server/index.d.ts",
|
|
45
|
-
"import": "./dist/server/index.js"
|
|
46
|
+
"import": "./dist/server/index.js",
|
|
47
|
+
"default": "./dist/server/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./audit-storage": {
|
|
50
|
+
"types": "./dist/server/audit-storage.d.ts",
|
|
51
|
+
"import": "./dist/server/audit-storage.js",
|
|
52
|
+
"default": "./dist/server/audit-storage.js"
|
|
46
53
|
},
|
|
47
54
|
"./react": {
|
|
48
55
|
"types": "./dist/react/index.d.ts",
|
|
49
|
-
"import": "./dist/react/index.js"
|
|
56
|
+
"import": "./dist/react/index.js",
|
|
57
|
+
"default": "./dist/react/index.js"
|
|
50
58
|
}
|
|
51
59
|
},
|
|
52
60
|
"files": [
|