@nacre.work/core 0.17.5 → 0.19.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/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +114 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/mail.d.ts +43 -0
- package/dist/mail.d.ts.map +1 -0
- package/dist/mail.js +92 -0
- package/dist/mail.js.map +1 -0
- package/dist/migrations/0029_second_factor.sql +111 -0
- package/dist/migrations/0030_password_reset.sql +59 -0
- package/dist/migrations/0031_webauthn.sql +123 -0
- package/dist/passwords.d.ts.map +1 -1
- package/dist/passwords.js +8 -0
- package/dist/passwords.js.map +1 -1
- package/dist/totp.d.ts +79 -0
- package/dist/totp.d.ts.map +1 -0
- package/dist/totp.js +206 -0
- package/dist/totp.js.map +1 -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 +3 -1
package/dist/webauthn.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebAuthn, as far as the arithmetic goes.
|
|
3
|
+
*
|
|
4
|
+
* The same split `totp.ts` has: this file knows nothing about a database and
|
|
5
|
+
* everything about bytes, so it can be checked against a real authenticator's
|
|
6
|
+
* output; `packages/api/src/second-factor.ts` holds the storage and the bounds.
|
|
7
|
+
*
|
|
8
|
+
* ## Why there is no dependency
|
|
9
|
+
*
|
|
10
|
+
* `@simplewebauthn/server` is the obvious answer and is a good library. It is
|
|
11
|
+
* not the answer here, and the reason is the same one the multipart parser
|
|
12
|
+
* gives: **this is attacker-supplied binary on the authentication path**, which
|
|
13
|
+
* is the one place a dependency's parser bugs become ours. The parser took
|
|
14
|
+
* `pdf-inspector` because a PDF is a format nobody can write a reader for in an
|
|
15
|
+
* afternoon; what a second factor needs is much smaller than that, and it is
|
|
16
|
+
* bounded by a specification that says the encoding is *canonical*.
|
|
17
|
+
*
|
|
18
|
+
* Two things make it small enough to be worth writing:
|
|
19
|
+
*
|
|
20
|
+
* - **`node:crypto` imports a JWK.** COSE keys map onto JWK field for field, so
|
|
21
|
+
* ES256, RS256 and EdDSA all verify with no ASN.1 and no DER assembly.
|
|
22
|
+
* Checked by round-tripping a generated key of each kind before a line of
|
|
23
|
+
* this was written.
|
|
24
|
+
* - **CTAP2 says canonical CBOR**, which is definite-length only, no tags, no
|
|
25
|
+
* floats. So the decoder below is a subset and every absence is a *refusal*
|
|
26
|
+
* rather than a gap — an indefinite-length map is not "unsupported here", it
|
|
27
|
+
* is a thing a conforming authenticator does not send.
|
|
28
|
+
*
|
|
29
|
+
* The obligation that comes with the choice is strictness, exactly as it is for
|
|
30
|
+
* multipart: every bound is a refusal and never a truncation, because a
|
|
31
|
+
* truncation is a silent disagreement between what was sent and what was
|
|
32
|
+
* verified. On this path that disagreement is somebody's second factor.
|
|
33
|
+
*
|
|
34
|
+
* ## What this deliberately does not do
|
|
35
|
+
*
|
|
36
|
+
* **Attestation is not verified, and `none` is what the server asks for.** This
|
|
37
|
+
* is a *second* factor: the person has already produced a password, and what
|
|
38
|
+
* this adds is proof of possession. Attestation answers a different question —
|
|
39
|
+
* "which model of authenticator is this" — which an enterprise buying
|
|
40
|
+
* hardware-key policy cares about and a self-hoster does not. Verifying it
|
|
41
|
+
* means a trust anchor list to keep current, and a list nobody updates is worse
|
|
42
|
+
* than no list. The registration path therefore reads the attestation object
|
|
43
|
+
* for its `authData` and ignores `attStmt` entirely, which is what makes the
|
|
44
|
+
* CBOR subset small.
|
|
45
|
+
*
|
|
46
|
+
* **Passwordless is not offered.** A discoverable credential used as a first
|
|
47
|
+
* factor changes what an account is, and `docs/authz.md` says a second factor
|
|
48
|
+
* decides whether a session *starts* and grants nothing. Widening that is a
|
|
49
|
+
* change to the specification and belongs there first.
|
|
50
|
+
*/
|
|
51
|
+
import { createHash, createPublicKey, createVerify, verify as verifySignature } from 'node:crypto';
|
|
52
|
+
/* ────────────────────────────── CBOR, the subset ────────────────────────────
|
|
53
|
+
*
|
|
54
|
+
* Enough of RFC 8949 to read an attestation object and a COSE key, and nothing
|
|
55
|
+
* else. Every bound below is chosen against what a conforming authenticator
|
|
56
|
+
* actually sends, so exceeding one is a refusal rather than a limit somebody
|
|
57
|
+
* will meet honestly.
|
|
58
|
+
*/
|
|
59
|
+
/** No structure here nests past four: attObj → attStmt → x5c → bytes. */
|
|
60
|
+
const MAX_DEPTH = 8;
|
|
61
|
+
/** A COSE key has six entries; an attestation object has three. */
|
|
62
|
+
const MAX_ENTRIES = 64;
|
|
63
|
+
/** An RSA modulus is 512 bytes. Nothing here is larger by an order. */
|
|
64
|
+
const MAX_BYTES = 8192;
|
|
65
|
+
export class CborError extends Error {
|
|
66
|
+
constructor(message) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.name = 'CborError';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function need(r, n) {
|
|
72
|
+
if (r.offset + n > r.bytes.length)
|
|
73
|
+
throw new CborError('ran off the end of the input');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The head of an item: its major type and its argument.
|
|
77
|
+
*
|
|
78
|
+
* Indefinite length (`additional === 31`) is refused rather than supported.
|
|
79
|
+
* CTAP2 canonical CBOR is definite-length, so an indefinite one is not an
|
|
80
|
+
* authenticator being flexible — it is input that did not come from one.
|
|
81
|
+
*/
|
|
82
|
+
function head(r) {
|
|
83
|
+
need(r, 1);
|
|
84
|
+
const initial = r.bytes[r.offset];
|
|
85
|
+
r.offset += 1;
|
|
86
|
+
const major = initial >> 5;
|
|
87
|
+
const additional = initial & 0x1f;
|
|
88
|
+
if (additional < 24)
|
|
89
|
+
return { major, arg: additional };
|
|
90
|
+
if (additional === 24) {
|
|
91
|
+
need(r, 1);
|
|
92
|
+
const arg = r.bytes[r.offset];
|
|
93
|
+
r.offset += 1;
|
|
94
|
+
return { major, arg };
|
|
95
|
+
}
|
|
96
|
+
if (additional === 25) {
|
|
97
|
+
need(r, 2);
|
|
98
|
+
const arg = (r.bytes[r.offset] << 8) | r.bytes[r.offset + 1];
|
|
99
|
+
r.offset += 2;
|
|
100
|
+
return { major, arg };
|
|
101
|
+
}
|
|
102
|
+
if (additional === 26) {
|
|
103
|
+
need(r, 4);
|
|
104
|
+
const view = new DataView(r.bytes.buffer, r.bytes.byteOffset + r.offset, 4);
|
|
105
|
+
const arg = view.getUint32(0);
|
|
106
|
+
r.offset += 4;
|
|
107
|
+
return { major, arg };
|
|
108
|
+
}
|
|
109
|
+
// 27 is a 64-bit argument and 28-30 are reserved; 31 is indefinite length.
|
|
110
|
+
throw new CborError(`unsupported CBOR argument ${String(additional)} for major type ${String(major)}; ` +
|
|
111
|
+
'this reader accepts the canonical, definite-length encoding CTAP2 requires');
|
|
112
|
+
}
|
|
113
|
+
function item(r, depth) {
|
|
114
|
+
if (depth > MAX_DEPTH)
|
|
115
|
+
throw new CborError(`nested past ${String(MAX_DEPTH)} levels`);
|
|
116
|
+
const { major, arg } = head(r);
|
|
117
|
+
switch (major) {
|
|
118
|
+
case 0:
|
|
119
|
+
return arg;
|
|
120
|
+
case 1:
|
|
121
|
+
// -1 - arg. Negative integers are how COSE labels its key parameters.
|
|
122
|
+
return -1 - arg;
|
|
123
|
+
case 2: {
|
|
124
|
+
if (arg > MAX_BYTES)
|
|
125
|
+
throw new CborError(`byte string of ${String(arg)} bytes is over the limit`);
|
|
126
|
+
need(r, arg);
|
|
127
|
+
const out = r.bytes.subarray(r.offset, r.offset + arg);
|
|
128
|
+
r.offset += arg;
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
case 3: {
|
|
132
|
+
if (arg > MAX_BYTES)
|
|
133
|
+
throw new CborError(`text string of ${String(arg)} bytes is over the limit`);
|
|
134
|
+
need(r, arg);
|
|
135
|
+
const out = new TextDecoder('utf-8', { fatal: true }).decode(r.bytes.subarray(r.offset, r.offset + arg));
|
|
136
|
+
r.offset += arg;
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
case 4: {
|
|
140
|
+
if (arg > MAX_ENTRIES)
|
|
141
|
+
throw new CborError(`array of ${String(arg)} items is over the limit`);
|
|
142
|
+
const out = [];
|
|
143
|
+
for (let i = 0; i < arg; i += 1)
|
|
144
|
+
out.push(item(r, depth + 1));
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
case 5: {
|
|
148
|
+
if (arg > MAX_ENTRIES)
|
|
149
|
+
throw new CborError(`map of ${String(arg)} entries is over the limit`);
|
|
150
|
+
const out = new Map();
|
|
151
|
+
for (let i = 0; i < arg; i += 1) {
|
|
152
|
+
const key = item(r, depth + 1);
|
|
153
|
+
if (typeof key !== 'number' && typeof key !== 'string') {
|
|
154
|
+
throw new CborError('map keys here are integers or text strings');
|
|
155
|
+
}
|
|
156
|
+
// A repeated key is not a conforming encoding, and picking one of the
|
|
157
|
+
// two would be this reader deciding something the sender did not.
|
|
158
|
+
if (out.has(key))
|
|
159
|
+
throw new CborError(`duplicate map key ${String(key)}`);
|
|
160
|
+
out.set(key, item(r, depth + 1));
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
case 7: {
|
|
165
|
+
if (arg === 20)
|
|
166
|
+
return false;
|
|
167
|
+
if (arg === 21)
|
|
168
|
+
return true;
|
|
169
|
+
if (arg === 22)
|
|
170
|
+
return null;
|
|
171
|
+
throw new CborError(`unsupported simple value ${String(arg)}`);
|
|
172
|
+
}
|
|
173
|
+
default:
|
|
174
|
+
// 6 is tags. Nothing in an attestation object or a COSE key is tagged.
|
|
175
|
+
throw new CborError(`unsupported CBOR major type ${String(major)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Decode one item, and **refuse trailing bytes**.
|
|
180
|
+
*
|
|
181
|
+
* A decoder that stops at the end of the first item and ignores what follows
|
|
182
|
+
* lets two different inputs verify identically, which is the shape of a
|
|
183
|
+
* signature-malleability bug. What is signed here is the whole buffer.
|
|
184
|
+
*/
|
|
185
|
+
export function decodeCbor(bytes) {
|
|
186
|
+
const r = { bytes, offset: 0 };
|
|
187
|
+
const value = item(r, 0);
|
|
188
|
+
if (r.offset !== bytes.length) {
|
|
189
|
+
throw new CborError(`${String(bytes.length - r.offset)} trailing byte(s) after the CBOR item`);
|
|
190
|
+
}
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The fixed-layout half: 32 bytes of RP id hash, a flags byte, a counter, and
|
|
195
|
+
* then the attested credential where the flag says there is one.
|
|
196
|
+
*
|
|
197
|
+
* The credential's public key is CBOR and is *not* length-prefixed — the spec
|
|
198
|
+
* says it runs to the end unless extensions follow. So this decodes it with the
|
|
199
|
+
* reader above and uses how far that got, which is also what refuses a key with
|
|
200
|
+
* junk appended.
|
|
201
|
+
*/
|
|
202
|
+
export function parseAuthenticatorData(bytes) {
|
|
203
|
+
if (bytes.length < 37)
|
|
204
|
+
throw new CborError(`authenticator data is ${String(bytes.length)} bytes; 37 is the minimum`);
|
|
205
|
+
const rpIdHash = bytes.subarray(0, 32);
|
|
206
|
+
const raw = bytes[32];
|
|
207
|
+
const flags = {
|
|
208
|
+
userPresent: (raw & 0x01) !== 0,
|
|
209
|
+
userVerified: (raw & 0x04) !== 0,
|
|
210
|
+
attestedCredentialData: (raw & 0x40) !== 0,
|
|
211
|
+
extensionData: (raw & 0x80) !== 0,
|
|
212
|
+
};
|
|
213
|
+
const signCount = new DataView(bytes.buffer, bytes.byteOffset + 33, 4).getUint32(0);
|
|
214
|
+
if (!flags.attestedCredentialData)
|
|
215
|
+
return { rpIdHash, flags, signCount };
|
|
216
|
+
if (bytes.length < 55)
|
|
217
|
+
throw new CborError('attested credential data is truncated');
|
|
218
|
+
const aaguid = bytes.subarray(37, 53);
|
|
219
|
+
const idLength = (bytes[53] << 8) | bytes[54];
|
|
220
|
+
// 1023 is the ceiling WebAuthn puts on a credential id.
|
|
221
|
+
if (idLength > 1023)
|
|
222
|
+
throw new CborError(`credential id claims ${String(idLength)} bytes; 1023 is the maximum`);
|
|
223
|
+
if (bytes.length < 55 + idLength)
|
|
224
|
+
throw new CborError('credential id is truncated');
|
|
225
|
+
const id = bytes.subarray(55, 55 + idLength);
|
|
226
|
+
const r = { bytes, offset: 55 + idLength };
|
|
227
|
+
const coseKey = item(r, 0);
|
|
228
|
+
if (!(coseKey instanceof Map))
|
|
229
|
+
throw new CborError('the credential public key is not a CBOR map');
|
|
230
|
+
// Extensions may follow, and nothing here reads one — but bytes that are
|
|
231
|
+
// neither a key nor an extension are input this did not understand.
|
|
232
|
+
if (!flags.extensionData && r.offset !== bytes.length) {
|
|
233
|
+
throw new CborError(`${String(bytes.length - r.offset)} byte(s) after the public key, with no extension flag`);
|
|
234
|
+
}
|
|
235
|
+
return { rpIdHash, flags, signCount, credential: { aaguid, id, coseKey } };
|
|
236
|
+
}
|
|
237
|
+
/* ───────────────────────────── COSE, as a JWK ─────────────────────────────── */
|
|
238
|
+
/**
|
|
239
|
+
* The algorithms this accepts, and why the list is short.
|
|
240
|
+
*
|
|
241
|
+
* ES256 is what every platform authenticator produces and is the one a
|
|
242
|
+
* deployment will actually see. RS256 is what older Windows Hello and some
|
|
243
|
+
* enterprise tokens produce. EdDSA is what a few hardware keys produce. Nothing
|
|
244
|
+
* else is offered: an algorithm this server does not name in
|
|
245
|
+
* `pubKeyCredParams` is one no authenticator will choose, so accepting it at
|
|
246
|
+
* registration would be accepting something we did not ask for.
|
|
247
|
+
*/
|
|
248
|
+
export const SUPPORTED_ALGORITHMS = [-7, -257, -8];
|
|
249
|
+
const b64url = (bytes) => Buffer.from(bytes).toString('base64url');
|
|
250
|
+
function bytesAt(key, label, what) {
|
|
251
|
+
const value = key.get(label);
|
|
252
|
+
if (!(value instanceof Uint8Array))
|
|
253
|
+
throw new CborError(`the COSE key has no ${what}`);
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A COSE key becomes a JWK, which `node:crypto` imports directly.
|
|
258
|
+
*
|
|
259
|
+
* This is the whole reason there is no ASN.1 in this file. COSE and JWK are the
|
|
260
|
+
* same parameters under different names — `crv`, `x`, `y` for an EC key; `n`
|
|
261
|
+
* and `e` for RSA — so the conversion is a field mapping and the DER assembly
|
|
262
|
+
* that a WebAuthn implementation usually carries does not exist here.
|
|
263
|
+
*/
|
|
264
|
+
export function coseToPublicKey(key) {
|
|
265
|
+
const kty = key.get(1);
|
|
266
|
+
const named = key.get(3);
|
|
267
|
+
if (typeof named !== 'number' || !SUPPORTED_ALGORITHMS.includes(named)) {
|
|
268
|
+
throw new CborError(`algorithm ${String(named)} is not one this server asked for (ES256, RS256 or EdDSA)`);
|
|
269
|
+
}
|
|
270
|
+
const alg = named;
|
|
271
|
+
if (alg === -7) {
|
|
272
|
+
if (kty !== 2)
|
|
273
|
+
throw new CborError('ES256 needs an EC2 key');
|
|
274
|
+
// -1 is the curve; 1 is P-256, and it is the only one ES256 is defined over.
|
|
275
|
+
if (key.get(-1) !== 1)
|
|
276
|
+
throw new CborError('ES256 needs curve P-256');
|
|
277
|
+
return {
|
|
278
|
+
alg,
|
|
279
|
+
jwk: { kty: 'EC', crv: 'P-256', x: b64url(bytesAt(key, -2, 'x coordinate')), y: b64url(bytesAt(key, -3, 'y coordinate')) },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if (alg === -8) {
|
|
283
|
+
if (kty !== 1)
|
|
284
|
+
throw new CborError('EdDSA needs an OKP key');
|
|
285
|
+
if (key.get(-1) !== 6)
|
|
286
|
+
throw new CborError('EdDSA here means Ed25519');
|
|
287
|
+
return { alg, jwk: { kty: 'OKP', crv: 'Ed25519', x: b64url(bytesAt(key, -2, 'public key')) } };
|
|
288
|
+
}
|
|
289
|
+
if (kty !== 3)
|
|
290
|
+
throw new CborError('RS256 needs an RSA key');
|
|
291
|
+
return {
|
|
292
|
+
alg,
|
|
293
|
+
jwk: { kty: 'RSA', n: b64url(bytesAt(key, -1, 'modulus')), e: b64url(bytesAt(key, -2, 'exponent')) },
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const keyObject = (key) =>
|
|
297
|
+
// `jwk` is a plain record here because that is what this file builds; the
|
|
298
|
+
// type `createPublicKey` wants is structurally the same thing.
|
|
299
|
+
createPublicKey({ key: key.jwk, format: 'jwk' });
|
|
300
|
+
/**
|
|
301
|
+
* `clientDataJSON`, parsed and checked.
|
|
302
|
+
*
|
|
303
|
+
* The origin is compared for **equality** against what the deployment
|
|
304
|
+
* configured, never by suffix. `evil-nacre.work` ends with `nacre.work`, and an
|
|
305
|
+
* origin check that admits it is not a check.
|
|
306
|
+
*/
|
|
307
|
+
export function readClientData(json, expect) {
|
|
308
|
+
let parsed;
|
|
309
|
+
try {
|
|
310
|
+
parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(json));
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
throw new CborError('clientDataJSON is not valid UTF-8 JSON');
|
|
314
|
+
}
|
|
315
|
+
const data = parsed;
|
|
316
|
+
if (data.type !== expect.type)
|
|
317
|
+
throw new CborError(`clientDataJSON says "${String(data.type)}"; expected "${expect.type}"`);
|
|
318
|
+
if (typeof data.challenge !== 'string' || data.challenge !== expect.challenge) {
|
|
319
|
+
throw new CborError('the challenge does not match the one this server issued');
|
|
320
|
+
}
|
|
321
|
+
if (typeof data.origin !== 'string' || !expect.origin.includes(data.origin)) {
|
|
322
|
+
throw new CborError(`origin ${String(data.origin)} is not one this deployment serves`);
|
|
323
|
+
}
|
|
324
|
+
return data;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Verify a registration, and store what comes back.
|
|
328
|
+
*
|
|
329
|
+
* `attStmt` is read and discarded: see the header for why attestation is not
|
|
330
|
+
* verified here. What *is* checked is everything that decides whether the
|
|
331
|
+
* credential belongs to this deployment and this ceremony — the challenge, the
|
|
332
|
+
* origin, the RP id, and that somebody was present at the authenticator.
|
|
333
|
+
*/
|
|
334
|
+
export function verifyRegistration(input) {
|
|
335
|
+
readClientData(input.clientDataJSON, {
|
|
336
|
+
type: 'webauthn.create',
|
|
337
|
+
challenge: input.challenge,
|
|
338
|
+
origin: input.origins,
|
|
339
|
+
});
|
|
340
|
+
const attestation = decodeCbor(input.attestationObject);
|
|
341
|
+
if (!(attestation instanceof Map))
|
|
342
|
+
throw new CborError('the attestation object is not a CBOR map');
|
|
343
|
+
const authDataBytes = attestation.get('authData');
|
|
344
|
+
if (!(authDataBytes instanceof Uint8Array))
|
|
345
|
+
throw new CborError('the attestation object carries no authData');
|
|
346
|
+
const authData = parseAuthenticatorData(authDataBytes);
|
|
347
|
+
const expectedRpIdHash = createHash('sha256').update(input.rpId).digest();
|
|
348
|
+
if (!expectedRpIdHash.equals(Buffer.from(authData.rpIdHash))) {
|
|
349
|
+
throw new CborError('the credential was made for a different relying party');
|
|
350
|
+
}
|
|
351
|
+
// User presence is the whole of what a second factor asserts: somebody with
|
|
352
|
+
// the authenticator in hand did something. A credential registered without it
|
|
353
|
+
// would be one a page could mint silently.
|
|
354
|
+
if (!authData.flags.userPresent)
|
|
355
|
+
throw new CborError('the authenticator reports nobody was present');
|
|
356
|
+
if (authData.credential === undefined)
|
|
357
|
+
throw new CborError('the registration carries no credential');
|
|
358
|
+
return {
|
|
359
|
+
credentialId: authData.credential.id,
|
|
360
|
+
publicKey: coseToPublicKey(authData.credential.coseKey),
|
|
361
|
+
signCount: authData.signCount,
|
|
362
|
+
userVerified: authData.flags.userVerified,
|
|
363
|
+
aaguid: authData.credential.aaguid,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Verify an assertion.
|
|
368
|
+
*
|
|
369
|
+
* The signature is over `authenticatorData || sha256(clientDataJSON)`, which is
|
|
370
|
+
* what binds the challenge and the origin into the signed message rather than
|
|
371
|
+
* leaving them as things this code merely looked at.
|
|
372
|
+
*
|
|
373
|
+
* **The counter is checked and a stuck counter is allowed.** A counter that
|
|
374
|
+
* went backwards is the signal WebAuthn offers for a cloned authenticator, so
|
|
375
|
+
* it is a refusal. A counter that stays at zero is what every platform
|
|
376
|
+
* authenticator — Touch ID, Windows Hello, a passkey in a password manager —
|
|
377
|
+
* actually does, so refusing that would refuse the authenticators most people
|
|
378
|
+
* have.
|
|
379
|
+
*/
|
|
380
|
+
export function verifyAssertion(input) {
|
|
381
|
+
readClientData(input.clientDataJSON, {
|
|
382
|
+
type: 'webauthn.get',
|
|
383
|
+
challenge: input.challenge,
|
|
384
|
+
origin: input.origins,
|
|
385
|
+
});
|
|
386
|
+
const authData = parseAuthenticatorData(input.authenticatorData);
|
|
387
|
+
const expectedRpIdHash = createHash('sha256').update(input.rpId).digest();
|
|
388
|
+
if (!expectedRpIdHash.equals(Buffer.from(authData.rpIdHash))) {
|
|
389
|
+
throw new CborError('the assertion is for a different relying party');
|
|
390
|
+
}
|
|
391
|
+
if (!authData.flags.userPresent)
|
|
392
|
+
throw new CborError('the authenticator reports nobody was present');
|
|
393
|
+
const signed = Buffer.concat([
|
|
394
|
+
Buffer.from(input.authenticatorData),
|
|
395
|
+
createHash('sha256').update(input.clientDataJSON).digest(),
|
|
396
|
+
]);
|
|
397
|
+
const key = keyObject(input.publicKey);
|
|
398
|
+
const ok = input.publicKey.alg === -8
|
|
399
|
+
? // EdDSA takes no digest name: the algorithm names its own.
|
|
400
|
+
verifySignature(null, signed, key, input.signature)
|
|
401
|
+
: createVerify('sha256').update(signed).verify(key, input.signature);
|
|
402
|
+
if (!ok)
|
|
403
|
+
throw new CborError('the signature does not verify against the stored credential');
|
|
404
|
+
if (authData.signCount !== 0 && authData.signCount <= input.storedSignCount) {
|
|
405
|
+
throw new CborError(`the signature counter went from ${String(input.storedSignCount)} to ${String(authData.signCount)}, ` +
|
|
406
|
+
'which is what a cloned authenticator looks like');
|
|
407
|
+
}
|
|
408
|
+
return { signCount: authData.signCount, userVerified: authData.flags.userVerified };
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=webauthn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webauthn.js","sourceRoot":"","sources":["../webauthn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,aAAa,CAAA;AAGlG;;;;;;GAMG;AAEH,yEAAyE;AACzE,MAAM,SAAS,GAAG,CAAC,CAAA;AACnB,mEAAmE;AACnE,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,uEAAuE;AACvE,MAAM,SAAS,GAAG,IAAI,CAAA;AAEtB,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,WAAW,CAAA;IACzB,CAAC;CACF;AASD,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS;IAChC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAA;AACxF,CAAC;AAED;;;;;;GAMG;AACH,SAAS,IAAI,CAAC,CAAS;IACrB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACV,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAE,CAAA;IAClC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;IACb,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,CAAA;IAC1B,MAAM,UAAU,GAAG,OAAO,GAAG,IAAI,CAAA;IAEjC,IAAI,UAAU,GAAG,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,CAAA;IACtD,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACV,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAE,CAAA;QAC9B,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;QACb,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;IACvB,CAAC;IACD,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACV,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAE,CAAA;QAC9D,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;QACb,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;IACvB,CAAC;IACD,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACV,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;QAC7B,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;QACb,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;IACvB,CAAC;IACD,2EAA2E;IAC3E,MAAM,IAAI,SAAS,CACjB,6BAA6B,MAAM,CAAC,UAAU,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,IAAI;QACjF,4EAA4E,CAC/E,CAAA;AACH,CAAC;AAED,SAAS,IAAI,CAAC,CAAS,EAAE,KAAa;IACpC,IAAI,KAAK,GAAG,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,eAAe,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IACrF,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAE9B,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,CAAC;YACJ,OAAO,GAAG,CAAA;QACZ,KAAK,CAAC;YACJ,sEAAsE;YACtE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAA;QACjB,KAAK,CAAC,CAAC,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,SAAS;gBAAE,MAAM,IAAI,SAAS,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACjG,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACZ,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA;YACtD,CAAC,CAAC,MAAM,IAAI,GAAG,CAAA;YACf,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,SAAS;gBAAE,MAAM,IAAI,SAAS,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YACjG,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACZ,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAA;YACxG,CAAC,CAAC,MAAM,IAAI,GAAG,CAAA;YACf,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,WAAW;gBAAE,MAAM,IAAI,SAAS,CAAC,YAAY,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;YAC7F,MAAM,GAAG,GAAgB,EAAE,CAAA;YAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAA;YAC7D,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,WAAW;gBAAE,MAAM,IAAI,SAAS,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;YAC7F,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAA;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;gBAC9B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;oBACvD,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAA;gBACnE,CAAC;gBACD,sEAAsE;gBACtE,kEAAkE;gBAClE,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,MAAM,IAAI,SAAS,CAAC,qBAAqB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACzE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAA;YAClC,CAAC;YACD,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,CAAC;YACP,IAAI,GAAG,KAAK,EAAE;gBAAE,OAAO,KAAK,CAAA;YAC5B,IAAI,GAAG,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAA;YAC3B,IAAI,GAAG,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAA;YAC3B,MAAM,IAAI,SAAS,CAAC,4BAA4B,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAChE,CAAC;QACD;YACE,uEAAuE;YACvE,MAAM,IAAI,SAAS,CAAC,+BAA+B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACvE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,CAAC,GAAW,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACxB,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAA;IAChG,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AA0BD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAiB;IACtD,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAA;IAEpH,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACtC,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE,CAAE,CAAA;IACtB,MAAM,KAAK,GAAuB;QAChC,WAAW,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;QAC/B,YAAY,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;QAChC,sBAAsB,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1C,aAAa,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;KAClC,CAAA;IACD,MAAM,SAAS,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAEnF,IAAI,CAAC,KAAK,CAAC,sBAAsB;QAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;IAExE,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAA;IACnF,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IACrC,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,EAAE,CAAE,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAE,CAAA;IAC/C,wDAAwD;IACxD,IAAI,QAAQ,GAAG,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAA;IAC/G,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ;QAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAA;IACnF,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAA;IAE5C,MAAM,CAAC,GAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAA;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,IAAI,CAAC,CAAC,OAAO,YAAY,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAA;IAEjG,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,uDAAuD,CAAC,CAAA;IAChH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,CAAA;AAC5E,CAAC;AAED,kFAAkF;AAElF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAU,CAAA;AAG3D,MAAM,MAAM,GAAG,CAAC,KAAiB,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAQtF,SAAS,OAAO,CAAC,GAAoC,EAAE,KAAa,EAAE,IAAY;IAChF,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAA;IACtF,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,GAAoC;IAClE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACtB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAE,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9F,MAAM,IAAI,SAAS,CACjB,aAAa,MAAM,CAAC,KAAK,CAAC,2DAA2D,CACtF,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,KAAkB,CAAA;IAE9B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,IAAI,GAAG,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAA;QAC5D,6EAA6E;QAC7E,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAA;QACrE,OAAO;YACL,GAAG;YACH,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,EAAE;SAC3H,CAAA;IACH,CAAC;IAED,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,IAAI,GAAG,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAA;QAC5D,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAA;QACtE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,EAAE,EAAE,CAAA;IAChG,CAAC;IAED,IAAI,GAAG,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAA;IAC5D,OAAO;QACL,GAAG;QACH,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE;KACrG,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,GAAc,EAAa,EAAE;AAC9C,0EAA0E;AAC1E,+DAA+D;AAC/D,eAAe,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAiF,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;AAWhI;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,MAAsE;IAEtE,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAA;IAC/D,CAAC;IACD,MAAM,IAAI,GAAG,MAA6B,CAAA;IAC1C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;QAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAA;IAC3H,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC9E,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAA;IAChF,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CAAC,UAAU,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,oCAAoC,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,IAAkB,CAAA;AAC3B,CAAC;AAoBD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAwB;IACzD,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE;QACnC,IAAI,EAAE,iBAAiB;QACvB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,OAAO;KACtB,CAAC,CAAA;IAEF,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACvD,IAAI,CAAC,CAAC,WAAW,YAAY,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAA;IAClG,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACjD,IAAI,CAAC,CAAC,aAAa,YAAY,UAAU,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAA;IAE7G,MAAM,QAAQ,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAA;IACtD,MAAM,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAA;IACzE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAA;IAC9E,CAAC;IACD,4EAA4E;IAC5E,8EAA8E;IAC9E,2CAA2C;IAC3C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW;QAAE,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAA;IACpG,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAA;IAEpG,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpC,SAAS,EAAE,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;QACvD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY;QACzC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,MAAM;KACnC,CAAA;AACH,CAAC;AAoBD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,KAAqB;IACnD,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE;QACnC,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,OAAO;KACtB,CAAC,CAAA;IAEF,MAAM,QAAQ,GAAG,sBAAsB,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IAChE,MAAM,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAA;IACzE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAA;IACvE,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW;QAAE,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAA;IAEpG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;QACpC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,EAAE;KAC3D,CAAC,CAAA;IAEF,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACtC,MAAM,EAAE,GACN,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,2DAA2D;YAC3D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC;QACrD,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IACxE,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAA;IAE3F,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,IAAI,QAAQ,CAAC,SAAS,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CACjB,mCAAmC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI;YACnG,iDAAiD,CACpD,CAAA;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,CAAA;AACrF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nacre.work/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Nacre core: data model, permission resolver, and shared types",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@qdrant/js-client-rest": "^1.18.0",
|
|
31
|
+
"nodemailer": "^9.0.5",
|
|
31
32
|
"pg": "^8.22.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/node": "^26.1.2",
|
|
36
|
+
"@types/nodemailer": "^8.0.1",
|
|
35
37
|
"jose": "^6.2.7"
|
|
36
38
|
},
|
|
37
39
|
"keywords": [
|