@revealui/auth 0.4.10 → 0.5.1
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/useSignIn.d.ts +1 -1
- package/dist/react/useSignIn.d.ts.map +1 -1
- package/dist/react/useSignIn.js +7 -3
- package/dist/server/audit-storage.d.ts +122 -0
- package/dist/server/audit-storage.d.ts.map +1 -0
- package/dist/server/audit-storage.js +298 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +68 -10
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -0
- package/dist/server/platform-roles.d.ts +42 -0
- package/dist/server/platform-roles.d.ts.map +1 -0
- package/dist/server/platform-roles.js +65 -0
- package/dist/server/session.js +1 -1
- package/dist/server/sso/__tests__/helpers/mock-oidc-idp.d.ts +31 -0
- package/dist/server/sso/__tests__/helpers/mock-oidc-idp.d.ts.map +1 -0
- package/dist/server/sso/__tests__/helpers/mock-oidc-idp.js +115 -0
- package/dist/server/sso/__tests__/helpers/mock-saml-idp.d.ts +28 -0
- package/dist/server/sso/__tests__/helpers/mock-saml-idp.d.ts.map +1 -0
- package/dist/server/sso/__tests__/helpers/mock-saml-idp.js +150 -0
- package/dist/server/sso/index.d.ts +13 -0
- package/dist/server/sso/index.d.ts.map +1 -0
- package/dist/server/sso/index.js +12 -0
- package/dist/server/sso/jit.d.ts +39 -0
- package/dist/server/sso/jit.d.ts.map +1 -0
- package/dist/server/sso/jit.js +141 -0
- package/dist/server/sso/oidc.d.ts +137 -0
- package/dist/server/sso/oidc.d.ts.map +1 -0
- package/dist/server/sso/oidc.js +345 -0
- package/dist/server/sso/roles.d.ts +48 -0
- package/dist/server/sso/roles.d.ts.map +1 -0
- package/dist/server/sso/roles.js +109 -0
- package/dist/server/sso/saml.d.ts +99 -0
- package/dist/server/sso/saml.d.ts.map +1 -0
- package/dist/server/sso/saml.js +392 -0
- package/dist/server/sso/state.d.ts +46 -0
- package/dist/server/sso/state.d.ts.map +1 -0
- package/dist/server/sso/state.js +101 -0
- package/dist/utils/database.d.ts +1 -1
- package/dist/utils/database.d.ts.map +1 -1
- package/package.json +15 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignIn.d.ts","sourceRoot":"","sources":["../../src/react/useSignIn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAuCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"useSignIn.d.ts","sourceRoot":"","sources":["../../src/react/useSignIn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAuCxC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CACnC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,GAC7B;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAGhD;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,WAAW,EAAE,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAC5D,CAAC;IACF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,IAAI,eAAe,CA+E3C"}
|
package/dist/react/useSignIn.js
CHANGED
|
@@ -94,13 +94,17 @@ export function useSignIn() {
|
|
|
94
94
|
code: errorData.code,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
-
//
|
|
97
|
+
// MFA challenge is HTTP 200 with { requiresMfa: true } and no `user`.
|
|
98
|
+
// The server may omit mfaUserId from the body (identity lives in the
|
|
99
|
+
// httpOnly mfa-pending cookie). Requiring mfaUserId here forced the
|
|
100
|
+
// success schema parse, which threw "expected object, received undefined"
|
|
101
|
+
// on `user` and locked MFA-enabled accounts out of the login UI.
|
|
98
102
|
const jsonObj = json;
|
|
99
|
-
if (jsonObj.requiresMfa === true
|
|
103
|
+
if (jsonObj.requiresMfa === true) {
|
|
100
104
|
return {
|
|
101
105
|
success: false,
|
|
102
106
|
requiresMfa: true,
|
|
103
|
-
mfaUserId: jsonObj.mfaUserId,
|
|
107
|
+
...(typeof jsonObj.mfaUserId === 'string' ? { mfaUserId: jsonObj.mfaUserId } : {}),
|
|
104
108
|
};
|
|
105
109
|
}
|
|
106
110
|
const successData = SignInSuccessResponseSchema.parse(json);
|
|
@@ -0,0 +1,122 @@
|
|
|
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, DrizzleAuditStoreOptions, 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
|
+
* @param options Optional store options (GAP-417 residual): pass `targetEnv`
|
|
50
|
+
* from CLI writers that resolve a deployment target independent of NODE_ENV.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createAuditStore(db: Database, options?: DrizzleAuditStoreOptions): DrizzleAuditStoreType;
|
|
53
|
+
/** Test-only reset of the cached signer (re-reads env on next `getAuditRowSigner`). */
|
|
54
|
+
export declare function __resetAuditSignerForTest(): void;
|
|
55
|
+
/** DB-side severity vocabulary — matches the `audit_log_severity_check` constraint. */
|
|
56
|
+
type DbSeverity = 'info' | 'warn' | 'critical';
|
|
57
|
+
export declare function mapSeverityToDb(severity: AuditSeverity): DbSeverity;
|
|
58
|
+
/**
|
|
59
|
+
* `AuditStorage` implementation backed by `DrizzleAuditStore`. Re-homes the
|
|
60
|
+
* row-storing responsibility onto `DrizzleAuditStore.append()` and keeps the
|
|
61
|
+
* security-model boundary mapping (severity + columns) here, so `@revealui/db`
|
|
62
|
+
* stays free of a dependency on the security package.
|
|
63
|
+
*/
|
|
64
|
+
export declare class DrizzleBackedAuditStorage implements AuditStorage {
|
|
65
|
+
private readonly store;
|
|
66
|
+
constructor(db: Database);
|
|
67
|
+
write(event: AuditEvent): Promise<void>;
|
|
68
|
+
query(query: AuditQuery): Promise<AuditEvent[]>;
|
|
69
|
+
count(query: AuditQuery): Promise<number>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Synchronous env-parity assertion for the audit write path — GAP-355 Stage 1
|
|
73
|
+
* closure, owner-ruled 2026-07-17.
|
|
74
|
+
*
|
|
75
|
+
* A serving process installs audit storage but cannot always run the async
|
|
76
|
+
* round-trip self-test (serverless has no clean "refuse to serve" for a
|
|
77
|
+
* boot-time DB round trip). This asserts the audit path's ENV preconditions
|
|
78
|
+
* SYNCHRONOUSLY at the install point, so a deploy whose audit-critical env has
|
|
79
|
+
* diverged (a required var missing or empty) FAILS THE DEPLOY rather than
|
|
80
|
+
* serving with an audit sink that silently drops every row.
|
|
81
|
+
*
|
|
82
|
+
* Scope, honestly: this catches env-var ABSENCE/emptiness synchronously — which
|
|
83
|
+
* `installAuditStorage()` itself does not, because `getClient()` is a lazy pool
|
|
84
|
+
* factory, so a missing connection URL would otherwise surface only on the
|
|
85
|
+
* first write, at request time. It does NOT, and on serverless cannot, catch
|
|
86
|
+
* migration-state divergence (the `audit_log` table missing or misshapen on the
|
|
87
|
+
* target DB); that needs the write-read round trip of `auditStorageSelfTest`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function assertAuditStorageEnv(env?: NodeJS.ProcessEnv): void;
|
|
90
|
+
/**
|
|
91
|
+
* Swap the process-wide `audit` system onto persistent Postgres storage.
|
|
92
|
+
* Synchronous and side-effect-free at call time: `getClient()` is a lazy pool
|
|
93
|
+
* factory (no connection opened here), so this is safe to run on a serverless
|
|
94
|
+
* cold-start path. WITHOUT this call, audit events fall into the default
|
|
95
|
+
* `InMemoryAuditStorage` and evaporate on restart — which is exactly what
|
|
96
|
+
* happened to every admin-process emit before GAP-338.
|
|
97
|
+
*
|
|
98
|
+
* Call `assertAuditStorageEnv()` before this at each install site so a
|
|
99
|
+
* diverged-env deploy fails loudly instead of installing a store that can
|
|
100
|
+
* never write.
|
|
101
|
+
*/
|
|
102
|
+
export declare function installAuditStorage(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Boot-time round-trip self-test. Writes a synthetic audit event through the
|
|
105
|
+
* REAL installed path (AuditSystem → boundary → DrizzleAuditStore → audit_log)
|
|
106
|
+
* and reads it back. Throws if the round trip fails, so a runtime that cannot
|
|
107
|
+
* record agent actions REFUSES TO SERVE rather than silently dropping them
|
|
108
|
+
* (fail-closed integrity — ADR §2a).
|
|
109
|
+
*
|
|
110
|
+
* Uses `severity: 'low'` deliberately: that value would be rejected by the DB
|
|
111
|
+
* CHECK constraint if the boundary mapping regressed, so this exercises the
|
|
112
|
+
* exact defect Stage 1 fixes. A healthy audit path lets startup proceed.
|
|
113
|
+
*
|
|
114
|
+
* Runs on the long-running worker (once per deploy) and the dev boot path,
|
|
115
|
+
* where an async boot chain already exists and `process.exit(1)` gives
|
|
116
|
+
* "refuse to serve" clean semantics. It is NOT run on serverless cold-start
|
|
117
|
+
* paths, which stay free of per-invocation DB round-trips (storage is still
|
|
118
|
+
* installed there synchronously by `installAuditStorage()`).
|
|
119
|
+
*/
|
|
120
|
+
export declare function auditStorageSelfTest(auditSystem?: AuditSystem): Promise<void>;
|
|
121
|
+
export {};
|
|
122
|
+
//# 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,EACV,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,IAAI,qBAAqB,EAC3C,MAAM,cAAc,CAAC;AAEtB,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;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,OAAO,CAAC,EAAE,wBAAwB,GACjC,qBAAqB,CAEvB;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,298 @@
|
|
|
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
|
+
* @param options Optional store options (GAP-417 residual): pass `targetEnv`
|
|
68
|
+
* from CLI writers that resolve a deployment target independent of NODE_ENV.
|
|
69
|
+
*/
|
|
70
|
+
export function createAuditStore(db, options) {
|
|
71
|
+
return new DrizzleAuditStore(db, getAuditRowSigner(), options);
|
|
72
|
+
}
|
|
73
|
+
/** Test-only reset of the cached signer (re-reads env on next `getAuditRowSigner`). */
|
|
74
|
+
export function __resetAuditSignerForTest() {
|
|
75
|
+
cachedSigner = undefined;
|
|
76
|
+
resolved = false;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Severity mapping AT THE BOUNDARY (ADR §5). Total over the security
|
|
80
|
+
* vocabulary; every value lands in the DB CHECK vocabulary. Declared as a
|
|
81
|
+
* `Record<AuditSeverity, ...>` so the compiler rejects any future severity
|
|
82
|
+
* that is not mapped.
|
|
83
|
+
*/
|
|
84
|
+
const SEVERITY_TO_DB = {
|
|
85
|
+
low: 'info',
|
|
86
|
+
medium: 'warn',
|
|
87
|
+
high: 'critical',
|
|
88
|
+
critical: 'critical',
|
|
89
|
+
};
|
|
90
|
+
export function mapSeverityToDb(severity) {
|
|
91
|
+
return SEVERITY_TO_DB[severity];
|
|
92
|
+
}
|
|
93
|
+
/** Reverse map for reconstructing legacy rows written without a full payload. */
|
|
94
|
+
const DB_TO_SEVERITY = {
|
|
95
|
+
info: 'low',
|
|
96
|
+
warn: 'medium',
|
|
97
|
+
critical: 'critical',
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* `AuditStorage` implementation backed by `DrizzleAuditStore`. Re-homes the
|
|
101
|
+
* row-storing responsibility onto `DrizzleAuditStore.append()` and keeps the
|
|
102
|
+
* security-model boundary mapping (severity + columns) here, so `@revealui/db`
|
|
103
|
+
* stays free of a dependency on the security package.
|
|
104
|
+
*/
|
|
105
|
+
export class DrizzleBackedAuditStorage {
|
|
106
|
+
store;
|
|
107
|
+
constructor(db) {
|
|
108
|
+
// createAuditStore injects the process-wide Ed25519 signer (GAP-355 Stage 3),
|
|
109
|
+
// so rows land signed on a signing deployment and NULL-signature in unsigned
|
|
110
|
+
// (dev/test) mode. The severity/column boundary mapping stays here.
|
|
111
|
+
this.store = createAuditStore(db);
|
|
112
|
+
}
|
|
113
|
+
async write(event) {
|
|
114
|
+
try {
|
|
115
|
+
// The injected signer (createAuditStore) signs the row at the door on a
|
|
116
|
+
// signing deployment; a signer failure makes append THROW (fail-closed),
|
|
117
|
+
// caught below and routed through the write-result rails.
|
|
118
|
+
await this.store.append({
|
|
119
|
+
id: event.id,
|
|
120
|
+
timestamp: new Date(event.timestamp),
|
|
121
|
+
eventType: event.type,
|
|
122
|
+
severity: mapSeverityToDb(event.severity),
|
|
123
|
+
agentId: event.actor.id,
|
|
124
|
+
payload: event,
|
|
125
|
+
policyViolations: [],
|
|
126
|
+
});
|
|
127
|
+
recordAuditWriteResult({ ok: true, eventId: event.id, eventType: event.type });
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
recordAuditWriteResult({
|
|
131
|
+
ok: false,
|
|
132
|
+
reason: classifyAuditWriteFailure(err),
|
|
133
|
+
eventId: event.id,
|
|
134
|
+
eventType: event.type,
|
|
135
|
+
});
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async query(query) {
|
|
140
|
+
const dbSeverities = query.severity && query.severity.length > 0
|
|
141
|
+
? [...new Set(query.severity.map(mapSeverityToDb))]
|
|
142
|
+
: undefined;
|
|
143
|
+
const entries = await this.store.query({
|
|
144
|
+
agentId: query.actorId,
|
|
145
|
+
eventTypes: query.types ? [...query.types] : undefined,
|
|
146
|
+
severity: dbSeverities,
|
|
147
|
+
startTime: query.startDate,
|
|
148
|
+
endTime: query.endDate,
|
|
149
|
+
limit: query.limit ?? 100,
|
|
150
|
+
offset: query.offset ?? 0,
|
|
151
|
+
});
|
|
152
|
+
return entries
|
|
153
|
+
.map((entry) => reconstructEvent(entry))
|
|
154
|
+
.filter((event) => {
|
|
155
|
+
if (query.resourceType && event.resource?.type !== query.resourceType)
|
|
156
|
+
return false;
|
|
157
|
+
if (query.resourceId && event.resource?.id !== query.resourceId)
|
|
158
|
+
return false;
|
|
159
|
+
if (query.result && query.result.length > 0 && !query.result.includes(event.result)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async count(query) {
|
|
166
|
+
// Count ignores pagination (matches InMemoryAuditStorage semantics). Query
|
|
167
|
+
// with an unbounded limit and count the post-filtered results.
|
|
168
|
+
const events = await this.query({ ...query, limit: Number.MAX_SAFE_INTEGER, offset: 0 });
|
|
169
|
+
return events.length;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Reconstruct the full `AuditEvent` from a stored row. The complete event is
|
|
174
|
+
* persisted in the `payload` column, so it round-trips exactly. Rows written
|
|
175
|
+
* before this adapter (or by another writer without a full-event payload) fall
|
|
176
|
+
* back to a column-derived event.
|
|
177
|
+
*/
|
|
178
|
+
function reconstructEvent(entry) {
|
|
179
|
+
const stored = entry.payload;
|
|
180
|
+
if (stored && typeof stored === 'object' && 'type' in stored) {
|
|
181
|
+
return stored;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
id: entry.id,
|
|
185
|
+
timestamp: entry.timestamp.toISOString(),
|
|
186
|
+
type: entry.eventType,
|
|
187
|
+
severity: DB_TO_SEVERITY[entry.severity] ?? 'low',
|
|
188
|
+
actor: { id: entry.agentId, type: 'system' },
|
|
189
|
+
action: entry.eventType,
|
|
190
|
+
result: 'success',
|
|
191
|
+
metadata: stored ?? undefined,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Synchronous env-parity assertion for the audit write path — GAP-355 Stage 1
|
|
196
|
+
* closure, owner-ruled 2026-07-17.
|
|
197
|
+
*
|
|
198
|
+
* A serving process installs audit storage but cannot always run the async
|
|
199
|
+
* round-trip self-test (serverless has no clean "refuse to serve" for a
|
|
200
|
+
* boot-time DB round trip). This asserts the audit path's ENV preconditions
|
|
201
|
+
* SYNCHRONOUSLY at the install point, so a deploy whose audit-critical env has
|
|
202
|
+
* diverged (a required var missing or empty) FAILS THE DEPLOY rather than
|
|
203
|
+
* serving with an audit sink that silently drops every row.
|
|
204
|
+
*
|
|
205
|
+
* Scope, honestly: this catches env-var ABSENCE/emptiness synchronously — which
|
|
206
|
+
* `installAuditStorage()` itself does not, because `getClient()` is a lazy pool
|
|
207
|
+
* factory, so a missing connection URL would otherwise surface only on the
|
|
208
|
+
* first write, at request time. It does NOT, and on serverless cannot, catch
|
|
209
|
+
* migration-state divergence (the `audit_log` table missing or misshapen on the
|
|
210
|
+
* target DB); that needs the write-read round trip of `auditStorageSelfTest`.
|
|
211
|
+
*/
|
|
212
|
+
export function assertAuditStorageEnv(env = process.env) {
|
|
213
|
+
// GAP-417 item 5: the predicate is OWNED by @revealui/db and matches
|
|
214
|
+
// getClient()'s resolution exactly (config url, then POSTGRES_URL /
|
|
215
|
+
// DATABASE_URL). The previous local triple also accepted DATABASE_HOST,
|
|
216
|
+
// which getClient() never consults — so the assert passed, the install then
|
|
217
|
+
// threw, and production silently kept the in-memory sink (proven in the
|
|
218
|
+
// #2161 re-review). Never re-inline this check.
|
|
219
|
+
if (!hasDatabaseConnectionEnv(env)) {
|
|
220
|
+
throw new Error('AUDIT STORAGE ENV PARITY FAILED: no usable database connection is configured ' +
|
|
221
|
+
'(set POSTGRES_URL or DATABASE_URL, or provide @revealui/config database.url), ' +
|
|
222
|
+
'so the audit write path cannot persist rows. Refusing to serve — an agent ' +
|
|
223
|
+
'action that cannot be recorded must not execute (fail-closed integrity, ' +
|
|
224
|
+
'docs/decisions/2026-07-12-audit-receipt-architecture.md §2a).');
|
|
225
|
+
}
|
|
226
|
+
// Signing key (GAP-355 Stage 3): a production signing deployment must have the
|
|
227
|
+
// Ed25519 key so rows land signed. Absence is a diverged-env deploy — fail it
|
|
228
|
+
// synchronously here (the serverless serving process's refuse-to-serve), the
|
|
229
|
+
// same rail the DB-connection check above uses. Full Ed25519 PKCS#8 parsing is
|
|
230
|
+
// done by validate-startup; this is the audit-owned presence parity check, so
|
|
231
|
+
// the contract cannot silently drift on one serving process. Dev/test
|
|
232
|
+
// (NODE_ENV !== 'production') runs unsigned by design and is exempt.
|
|
233
|
+
//
|
|
234
|
+
// GAP-417 items 1-2 (owner-countersigned 2026-07-25): SKIP_ENV_VALIDATION no
|
|
235
|
+
// longer exempts this check. The audit path has NO escape hatch — a
|
|
236
|
+
// production process that cannot sign must not boot, because unsigned rows
|
|
237
|
+
// are indistinguishable from tampering AND permanently stall anchor
|
|
238
|
+
// contiguity once the sweep filters them. SKIP still covers non-audit env
|
|
239
|
+
// validation elsewhere; it never buys an unsigned production audit log.
|
|
240
|
+
if (env.NODE_ENV === 'production') {
|
|
241
|
+
if (!env.REVEALUI_AUDIT_SIGNING_KEY) {
|
|
242
|
+
throw new Error('AUDIT STORAGE ENV PARITY FAILED: REVEALUI_AUDIT_SIGNING_KEY is not set on a ' +
|
|
243
|
+
'production deployment, so the audit write path cannot sign rows. Refusing to ' +
|
|
244
|
+
'serve — an unsigned row in the post-Stage-3 era is indistinguishable from ' +
|
|
245
|
+
'tampering (docs/decisions/2026-07-12-audit-receipt-architecture.md §2a). ' +
|
|
246
|
+
'SKIP_ENV_VALIDATION does not exempt the audit path (GAP-417).');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Swap the process-wide `audit` system onto persistent Postgres storage.
|
|
252
|
+
* Synchronous and side-effect-free at call time: `getClient()` is a lazy pool
|
|
253
|
+
* factory (no connection opened here), so this is safe to run on a serverless
|
|
254
|
+
* cold-start path. WITHOUT this call, audit events fall into the default
|
|
255
|
+
* `InMemoryAuditStorage` and evaporate on restart — which is exactly what
|
|
256
|
+
* happened to every admin-process emit before GAP-338.
|
|
257
|
+
*
|
|
258
|
+
* Call `assertAuditStorageEnv()` before this at each install site so a
|
|
259
|
+
* diverged-env deploy fails loudly instead of installing a store that can
|
|
260
|
+
* never write.
|
|
261
|
+
*/
|
|
262
|
+
export function installAuditStorage() {
|
|
263
|
+
audit.setStorage(new DrizzleBackedAuditStorage(getClient()));
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Boot-time round-trip self-test. Writes a synthetic audit event through the
|
|
267
|
+
* REAL installed path (AuditSystem → boundary → DrizzleAuditStore → audit_log)
|
|
268
|
+
* and reads it back. Throws if the round trip fails, so a runtime that cannot
|
|
269
|
+
* record agent actions REFUSES TO SERVE rather than silently dropping them
|
|
270
|
+
* (fail-closed integrity — ADR §2a).
|
|
271
|
+
*
|
|
272
|
+
* Uses `severity: 'low'` deliberately: that value would be rejected by the DB
|
|
273
|
+
* CHECK constraint if the boundary mapping regressed, so this exercises the
|
|
274
|
+
* exact defect Stage 1 fixes. A healthy audit path lets startup proceed.
|
|
275
|
+
*
|
|
276
|
+
* Runs on the long-running worker (once per deploy) and the dev boot path,
|
|
277
|
+
* where an async boot chain already exists and `process.exit(1)` gives
|
|
278
|
+
* "refuse to serve" clean semantics. It is NOT run on serverless cold-start
|
|
279
|
+
* paths, which stay free of per-invocation DB round-trips (storage is still
|
|
280
|
+
* installed there synchronously by `installAuditStorage()`).
|
|
281
|
+
*/
|
|
282
|
+
export async function auditStorageSelfTest(auditSystem = audit) {
|
|
283
|
+
const marker = `__audit-self-test__:${randomUUID()}`;
|
|
284
|
+
const written = await auditSystem.log({
|
|
285
|
+
type: 'security.audit_self_test',
|
|
286
|
+
severity: 'low',
|
|
287
|
+
actor: { id: marker, type: 'system' },
|
|
288
|
+
action: 'audit-storage-self-test',
|
|
289
|
+
result: 'success',
|
|
290
|
+
metadata: { synthetic: true },
|
|
291
|
+
});
|
|
292
|
+
const found = await auditSystem.query({ actorId: marker, limit: 5 });
|
|
293
|
+
if (!found.some((event) => event.id === written.id)) {
|
|
294
|
+
throw new Error('AUDIT STORAGE SELF-TEST FAILED: wrote a synthetic audit event but could not read it ' +
|
|
295
|
+
'back. Refusing to serve — a runtime that cannot record agent actions must not accept ' +
|
|
296
|
+
'traffic (fail-closed integrity, docs/decisions/2026-07-12-audit-receipt-architecture.md §2a).');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -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;
|
|
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;AAWpE;;;;;;;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,CA0PvB;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,CAyOvB"}
|
package/dist/server/auth.js
CHANGED
|
@@ -12,6 +12,7 @@ import { and, eq, isNull } from 'drizzle-orm';
|
|
|
12
12
|
import { auditLoginFailure, auditLoginSuccess } from './audit-bridge.js';
|
|
13
13
|
import { clearFailedAttempts, isAccountLocked, recordFailedAttempt } from './brute-force.js';
|
|
14
14
|
import { validatePasswordStrength } from './password-validation.js';
|
|
15
|
+
import { ensureAccountOwnerPlatformAdmin } from './platform-roles.js';
|
|
15
16
|
import { checkRateLimit } from './rate-limit.js';
|
|
16
17
|
import { createSession, rotateSession } from './session.js';
|
|
17
18
|
/** Grace period after signup during which unverified users can still sign in (24 hours) */
|
|
@@ -26,9 +27,27 @@ const EMAIL_VERIFICATION_GRACE_PERIOD_MS = 24 * 60 * 60 * 1000;
|
|
|
26
27
|
*/
|
|
27
28
|
export async function signIn(email, password, options) {
|
|
28
29
|
try {
|
|
29
|
-
// Rate limiting by IP address
|
|
30
|
+
// Rate limiting by IP address. The storage backend (DatabaseStorage in
|
|
31
|
+
// production) can throw for reasons unrelated to the credential check
|
|
32
|
+
// itself (pool exhaustion, a transaction-pooling quirk, a storage
|
|
33
|
+
// misconfiguration) — guard it like every other DB-touching step below so
|
|
34
|
+
// a throw here fails closed with a diagnosable reason instead of falling
|
|
35
|
+
// through to the generic outer catch.
|
|
30
36
|
const ipKey = options?.ipAddress || 'unknown';
|
|
31
|
-
|
|
37
|
+
let rateLimit;
|
|
38
|
+
try {
|
|
39
|
+
rateLimit = await checkRateLimit(`signin:${ipKey}`);
|
|
40
|
+
}
|
|
41
|
+
catch (rateLimitError) {
|
|
42
|
+
logger.error('Error checking rate limit', rateLimitError instanceof Error ? rateLimitError : undefined, {
|
|
43
|
+
message: rateLimitError instanceof Error ? rateLimitError.message : String(rateLimitError),
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
reason: 'database_error',
|
|
48
|
+
error: 'Database error',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
32
51
|
if (!rateLimit.allowed) {
|
|
33
52
|
return {
|
|
34
53
|
success: false,
|
|
@@ -36,8 +55,23 @@ export async function signIn(email, password, options) {
|
|
|
36
55
|
error: 'Too many login attempts. Please try again later.',
|
|
37
56
|
};
|
|
38
57
|
}
|
|
39
|
-
// Brute force protection by email
|
|
40
|
-
|
|
58
|
+
// Brute force protection by email. Same rationale as the rate-limit
|
|
59
|
+
// guard above — the storage backend can throw independently of whether
|
|
60
|
+
// the account is actually locked.
|
|
61
|
+
let bruteForceCheck;
|
|
62
|
+
try {
|
|
63
|
+
bruteForceCheck = await isAccountLocked(email);
|
|
64
|
+
}
|
|
65
|
+
catch (lockCheckError) {
|
|
66
|
+
logger.error('Error checking account lock status', lockCheckError instanceof Error ? lockCheckError : undefined, {
|
|
67
|
+
message: lockCheckError instanceof Error ? lockCheckError.message : String(lockCheckError),
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
reason: 'database_error',
|
|
72
|
+
error: 'Database error',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
41
75
|
if (bruteForceCheck.locked) {
|
|
42
76
|
const lockMinutes = bruteForceCheck.lockUntil
|
|
43
77
|
? Math.ceil((bruteForceCheck.lockUntil - Date.now()) / (60 * 1000))
|
|
@@ -118,11 +152,27 @@ export async function signIn(email, password, options) {
|
|
|
118
152
|
if (!isValid) {
|
|
119
153
|
return failInvalidCredentials();
|
|
120
154
|
}
|
|
121
|
-
// Successful login - clear failed attempts
|
|
122
|
-
|
|
123
|
-
//
|
|
155
|
+
// Successful login - clear failed attempts. Best-effort: this is
|
|
156
|
+
// bookkeeping on an already-successful credential check, not a gate, so a
|
|
157
|
+
// storage hiccup here must not fail an otherwise-valid login (it can only
|
|
158
|
+
// make a future lockout trigger slightly sooner, never bypass one).
|
|
159
|
+
try {
|
|
160
|
+
await clearFailedAttempts(email);
|
|
161
|
+
}
|
|
162
|
+
catch (clearAttemptsError) {
|
|
163
|
+
logger.error('Error clearing failed attempts after successful login', clearAttemptsError instanceof Error ? clearAttemptsError : undefined, {
|
|
164
|
+
message: clearAttemptsError instanceof Error
|
|
165
|
+
? clearAttemptsError.message
|
|
166
|
+
: String(clearAttemptsError),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// Check email verification (with grace period for new accounts).
|
|
170
|
+
// Coerce createdAt: the Postgres storage path returns timestamps as strings
|
|
171
|
+
// (not Date objects), so calling .getTime() directly threw a TypeError that
|
|
172
|
+
// fell through to the outer catch and turned a valid login into a generic
|
|
173
|
+
// "unexpected error" on real deploys. new Date() accepts a Date or a string.
|
|
124
174
|
if (!user.emailVerified) {
|
|
125
|
-
const accountAge = Date.now() - user.createdAt.getTime();
|
|
175
|
+
const accountAge = Date.now() - new Date(user.createdAt).getTime();
|
|
126
176
|
if (accountAge > EMAIL_VERIFICATION_GRACE_PERIOD_MS) {
|
|
127
177
|
return {
|
|
128
178
|
success: false,
|
|
@@ -177,8 +227,12 @@ export async function signIn(email, password, options) {
|
|
|
177
227
|
sessionToken: token,
|
|
178
228
|
};
|
|
179
229
|
}
|
|
180
|
-
catch {
|
|
181
|
-
logger.error('Unexpected error in signIn'
|
|
230
|
+
catch (err) {
|
|
231
|
+
logger.error('Unexpected error in signIn', err instanceof Error ? err : undefined, {
|
|
232
|
+
message: err instanceof Error ? err.message : String(err),
|
|
233
|
+
name: err instanceof Error ? err.name : 'unknown',
|
|
234
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
235
|
+
});
|
|
182
236
|
return {
|
|
183
237
|
success: false,
|
|
184
238
|
reason: 'unexpected_error',
|
|
@@ -422,6 +476,10 @@ export async function signUp(email, password, name, options) {
|
|
|
422
476
|
createdAt: now,
|
|
423
477
|
updatedAt: now,
|
|
424
478
|
});
|
|
479
|
+
// First membership owner of a hosted account gets platform shell
|
|
480
|
+
// admin (users.role), not super-admin. Unlocks /settings + API keys
|
|
481
|
+
// without granting fleet control-plane power.
|
|
482
|
+
await ensureAccountOwnerPlatformAdmin(db, user.id);
|
|
425
483
|
}
|
|
426
484
|
catch {
|
|
427
485
|
logger.error('Failed to provision personal account at signup (billing webhook will backfill)', undefined, { userId: user.id });
|
package/dist/server/index.d.ts
CHANGED
|
@@ -21,10 +21,12 @@ export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenti
|
|
|
21
21
|
export type { ChangePasswordResult, PasswordResetResult, PasswordResetToken, } from './password-reset.js';
|
|
22
22
|
export { changePassword, generatePasswordResetToken, invalidatePasswordResetToken, resetPasswordWithToken, validatePasswordResetToken, } from './password-reset.js';
|
|
23
23
|
export { meetsMinimumPasswordRequirements, validatePasswordStrength, } from './password-validation.js';
|
|
24
|
+
export { ensureAccountOwnerPlatformAdmin, isPlatformShellAdminRole, PLATFORM_SHELL_ADMIN_COOKIE_ROLES, PLATFORM_SHELL_ADMIN_ROLES, platformRoleForAccountOwner, } from './platform-roles.js';
|
|
24
25
|
export { checkRateLimit, configureRateLimit, getRateLimitStatus, resetRateLimit, resetRateLimitConfig, } from './rate-limit.js';
|
|
25
26
|
export type { RequestContext, SessionBindingConfig, SessionData } from './session.js';
|
|
26
27
|
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteOtherUserSessions, deleteSession, getSession, isRecoverySession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
27
28
|
export { signCookiePayload, verifyCookiePayload } from './signed-cookie.js';
|
|
29
|
+
export { type BuildOidcAuthorizationUrlInput, type BuildSamlAuthorizeUrlFailureReason, type BuildSamlAuthorizeUrlResult, buildOidcAuthorizationUrl, buildSamlAuthorizeUrl, buildSamlSpMetadata, createOidcRemoteJwkSet, type ExchangeOidcCodeFailureReason, type ExchangeOidcCodeInput, type ExchangeOidcCodeResult, type ExchangeOidcCodeSuccess, exchangeOidcCode, extractGroupsFromClaim, type FetchOidcDiscoveryOptions, type FetchOidcDiscoveryResult, fetchIdpMetadata, fetchOidcDiscovery, type GenerateSsoStateInput, type GenerateSsoStateResult, generateSsoState, type JWTPayload, type JWTVerifyGetKey, type KeyLike, type MapSsoGroupsFailureReason, type MapSsoGroupsInput, type MapSsoGroupsResult, mapSsoGroupsToRole, normalizeIdpCertPem, normalizeSsoUserRole, type OidcDiscoveryDocument, type OidcDiscoveryFailureReason, type ParseIdpMetadataFailureReason, type ParseIdpMetadataResult, parseIdpMetadataXml, type SamlSpConfig, type SsoStatePayload, type UpsertSsoUserInput, upsertSsoUser, type ValidatedIdTokenClaims, type ValidatedSamlAssertion, type ValidateIdTokenFailureReason, type ValidateIdTokenResult, type ValidateOidcIdTokenOptions, type ValidateSamlResponseFailureReason, type ValidateSamlResponseResult, type VerifiedSsoState, validateOidcIdToken, validateSamlPostResponse, verifySsoState, } from './sso/index.js';
|
|
28
30
|
export type { Storage } from './storage/index.js';
|
|
29
31
|
export { createStorage, DatabaseStorage, getStorage, InMemoryStorage, resetStorage, } from './storage/index.js';
|
|
30
32
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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":"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"}
|
package/dist/server/index.js
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
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';
|
|
@@ -16,8 +22,11 @@ export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getL
|
|
|
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';
|
|
18
24
|
export { meetsMinimumPasswordRequirements, validatePasswordStrength, } from './password-validation.js';
|
|
25
|
+
export { ensureAccountOwnerPlatformAdmin, isPlatformShellAdminRole, PLATFORM_SHELL_ADMIN_COOKIE_ROLES, PLATFORM_SHELL_ADMIN_ROLES, platformRoleForAccountOwner, } from './platform-roles.js';
|
|
19
26
|
export { checkRateLimit, configureRateLimit, getRateLimitStatus, resetRateLimit, resetRateLimitConfig, } from './rate-limit.js';
|
|
20
27
|
export { configureSessionBinding, createSession, deleteAllUserSessions, deleteOtherUserSessions, deleteSession, getSession, isRecoverySession, resetSessionBindingConfig, rotateSession, validateSessionBinding, } from './session.js';
|
|
21
28
|
// Signed Cookie
|
|
22
29
|
export { signCookiePayload, verifyCookiePayload } from './signed-cookie.js';
|
|
30
|
+
// Enterprise SSO pure layer (GAP-464) — OIDC + SAML SP helpers
|
|
31
|
+
export { buildOidcAuthorizationUrl, buildSamlAuthorizeUrl, buildSamlSpMetadata, createOidcRemoteJwkSet, exchangeOidcCode, extractGroupsFromClaim, fetchIdpMetadata, fetchOidcDiscovery, generateSsoState, mapSsoGroupsToRole, normalizeIdpCertPem, normalizeSsoUserRole, parseIdpMetadataXml, upsertSsoUser, validateOidcIdToken, validateSamlPostResponse, verifySsoState, } from './sso/index.js';
|
|
23
32
|
export { createStorage, DatabaseStorage, getStorage, InMemoryStorage, resetStorage, } from './storage/index.js';
|