@nacre.work/core 0.18.0 → 0.20.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/extensions.d.ts +110 -1
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +43 -1
- package/dist/extensions.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/0031_webauthn.sql +123 -0
- package/dist/migrations/0032_shared_accounts.sql +56 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webauthn.d.ts +139 -0
- package/dist/webauthn.d.ts.map +1 -0
- package/dist/webauthn.js +410 -0
- package/dist/webauthn.js.map +1 -0
- package/package.json +1 -1
package/dist/extensions.d.ts
CHANGED
|
@@ -9,7 +9,10 @@ import type { AccessPlan, ResolveInput } from './authz/resolve.js';
|
|
|
9
9
|
* there was a module to write. The fifth, `registerIngestGate`, is the other
|
|
10
10
|
* direction: a point the core declares for a capability the schema always had
|
|
11
11
|
* and nothing enforced (`max_documents`), designed so the open half is complete
|
|
12
|
-
* and correct with no gate registered.
|
|
12
|
+
* and correct with no gate registered. The sixth, `registerSignInGate`, is the
|
|
13
|
+
* same direction again: both kinds of second factor are individually opt-in and
|
|
14
|
+
* an organization had no way to *require* one, so the point is declared here
|
|
15
|
+
* and the policy that uses it is a module.
|
|
13
16
|
*
|
|
14
17
|
* `nacre-enterprise` has named these since before there was a module to write —
|
|
15
18
|
* `registerAuthProvider`, `registerAuthzResolver`, `registerAuditSink`,
|
|
@@ -204,10 +207,91 @@ export interface IngestGate {
|
|
|
204
207
|
readonly name: string;
|
|
205
208
|
admit(context: IngestContext): Promise<IngestVerdict>;
|
|
206
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* What is known about a session at the moment one would be minted.
|
|
212
|
+
*
|
|
213
|
+
* The principal is always a **user**, because this is the only path that mints
|
|
214
|
+
* a Nacre session and only a person walks it. A service account presents its
|
|
215
|
+
* `nacre_sk_` key on every request and a delegation carries a token issued at
|
|
216
|
+
* consent; neither reaches here, which is why there is no principal type on
|
|
217
|
+
* this object to branch on.
|
|
218
|
+
*
|
|
219
|
+
* `secondFactor` is what was *proved on this sign-in* and `enrolled` is what
|
|
220
|
+
* the account *holds*, and they are two facts rather than one. A refresh proves
|
|
221
|
+
* no factor and the account still has one; an account with a factor that was
|
|
222
|
+
* never used is the case a policy is being turned on for. Collapsing them would
|
|
223
|
+
* make a renewal indistinguishable from a password-only sign-in.
|
|
224
|
+
*/
|
|
225
|
+
export interface SignInContext {
|
|
226
|
+
/** From the row that authenticated, never from the request. Invariant 1. */
|
|
227
|
+
readonly orgId: string;
|
|
228
|
+
readonly userId: string;
|
|
229
|
+
readonly role: OrgRole;
|
|
230
|
+
/** Which of the four paths is minting: a sign-in, a renewal, a password change. */
|
|
231
|
+
readonly path: 'password' | 'second-factor' | 'refresh' | 'password-change';
|
|
232
|
+
/** The kind proved on this sign-in, or `undefined` where none was asked for. */
|
|
233
|
+
readonly secondFactor: 'totp' | 'webauthn' | undefined;
|
|
234
|
+
/** Whether the account holds any confirmed factor at all. */
|
|
235
|
+
readonly enrolled: boolean;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A gate's answer, and three outcomes rather than a boolean with a flag.
|
|
239
|
+
*
|
|
240
|
+
* A union for the reason `LoginOutcome` is one: a caller that can ignore a
|
|
241
|
+
* field will, and ignoring this one means issuing a full session to somebody a
|
|
242
|
+
* policy says may only enrol.
|
|
243
|
+
*
|
|
244
|
+
* **`enrol` is what makes such a policy usable at all.** Enrolment lives under
|
|
245
|
+
* `/v1/me` and therefore needs authority, so a policy that only refused would
|
|
246
|
+
* lock out everybody who had not already enrolled on the day it was turned on,
|
|
247
|
+
* with no route back that does not go through the database — the shape this
|
|
248
|
+
* repository keeps closing. So a gate may say "this person may sign in, but
|
|
249
|
+
* only far enough to add a factor", and the core answers with a challenge that
|
|
250
|
+
* reaches the enrolment routes and nothing else.
|
|
251
|
+
*
|
|
252
|
+
* `refuse` is the stronger answer and is for the case where no action by the
|
|
253
|
+
* person could change it.
|
|
254
|
+
*/
|
|
255
|
+
export type SignInVerdict = {
|
|
256
|
+
readonly kind: 'admit';
|
|
257
|
+
} | {
|
|
258
|
+
readonly kind: 'enrol';
|
|
259
|
+
readonly reason: string;
|
|
260
|
+
} | {
|
|
261
|
+
readonly kind: 'refuse';
|
|
262
|
+
readonly reason: string;
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* A check run before a session is minted, in addition to the credential the
|
|
266
|
+
* core has already verified.
|
|
267
|
+
*
|
|
268
|
+
* A list, and every gate must admit. Gates decide whether *this* authentication
|
|
269
|
+
* is enough, never who the person is or what they may see: one runs only after
|
|
270
|
+
* a password has been verified or a refresh token spent, so it can subtract a
|
|
271
|
+
* session and can never grant one. That is why more than one is coherent and
|
|
272
|
+
* why the default — no gates — is the open core issuing a session for every
|
|
273
|
+
* credential it verifies, exactly as it did before this point existed.
|
|
274
|
+
*
|
|
275
|
+
* An organization policy requiring a second factor is the first user: a
|
|
276
|
+
* commercial gate that reads the policy and answers `enrol` for an account that
|
|
277
|
+
* has none.
|
|
278
|
+
*
|
|
279
|
+
* **SSO needs no gate and cannot have one.** An `AuthProvider` principal
|
|
280
|
+
* presents the identity provider's assertion as its credential on every
|
|
281
|
+
* request and never mints a session here, so a policy of the form "a second
|
|
282
|
+
* factor, or sign in through your identity provider" is satisfied by
|
|
283
|
+
* construction on its second half: the SSO door does not pass this way, and the
|
|
284
|
+
* password door is the one a gate closes.
|
|
285
|
+
*/
|
|
286
|
+
export interface SignInGate {
|
|
287
|
+
readonly name: string;
|
|
288
|
+
check(context: SignInContext): Promise<SignInVerdict>;
|
|
289
|
+
}
|
|
207
290
|
export declare function registerAuthzResolver(resolver: AuthzResolver): void;
|
|
208
291
|
export declare function registerAuthProvider(provider: AuthProvider): void;
|
|
209
292
|
export declare function registerAuditSink(sink: AuditSink): void;
|
|
210
293
|
export declare function registerIngestGate(gate: IngestGate): void;
|
|
294
|
+
export declare function registerSignInGate(gate: SignInGate): void;
|
|
211
295
|
export declare const ADMIN_PREFIX = "/v1/admin/";
|
|
212
296
|
export declare function mountAdminRoutes(...routes: readonly AdminRoute[]): void;
|
|
213
297
|
/**
|
|
@@ -251,6 +335,30 @@ export interface IngestRefusal {
|
|
|
251
335
|
* a check against.
|
|
252
336
|
*/
|
|
253
337
|
export declare function admitIngest(context: IngestContext): Promise<IngestRefusal | undefined>;
|
|
338
|
+
/** A gate's non-admission, as `admitSignIn` reports it. */
|
|
339
|
+
export interface SignInRefusal {
|
|
340
|
+
readonly module: string;
|
|
341
|
+
readonly kind: 'enrol' | 'refuse';
|
|
342
|
+
readonly reason: string;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Run every gate and report the strongest non-admission.
|
|
346
|
+
*
|
|
347
|
+
* `undefined` is admission: no gates registered, or every one admitted, which
|
|
348
|
+
* is why the open core mints a session for every credential it verifies.
|
|
349
|
+
*
|
|
350
|
+
* **This does not short-circuit on the first non-admission, and `admitIngest`
|
|
351
|
+
* beside it does.** That is a real difference rather than an inconsistency. An
|
|
352
|
+
* ingest verdict is admit or refuse, so the first refusal is the answer and
|
|
353
|
+
* asking the rest is work whose result cannot change it. Here there are two
|
|
354
|
+
* ways to say no and one is stronger: `refuse` is "nothing you can do gets you
|
|
355
|
+
* a session" and `enrol` is "a session, but only far enough to add a factor".
|
|
356
|
+
* A gate answering `enrol` while a later one would refuse must not hand out the
|
|
357
|
+
* weaker outcome, so every gate is asked and `refuse` wins.
|
|
358
|
+
*
|
|
359
|
+
* A `refuse` does stop the scan, because nothing outranks it.
|
|
360
|
+
*/
|
|
361
|
+
export declare function admitSignIn(context: SignInContext): Promise<SignInRefusal | undefined>;
|
|
254
362
|
/** Anything that records an event. Structurally the API's `audit` port. */
|
|
255
363
|
export interface AuditWriter {
|
|
256
364
|
write(event: AuditEvent): Promise<void>;
|
|
@@ -283,6 +391,7 @@ export declare function loadedExtensions(): {
|
|
|
283
391
|
sinks: string[];
|
|
284
392
|
routes: number;
|
|
285
393
|
gates: string[];
|
|
394
|
+
signIn: string[];
|
|
286
395
|
};
|
|
287
396
|
/**
|
|
288
397
|
* Empty the registry. **Tests only**, and named so that is unmistakable.
|
package/dist/extensions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGlE
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../extensions.ts"],"names":[],"mappings":"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"}
|
package/dist/extensions.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { resolve as builtInResolve } from './authz/resolve.js';
|
|
2
|
-
const registry = {
|
|
2
|
+
const registry = {
|
|
3
|
+
resolver: undefined,
|
|
4
|
+
providers: [],
|
|
5
|
+
sinks: [],
|
|
6
|
+
routes: [],
|
|
7
|
+
gates: [],
|
|
8
|
+
signIn: [],
|
|
9
|
+
};
|
|
3
10
|
/** Which module is registering, or `undefined` when registration is closed. */
|
|
4
11
|
let loading;
|
|
5
12
|
class ExtensionError extends Error {
|
|
@@ -37,6 +44,9 @@ export function registerAuditSink(sink) {
|
|
|
37
44
|
export function registerIngestGate(gate) {
|
|
38
45
|
registry.gates.push({ module: mustBeLoading('an ingest gate'), value: gate });
|
|
39
46
|
}
|
|
47
|
+
export function registerSignInGate(gate) {
|
|
48
|
+
registry.signIn.push({ module: mustBeLoading('a sign-in gate'), value: gate });
|
|
49
|
+
}
|
|
40
50
|
export const ADMIN_PREFIX = '/v1/admin/';
|
|
41
51
|
/**
|
|
42
52
|
* Paths the pattern is asked about directly.
|
|
@@ -168,6 +178,36 @@ export async function admitIngest(context) {
|
|
|
168
178
|
}
|
|
169
179
|
return undefined;
|
|
170
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Run every gate and report the strongest non-admission.
|
|
183
|
+
*
|
|
184
|
+
* `undefined` is admission: no gates registered, or every one admitted, which
|
|
185
|
+
* is why the open core mints a session for every credential it verifies.
|
|
186
|
+
*
|
|
187
|
+
* **This does not short-circuit on the first non-admission, and `admitIngest`
|
|
188
|
+
* beside it does.** That is a real difference rather than an inconsistency. An
|
|
189
|
+
* ingest verdict is admit or refuse, so the first refusal is the answer and
|
|
190
|
+
* asking the rest is work whose result cannot change it. Here there are two
|
|
191
|
+
* ways to say no and one is stronger: `refuse` is "nothing you can do gets you
|
|
192
|
+
* a session" and `enrol` is "a session, but only far enough to add a factor".
|
|
193
|
+
* A gate answering `enrol` while a later one would refuse must not hand out the
|
|
194
|
+
* weaker outcome, so every gate is asked and `refuse` wins.
|
|
195
|
+
*
|
|
196
|
+
* A `refuse` does stop the scan, because nothing outranks it.
|
|
197
|
+
*/
|
|
198
|
+
export async function admitSignIn(context) {
|
|
199
|
+
let weakest;
|
|
200
|
+
for (const gate of registry.signIn) {
|
|
201
|
+
const verdict = await gate.value.check(context);
|
|
202
|
+
if (verdict.kind === 'refuse') {
|
|
203
|
+
return { module: gate.value.name, kind: 'refuse', reason: verdict.reason };
|
|
204
|
+
}
|
|
205
|
+
if (verdict.kind === 'enrol' && weakest === undefined) {
|
|
206
|
+
weakest = { module: gate.value.name, kind: 'enrol', reason: verdict.reason };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return weakest;
|
|
210
|
+
}
|
|
171
211
|
const FANNED_OUT = Symbol.for('nacre.auditSinksApplied');
|
|
172
212
|
/**
|
|
173
213
|
* Record the event, then hand it to every sink.
|
|
@@ -215,6 +255,7 @@ export function loadedExtensions() {
|
|
|
215
255
|
sinks: registry.sinks.map((s) => `${s.module}:${s.value.name}`),
|
|
216
256
|
routes: registry.routes.length,
|
|
217
257
|
gates: registry.gates.map((g) => `${g.module}:${g.value.name}`),
|
|
258
|
+
signIn: registry.signIn.map((g) => `${g.module}:${g.value.name}`),
|
|
218
259
|
};
|
|
219
260
|
}
|
|
220
261
|
/**
|
|
@@ -231,6 +272,7 @@ export function resetExtensionsForTests() {
|
|
|
231
272
|
registry.sinks.length = 0;
|
|
232
273
|
registry.routes.length = 0;
|
|
233
274
|
registry.gates.length = 0;
|
|
275
|
+
registry.signIn.length = 0;
|
|
234
276
|
loading = undefined;
|
|
235
277
|
}
|
|
236
278
|
/** Open registration without importing anything. **Tests only.** */
|
package/dist/extensions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../extensions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../extensions.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export { protectedResourceMetadata, PROTECTED_RESOURCE_PATH, JWKS_PATH } from '.
|
|
|
7
7
|
export { allowedRequestHeaders, corsHeaders, isPreflight, mcpWalkHeaders, preflightHeaders } from './cors.js';
|
|
8
8
|
export { AUTHORIZATION_SERVER_PATH, AUTHORIZE_PATH, CODE_TTL_MS, REGISTER_PATH, TOKEN_PATH, authorizationServerMetadata, consentRedirect, generateClientId, generateCode, hashCode, redirectAllowed, verifierMatches, } from './authserver.js';
|
|
9
9
|
export type { AuthorizationServerMetadata } from './authserver.js';
|
|
10
|
-
export { activeResolver, admitIngest, ADMIN_PREFIX, adminRoutes, auditSinks, authProviders, loadModules, loadedExtensions, mountAdminRoutes, registerAuditSink, registerAuthProvider, registerAuthzResolver, registerIngestGate, resetExtensionsForTests, withAuditSinks, withLoadingModuleForTests, } from './extensions.js';
|
|
11
|
-
export type { AdminRequest, AdminResponse, AdminRoute, AuditEvent, AuditSink, AuditWriter, AuthProvider, AuthzResolver, IngestContext, IngestGate, IngestRefusal, IngestVerdict, ResolvedPrincipal, } from './extensions.js';
|
|
10
|
+
export { activeResolver, admitIngest, admitSignIn, ADMIN_PREFIX, adminRoutes, auditSinks, authProviders, loadModules, loadedExtensions, mountAdminRoutes, registerAuditSink, registerAuthProvider, registerAuthzResolver, registerIngestGate, registerSignInGate, resetExtensionsForTests, withAuditSinks, withLoadingModuleForTests, } from './extensions.js';
|
|
11
|
+
export type { AdminRequest, AdminResponse, AdminRoute, AuditEvent, AuditSink, AuditWriter, AuthProvider, AuthzResolver, IngestContext, IngestGate, IngestRefusal, IngestVerdict, ResolvedPrincipal, SignInContext, SignInGate, SignInRefusal, SignInVerdict, } from './extensions.js';
|
|
12
12
|
export type { ProtectedResourceMetadata } from './oauth.js';
|
|
13
13
|
export type { JwtKeys, JwtVerification } from './config.js';
|
|
14
14
|
export type { Config } from './config.js';
|
|
@@ -34,6 +34,8 @@ export { buildHybridQuery, collectionConfig, collectionName, PAYLOAD_INDEXES, ve
|
|
|
34
34
|
export type { Branch, CollectionShape, HybridQuery } from './vector/query.js';
|
|
35
35
|
export { encodeDocument, encodeQuery, isEmpty, SPARSE_VECTOR_NAME, tokenize } from './text/bm25.js';
|
|
36
36
|
export { base32Decode, base32Encode, generateRecoveryCode, generateTotpSecret, hashRecoveryCode, normalizeRecoveryCode, openTotpSecret, otpauthUrl, RECOVERY_CODE_COUNT, sealTotpSecret, TOTP_DIGITS, TOTP_PERIOD_SECONDS, TOTP_SKEW_STEPS, totpCode, totpStep, verifyTotp, } from './totp.js';
|
|
37
|
+
export { CborError, coseToPublicKey, decodeCbor, parseAuthenticatorData, readClientData, SUPPORTED_ALGORITHMS, verifyAssertion, verifyRegistration, } from './webauthn.js';
|
|
38
|
+
export type { Algorithm, Assertion, AssertionInput, AttestedCredential, AuthenticatorData, AuthenticatorFlags, ClientData, PublicKey, Registration, RegistrationInput, } from './webauthn.js';
|
|
37
39
|
export type { SparseVector } from './text/bm25.js';
|
|
38
40
|
export { classifyIngestFailure, withoutHosts } from './ingest-failure.js';
|
|
39
41
|
export type { IngestFailure, IngestFailureReason } from './ingest-failure.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5I,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC1F,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC7G,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAA;AAClE,OAAO,EACL,cAAc,EACd,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { loadConfig, loadJwtKeys, loadJwtVerification, loadMailConfig, loadSecon
|
|
|
5
5
|
export { protectedResourceMetadata, PROTECTED_RESOURCE_PATH, JWKS_PATH } from './oauth.js';
|
|
6
6
|
export { allowedRequestHeaders, corsHeaders, isPreflight, mcpWalkHeaders, preflightHeaders } from './cors.js';
|
|
7
7
|
export { AUTHORIZATION_SERVER_PATH, AUTHORIZE_PATH, CODE_TTL_MS, REGISTER_PATH, TOKEN_PATH, authorizationServerMetadata, consentRedirect, generateClientId, generateCode, hashCode, redirectAllowed, verifierMatches, } from './authserver.js';
|
|
8
|
-
export { activeResolver, admitIngest, ADMIN_PREFIX, adminRoutes, auditSinks, authProviders, loadModules, loadedExtensions, mountAdminRoutes, registerAuditSink, registerAuthProvider, registerAuthzResolver, registerIngestGate, resetExtensionsForTests, withAuditSinks, withLoadingModuleForTests, } from './extensions.js';
|
|
8
|
+
export { activeResolver, admitIngest, admitSignIn, ADMIN_PREFIX, adminRoutes, auditSinks, authProviders, loadModules, loadedExtensions, mountAdminRoutes, registerAuditSink, registerAuthProvider, registerAuthzResolver, registerIngestGate, registerSignInGate, resetExtensionsForTests, withAuditSinks, withLoadingModuleForTests, } from './extensions.js';
|
|
9
9
|
export { acrossOrganizations, createPool, whileAuthenticating, withOrg } from './db/client.js';
|
|
10
10
|
export { migrate, loadMigrations, migrationNames, pendingMigrations } from './db/migrate.js';
|
|
11
11
|
export { looksLikeEmail, organizationSlugError, provisionInPostgres, provisionOrganization } from './provision.js';
|
|
@@ -20,6 +20,7 @@ export { documentKey, S3, S3Error } from './s3.js';
|
|
|
20
20
|
export { buildHybridQuery, collectionConfig, collectionName, PAYLOAD_INDEXES, vectorName } from './vector/query.js';
|
|
21
21
|
export { encodeDocument, encodeQuery, isEmpty, SPARSE_VECTOR_NAME, tokenize } from './text/bm25.js';
|
|
22
22
|
export { base32Decode, base32Encode, generateRecoveryCode, generateTotpSecret, hashRecoveryCode, normalizeRecoveryCode, openTotpSecret, otpauthUrl, RECOVERY_CODE_COUNT, sealTotpSecret, TOTP_DIGITS, TOTP_PERIOD_SECONDS, TOTP_SKEW_STEPS, totpCode, totpStep, verifyTotp, } from './totp.js';
|
|
23
|
+
export { CborError, coseToPublicKey, decodeCbor, parseAuthenticatorData, readClientData, SUPPORTED_ALGORITHMS, verifyAssertion, verifyRegistration, } from './webauthn.js';
|
|
23
24
|
export { classifyIngestFailure, withoutHosts } from './ingest-failure.js';
|
|
24
25
|
export { DEFAULT_EMBED_MAX_TOKENS, estimateTokens, refusedForLength, TOKEN_RESERVE, tokenBudget, } from './text/tokens.js';
|
|
25
26
|
export { Counter, Gauge, Histogram, Registry, createMetrics } from './metrics.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5I,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC1F,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC7G,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EACL,cAAc,EACd,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
-- 0031 — a second factor you cannot be phished out of.
|
|
2
|
+
--
|
|
3
|
+
-- TOTP bounds a stolen password and does not bound a convincing page: six
|
|
4
|
+
-- digits typed into `nacre-work.com` work exactly as well there as here, and
|
|
5
|
+
-- the person who typed them has no way to know. WebAuthn is the factor whose
|
|
6
|
+
-- signature covers the origin, so an assertion produced for the wrong site does
|
|
7
|
+
-- not verify at the right one — that is the whole reason to carry a second kind
|
|
8
|
+
-- rather than more of the first.
|
|
9
|
+
--
|
|
10
|
+
-- Everything docs/authz.md says about a second factor is unchanged and applies
|
|
11
|
+
-- to this one: it decides whether a session starts, it grants nothing, it is
|
|
12
|
+
-- enrolled per person and never on their behalf, and a service account and a
|
|
13
|
+
-- delegation are refused. Nothing in packages/core/authz reads any of it.
|
|
14
|
+
|
|
15
|
+
-- ─────────── the kind ───────────
|
|
16
|
+
--
|
|
17
|
+
-- 0029 wrote `CHECK (kind IN ('totp'))` and said in its own comment that
|
|
18
|
+
-- WebAuthn would widen it "in its own migration rather than being written here
|
|
19
|
+
-- on speculation". This is that migration.
|
|
20
|
+
ALTER TABLE user_second_factors DROP CONSTRAINT user_second_factors_kind_check;
|
|
21
|
+
ALTER TABLE user_second_factors ADD CONSTRAINT user_second_factors_kind_check
|
|
22
|
+
CHECK (kind IN ('totp', 'webauthn'));
|
|
23
|
+
|
|
24
|
+
-- ─────────── what a WebAuthn factor is made of ───────────
|
|
25
|
+
--
|
|
26
|
+
-- A TOTP factor is a sealed shared secret; a WebAuthn factor is a public key
|
|
27
|
+
-- and has no secret at all. That asymmetry is the feature — a database dump
|
|
28
|
+
-- hands over nothing that can produce an assertion — and it is why `secret`
|
|
29
|
+
-- becomes nullable below rather than these columns being squeezed into it.
|
|
30
|
+
ALTER TABLE user_second_factors
|
|
31
|
+
-- The credential the authenticator minted, base64url as the browser reports
|
|
32
|
+
-- it. Bytes would be tidier in the abstract and worse here: every comparison
|
|
33
|
+
-- this column takes part in arrives as the string a browser sent.
|
|
34
|
+
ADD COLUMN credential_id text,
|
|
35
|
+
|
|
36
|
+
-- The public key, as a JWK. COSE and JWK are the same parameters under
|
|
37
|
+
-- different names, and `node:crypto` imports the second directly — so storing
|
|
38
|
+
-- the converted form means the verification path does no parsing at all on a
|
|
39
|
+
-- value that has already been through it once.
|
|
40
|
+
ADD COLUMN public_key jsonb,
|
|
41
|
+
|
|
42
|
+
-- The COSE algorithm: -7 ES256, -257 RS256, -8 EdDSA. Kept beside the key
|
|
43
|
+
-- rather than derived from it, because "which algorithm did this credential
|
|
44
|
+
-- register with" is a question about the registration and not about the
|
|
45
|
+
-- key's shape — and an RSA key alone does not say whether PKCS#1 or PSS was
|
|
46
|
+
-- agreed.
|
|
47
|
+
ADD COLUMN alg integer,
|
|
48
|
+
|
|
49
|
+
-- The authenticator's own counter. A value that goes backwards is the one
|
|
50
|
+
-- signal WebAuthn offers for a cloned authenticator. Most platform
|
|
51
|
+
-- authenticators never move it, which is allowed and is why this is not
|
|
52
|
+
-- NOT NULL DEFAULT 0 with a monotonic constraint — see the verifier.
|
|
53
|
+
ADD COLUMN sign_count bigint;
|
|
54
|
+
|
|
55
|
+
-- A TOTP factor has a secret and no key; a WebAuthn factor has a key and no
|
|
56
|
+
-- secret. Stated as a constraint rather than left to the application, because
|
|
57
|
+
-- the application is one caller and a row with neither is a factor that can
|
|
58
|
+
-- never answer.
|
|
59
|
+
ALTER TABLE user_second_factors ALTER COLUMN secret DROP NOT NULL;
|
|
60
|
+
ALTER TABLE user_second_factors ADD CONSTRAINT user_second_factors_shape CHECK (
|
|
61
|
+
(kind = 'totp' AND secret IS NOT NULL AND credential_id IS NULL AND public_key IS NULL AND alg IS NULL)
|
|
62
|
+
OR
|
|
63
|
+
(kind = 'webauthn' AND secret IS NULL AND credential_id IS NOT NULL AND public_key IS NOT NULL AND alg IS NOT NULL)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
-- Within one organization a credential id names one factor. Not installation
|
|
67
|
+
-- wide, and that is the point: an assertion here is the *second* step of a
|
|
68
|
+
-- sign-in, so the organization and the person are already known and the lookup
|
|
69
|
+
-- reads through `withOrg` like everything else. A globally unique index would
|
|
70
|
+
-- be a cross-tenant read waiting for somebody to write the query.
|
|
71
|
+
CREATE UNIQUE INDEX user_second_factors_credential
|
|
72
|
+
ON user_second_factors (org_id, credential_id)
|
|
73
|
+
WHERE credential_id IS NOT NULL;
|
|
74
|
+
|
|
75
|
+
-- ─────────── the challenge ───────────
|
|
76
|
+
--
|
|
77
|
+
-- Every ceremony takes a challenge this server issued, and it has to be
|
|
78
|
+
-- **single-use**: the signature covers it, so an assertion captured on the wire
|
|
79
|
+
-- is replayable for as long as its challenge is. Nothing else in the ceremony
|
|
80
|
+
-- stops that.
|
|
81
|
+
--
|
|
82
|
+
-- A table rather than a signed, stateless value. A stateless challenge would
|
|
83
|
+
-- have to be spent somewhere anyway to be single-use, and a store that only
|
|
84
|
+
-- prevents replay is the same store as one that also carries the value — with
|
|
85
|
+
-- the difference that this one can be read to see what is outstanding.
|
|
86
|
+
CREATE TABLE webauthn_challenges (
|
|
87
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
88
|
+
org_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
89
|
+
user_id uuid NOT NULL,
|
|
90
|
+
|
|
91
|
+
-- 'register' or 'authenticate'. A challenge issued for enrolment must not be
|
|
92
|
+
-- spendable on a sign-in: the first is asked for by somebody already signed
|
|
93
|
+
-- in, and letting the two share a pool would let a session mint the input to
|
|
94
|
+
-- a ceremony it is not in.
|
|
95
|
+
purpose text NOT NULL CHECK (purpose IN ('register', 'authenticate')),
|
|
96
|
+
|
|
97
|
+
-- base64url, exactly as it goes into `clientDataJSON` and comes back out, so
|
|
98
|
+
-- the comparison is a string equality and never an encoding round trip.
|
|
99
|
+
challenge text NOT NULL,
|
|
100
|
+
|
|
101
|
+
expires_at timestamptz NOT NULL,
|
|
102
|
+
used_at timestamptz,
|
|
103
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
104
|
+
|
|
105
|
+
FOREIGN KEY (user_id, org_id) REFERENCES users (id, org_id) ON DELETE CASCADE,
|
|
106
|
+
|
|
107
|
+
-- Spending one is a single UPDATE with no read in front of it that another
|
|
108
|
+
-- request could race, which is the same shape `password_reset_tokens` uses.
|
|
109
|
+
UNIQUE (org_id, challenge)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE INDEX ON webauthn_challenges (expires_at) WHERE used_at IS NULL;
|
|
113
|
+
|
|
114
|
+
-- ─────────── second line of defense ───────────
|
|
115
|
+
ALTER TABLE webauthn_challenges ENABLE ROW LEVEL SECURITY;
|
|
116
|
+
ALTER TABLE webauthn_challenges FORCE ROW LEVEL SECURITY;
|
|
117
|
+
CREATE POLICY org_isolation ON webauthn_challenges USING (org_id = current_setting('app.current_org')::uuid);
|
|
118
|
+
|
|
119
|
+
-- The application reads and writes it; nothing else does. No grant to
|
|
120
|
+
-- `nacre_worker`, for the reason 0029 gives: the worker has no question about
|
|
121
|
+
-- who signed in, and a privilege nothing reads is the shape this repository
|
|
122
|
+
-- keeps removing.
|
|
123
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON webauthn_challenges TO nacre_app;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- An account that more than one person holds is not a person.
|
|
2
|
+
--
|
|
3
|
+
-- The product's whole argument for `/v1/me` is that a second factor is a thing
|
|
4
|
+
-- the *person* holds — which is why an administrator can reset a password and
|
|
5
|
+
-- deliberately cannot touch a factor. A published demo login breaks that
|
|
6
|
+
-- premise: several strangers hold one credential, so "the person" is not a
|
|
7
|
+
-- thing, and the first of them to enrol a factor locks out every other one
|
|
8
|
+
-- permanently, with no administrative route back.
|
|
9
|
+
--
|
|
10
|
+
-- Until 0.19.0 a deployment had an accidental switch: with no `NACRE_2FA_KEY`
|
|
11
|
+
-- the whole surface answered 404. WebAuthn needs no key, so that switch is
|
|
12
|
+
-- gone — correctly, because it was installation-wide and the property is not.
|
|
13
|
+
-- An installation may have one shared account and a hundred people.
|
|
14
|
+
--
|
|
15
|
+
-- So it is a column on the row it is about. A shared account:
|
|
16
|
+
-- * cannot enrol or remove a second factor,
|
|
17
|
+
-- * cannot change its own password,
|
|
18
|
+
-- * is issued no password-reset token.
|
|
19
|
+
-- An administrator still sets its password through `POST /v1/users/{id}/password`,
|
|
20
|
+
-- which is how whoever publishes such a credential rotates it. The account's
|
|
21
|
+
-- credentials are *administered* rather than held, which is the whole of what
|
|
22
|
+
-- this column says.
|
|
23
|
+
--
|
|
24
|
+
-- `false` for every existing row, so nothing changes for any account that
|
|
25
|
+
-- belongs to somebody.
|
|
26
|
+
ALTER TABLE users ADD COLUMN shared boolean NOT NULL DEFAULT false;
|
|
27
|
+
|
|
28
|
+
-- A shared account has no factor, and this is the structural half rather than
|
|
29
|
+
-- a second copy of the route check.
|
|
30
|
+
--
|
|
31
|
+
-- The routes refuse first and answer 404, which is what a caller sees. This is
|
|
32
|
+
-- what holds when a route is added later and forgets: an INSERT is refused by
|
|
33
|
+
-- the database whichever surface issued it, so the worst a forgotten check can
|
|
34
|
+
-- produce is a 500 rather than a locked-out demonstration. A check constraint
|
|
35
|
+
-- cannot do it — the fact lives in another table — so it is a trigger.
|
|
36
|
+
CREATE OR REPLACE FUNCTION refuse_factor_on_shared_account() RETURNS trigger
|
|
37
|
+
LANGUAGE plpgsql
|
|
38
|
+
-- The function reads `users`, which is FORCE'd, so it runs as the owner and
|
|
39
|
+
-- pins its search path — the same shape every SECURITY DEFINER here follows.
|
|
40
|
+
SECURITY DEFINER SET search_path = public, pg_temp
|
|
41
|
+
AS $$
|
|
42
|
+
BEGIN
|
|
43
|
+
IF EXISTS (
|
|
44
|
+
SELECT 1 FROM users
|
|
45
|
+
WHERE id = NEW.user_id AND org_id = NEW.org_id AND shared
|
|
46
|
+
) THEN
|
|
47
|
+
RAISE EXCEPTION 'a shared account cannot hold a second factor'
|
|
48
|
+
USING ERRCODE = 'check_violation';
|
|
49
|
+
END IF;
|
|
50
|
+
RETURN NEW;
|
|
51
|
+
END;
|
|
52
|
+
$$;
|
|
53
|
+
|
|
54
|
+
CREATE TRIGGER user_second_factors_not_shared
|
|
55
|
+
BEFORE INSERT ON user_second_factors
|
|
56
|
+
FOR EACH ROW EXECUTE FUNCTION refuse_factor_on_shared_account();
|