@cauthyhq/node 1.0.0 → 1.2.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/CHANGELOG.md +71 -0
- package/README.md +81 -24
- package/dist/{cauthy-B6Sm3T8M.d.ts → cauthy-C7gZ-pbv.d.ts} +88 -3
- package/dist/{cauthy-ClP5orP4.d.mts → cauthy-CCSSj3Lg.d.mts} +88 -3
- package/dist/express.d.mts +2 -1
- package/dist/express.d.ts +2 -1
- package/dist/fastify.d.mts +2 -1
- package/dist/fastify.d.ts +2 -1
- package/dist/hono.d.mts +2 -1
- package/dist/hono.d.ts +2 -1
- package/dist/identity-BTJ8NB_w.d.mts +27 -0
- package/dist/identity-BTJ8NB_w.d.ts +27 -0
- package/dist/index.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +686 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +681 -66
- package/dist/index.mjs.map +1 -1
- package/dist/keys.js.map +1 -1
- package/dist/keys.mjs.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/mcp.mjs.map +1 -1
- package/dist/next.d.mts +2 -1
- package/dist/next.d.ts +2 -1
- package/dist/testing.d.mts +49 -1
- package/dist/testing.d.ts +49 -1
- package/dist/testing.js +488 -5
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +486 -4
- package/dist/testing.mjs.map +1 -1
- package/package.json +8 -3
package/dist/index.mjs
CHANGED
|
@@ -68,6 +68,14 @@ var CauthyConfigError = class extends Error {
|
|
|
68
68
|
this.name = "CauthyConfigError";
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
+
var CauthyKeyError = class extends CauthyConfigError {
|
|
72
|
+
constructor(reason, message) {
|
|
73
|
+
super(`CAUTHY_KEY: ${message} (${reason})`);
|
|
74
|
+
this.reason = reason;
|
|
75
|
+
this.name = "CauthyKeyError";
|
|
76
|
+
}
|
|
77
|
+
reason;
|
|
78
|
+
};
|
|
71
79
|
|
|
72
80
|
// src/url.ts
|
|
73
81
|
function withoutTrailingSlashes(s) {
|
|
@@ -978,6 +986,7 @@ function createSystem(given = {}) {
|
|
|
978
986
|
};
|
|
979
987
|
const tokenEndpoint = `${issuer}/v1/oauth/token`;
|
|
980
988
|
function configured() {
|
|
989
|
+
if (given.signer) return;
|
|
981
990
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
982
991
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
983
992
|
if (given.keys) {
|
|
@@ -990,7 +999,7 @@ function createSystem(given = {}) {
|
|
|
990
999
|
}
|
|
991
1000
|
let imported;
|
|
992
1001
|
const signingKey = () => imported ??= loadKey(key);
|
|
993
|
-
async function
|
|
1002
|
+
async function fail2(res, what) {
|
|
994
1003
|
const body = await res.json().catch(() => null);
|
|
995
1004
|
const first = body?.errors?.[0];
|
|
996
1005
|
const code = typeof body?.error === "string" ? body.error : first?.code;
|
|
@@ -1001,9 +1010,13 @@ function createSystem(given = {}) {
|
|
|
1001
1010
|
let asking;
|
|
1002
1011
|
async function ask() {
|
|
1003
1012
|
configured();
|
|
1004
|
-
const self = given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1013
|
+
const self = given.signer ? given.signer.clientId : given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1005
1014
|
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
|
|
1006
|
-
if (given.
|
|
1015
|
+
if (given.signer) {
|
|
1016
|
+
const s = await given.signer.signer;
|
|
1017
|
+
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
1018
|
+
form.set("client_assertion", await s.assertion(self, tokenEndpoint));
|
|
1019
|
+
} else if (given.keys || key) {
|
|
1007
1020
|
const signer = given.keys ? await given.keys.current() : { kid: key.keyId, privateKey: await signingKey() };
|
|
1008
1021
|
const assertion = await new SignJWT({}).setProtectedHeader({ alg: "RS256", kid: signer.kid }).setIssuer(self).setSubject(self).setAudience(tokenEndpoint).setJti(crypto.randomUUID()).setIssuedAt().setExpirationTime("2m").sign(signer.privateKey);
|
|
1009
1022
|
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
@@ -1012,7 +1025,7 @@ function createSystem(given = {}) {
|
|
|
1012
1025
|
form.set("client_secret", clientSecret);
|
|
1013
1026
|
}
|
|
1014
1027
|
const res = await doFetch(tokenEndpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
|
|
1015
|
-
if (!res.ok) await
|
|
1028
|
+
if (!res.ok) await fail2(res, "the application's own token");
|
|
1016
1029
|
const t = await res.json();
|
|
1017
1030
|
held = { value: t.access_token, until: Date.now() + t.expires_in * 1e3 - SKEW_MS };
|
|
1018
1031
|
return t.access_token;
|
|
@@ -1034,7 +1047,7 @@ function createSystem(given = {}) {
|
|
|
1034
1047
|
held = void 0;
|
|
1035
1048
|
res = await send();
|
|
1036
1049
|
}
|
|
1037
|
-
if (!res.ok) await
|
|
1050
|
+
if (!res.ok) await fail2(res, `${method} ${path}`);
|
|
1038
1051
|
return res.status === 204 ? void 0 : res.json();
|
|
1039
1052
|
}
|
|
1040
1053
|
const data = async (method, path, body, extra) => (await call(method, path, body, extra))?.data;
|
|
@@ -1108,7 +1121,7 @@ function createSystem(given = {}) {
|
|
|
1108
1121
|
}
|
|
1109
1122
|
},
|
|
1110
1123
|
async check(q) {
|
|
1111
|
-
const system = q.system ?? ownSystem;
|
|
1124
|
+
const system = q.system ?? (ownSystem || given.currentSystem?.() || "");
|
|
1112
1125
|
if (system === "") throw new CauthyConfigError("name the system to check in (system, or CAUTHY_SYSTEM)");
|
|
1113
1126
|
return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
|
|
1114
1127
|
},
|
|
@@ -1278,6 +1291,219 @@ function firstProblem(...checks) {
|
|
|
1278
1291
|
|
|
1279
1292
|
// src/webhook.ts
|
|
1280
1293
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
1294
|
+
|
|
1295
|
+
// src/sha256.ts
|
|
1296
|
+
var K = new Uint32Array([
|
|
1297
|
+
1116352408,
|
|
1298
|
+
1899447441,
|
|
1299
|
+
3049323471,
|
|
1300
|
+
3921009573,
|
|
1301
|
+
961987163,
|
|
1302
|
+
1508970993,
|
|
1303
|
+
2453635748,
|
|
1304
|
+
2870763221,
|
|
1305
|
+
3624381080,
|
|
1306
|
+
310598401,
|
|
1307
|
+
607225278,
|
|
1308
|
+
1426881987,
|
|
1309
|
+
1925078388,
|
|
1310
|
+
2162078206,
|
|
1311
|
+
2614888103,
|
|
1312
|
+
3248222580,
|
|
1313
|
+
3835390401,
|
|
1314
|
+
4022224774,
|
|
1315
|
+
264347078,
|
|
1316
|
+
604807628,
|
|
1317
|
+
770255983,
|
|
1318
|
+
1249150122,
|
|
1319
|
+
1555081692,
|
|
1320
|
+
1996064986,
|
|
1321
|
+
2554220882,
|
|
1322
|
+
2821834349,
|
|
1323
|
+
2952996808,
|
|
1324
|
+
3210313671,
|
|
1325
|
+
3336571891,
|
|
1326
|
+
3584528711,
|
|
1327
|
+
113926993,
|
|
1328
|
+
338241895,
|
|
1329
|
+
666307205,
|
|
1330
|
+
773529912,
|
|
1331
|
+
1294757372,
|
|
1332
|
+
1396182291,
|
|
1333
|
+
1695183700,
|
|
1334
|
+
1986661051,
|
|
1335
|
+
2177026350,
|
|
1336
|
+
2456956037,
|
|
1337
|
+
2730485921,
|
|
1338
|
+
2820302411,
|
|
1339
|
+
3259730800,
|
|
1340
|
+
3345764771,
|
|
1341
|
+
3516065817,
|
|
1342
|
+
3600352804,
|
|
1343
|
+
4094571909,
|
|
1344
|
+
275423344,
|
|
1345
|
+
430227734,
|
|
1346
|
+
506948616,
|
|
1347
|
+
659060556,
|
|
1348
|
+
883997877,
|
|
1349
|
+
958139571,
|
|
1350
|
+
1322822218,
|
|
1351
|
+
1537002063,
|
|
1352
|
+
1747873779,
|
|
1353
|
+
1955562222,
|
|
1354
|
+
2024104815,
|
|
1355
|
+
2227730452,
|
|
1356
|
+
2361852424,
|
|
1357
|
+
2428436474,
|
|
1358
|
+
2756734187,
|
|
1359
|
+
3204031479,
|
|
1360
|
+
3329325298
|
|
1361
|
+
]);
|
|
1362
|
+
function sha256(data) {
|
|
1363
|
+
const h = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
1364
|
+
const len = data.length;
|
|
1365
|
+
const padded = new Uint8Array(len + 9 + 63 >> 6 << 6);
|
|
1366
|
+
padded.set(data);
|
|
1367
|
+
padded[len] = 128;
|
|
1368
|
+
const view = new DataView(padded.buffer);
|
|
1369
|
+
view.setUint32(padded.length - 8, Math.floor(len / 536870912));
|
|
1370
|
+
view.setUint32(padded.length - 4, len << 3 >>> 0);
|
|
1371
|
+
const w = new Uint32Array(64);
|
|
1372
|
+
const rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1373
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
1374
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
|
|
1375
|
+
for (let i = 16; i < 64; i++) {
|
|
1376
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
1377
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
1378
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
1379
|
+
}
|
|
1380
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
1381
|
+
for (let i = 0; i < 64; i++) {
|
|
1382
|
+
const t1 = hh + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[i] + w[i] >>> 0;
|
|
1383
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
1384
|
+
hh = g;
|
|
1385
|
+
g = f;
|
|
1386
|
+
f = e;
|
|
1387
|
+
e = d + t1 >>> 0;
|
|
1388
|
+
d = c;
|
|
1389
|
+
c = b;
|
|
1390
|
+
b = a;
|
|
1391
|
+
a = t1 + t2 >>> 0;
|
|
1392
|
+
}
|
|
1393
|
+
h[0] += a;
|
|
1394
|
+
h[1] += b;
|
|
1395
|
+
h[2] += c;
|
|
1396
|
+
h[3] += d;
|
|
1397
|
+
h[4] += e;
|
|
1398
|
+
h[5] += f;
|
|
1399
|
+
h[6] += g;
|
|
1400
|
+
h[7] += hh;
|
|
1401
|
+
}
|
|
1402
|
+
const out2 = new Uint8Array(32);
|
|
1403
|
+
const ov = new DataView(out2.buffer);
|
|
1404
|
+
for (let i = 0; i < 8; i++) ov.setUint32(i * 4, h[i]);
|
|
1405
|
+
return out2;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/cauthykey.ts
|
|
1409
|
+
var KEY_PREFIX = "cauthy1";
|
|
1410
|
+
var KEY_REPLACES = [
|
|
1411
|
+
"CAUTHY_ISSUER",
|
|
1412
|
+
"CAUTHY_CLIENT_ID",
|
|
1413
|
+
"CAUTHY_CLIENT_SECRET",
|
|
1414
|
+
"CAUTHY_SYSTEM",
|
|
1415
|
+
"CAUTHY_TENANT",
|
|
1416
|
+
"CAUTHY_SYSTEM_KEY",
|
|
1417
|
+
"CAUTHY_SYSTEM_KEY_ID",
|
|
1418
|
+
"CAUTHY_BASE_URL",
|
|
1419
|
+
"CAUTHY_AUDIENCE",
|
|
1420
|
+
"CAUTHY_TENANT_ID"
|
|
1421
|
+
];
|
|
1422
|
+
function refuseBesideKey(environment) {
|
|
1423
|
+
const set = KEY_REPLACES.filter((n) => (environment?.[n] ?? "") !== "");
|
|
1424
|
+
if (set.length > 0) {
|
|
1425
|
+
throw new CauthyConfigError(`the application runs on CAUTHY_KEY, and ${set.join(", ")} ${set.length === 1 ? "is" : "are"} also set: remove ${set.length === 1 ? "it" : "them"} from the environment. Everything comes from the key.`);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
var MAX_LEN = 512;
|
|
1429
|
+
var enc = new TextEncoder();
|
|
1430
|
+
var dec = new TextDecoder("utf-8", { fatal: true });
|
|
1431
|
+
function b64uEncode(b) {
|
|
1432
|
+
let bin = "";
|
|
1433
|
+
for (const x of b) bin += String.fromCharCode(x);
|
|
1434
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1435
|
+
}
|
|
1436
|
+
function b64uDecode(s) {
|
|
1437
|
+
if (!/^[A-Za-z0-9_-]*$/.test(s) || s.length % 4 === 1) return null;
|
|
1438
|
+
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4));
|
|
1439
|
+
const out2 = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
1440
|
+
return b64uEncode(out2) === s ? out2 : null;
|
|
1441
|
+
}
|
|
1442
|
+
function checksum(body) {
|
|
1443
|
+
return b64uEncode(sha256(enc.encode(body)).slice(0, 6));
|
|
1444
|
+
}
|
|
1445
|
+
function printableAscii(s) {
|
|
1446
|
+
return /^[\x21-\x7e]*$/.test(s);
|
|
1447
|
+
}
|
|
1448
|
+
function validIssuer(s) {
|
|
1449
|
+
if (s === "" || !printableAscii(s)) return false;
|
|
1450
|
+
let u;
|
|
1451
|
+
try {
|
|
1452
|
+
u = new URL(s);
|
|
1453
|
+
} catch {
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
if (u.origin !== s) return false;
|
|
1457
|
+
if (/^\[::ffff:/i.test(u.host)) return false;
|
|
1458
|
+
if (u.protocol === "https:") return true;
|
|
1459
|
+
return u.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname);
|
|
1460
|
+
}
|
|
1461
|
+
var fail = (reason, message) => {
|
|
1462
|
+
throw new CauthyKeyError(reason, message);
|
|
1463
|
+
};
|
|
1464
|
+
var NOT_A_KEY = "not a Cauthy key (copy it again from the dashboard)";
|
|
1465
|
+
function parseKey(text2) {
|
|
1466
|
+
const s = text2.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
|
|
1467
|
+
if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
|
|
1468
|
+
const head = s.split(".", 1)[0];
|
|
1469
|
+
if (head !== KEY_PREFIX) {
|
|
1470
|
+
if (/^cauthy[0-9]+$/.test(head)) fail("key_version", `this key is for a newer SDK (${head}): update @cauthyhq/node`);
|
|
1471
|
+
fail("key_format", NOT_A_KEY);
|
|
1472
|
+
}
|
|
1473
|
+
const parts = s.split(".");
|
|
1474
|
+
if (parts.length !== 5 || parts.some((p) => p === "")) fail("key_format", NOT_A_KEY);
|
|
1475
|
+
if (!constantTimeEqual(checksum(parts.slice(0, 4).join(".")), parts[4])) {
|
|
1476
|
+
fail("key_checksum", "the key is damaged (part of it is missing or changed): copy it again from the dashboard");
|
|
1477
|
+
}
|
|
1478
|
+
const issuerBytes = b64uDecode(parts[1]);
|
|
1479
|
+
let issuer = "";
|
|
1480
|
+
try {
|
|
1481
|
+
issuer = issuerBytes ? dec.decode(issuerBytes) : "";
|
|
1482
|
+
} catch {
|
|
1483
|
+
issuer = "";
|
|
1484
|
+
}
|
|
1485
|
+
if (!validIssuer(issuer)) fail("key_issuer", "the key names no valid Cauthy address");
|
|
1486
|
+
const clientBytes = b64uDecode(parts[2]);
|
|
1487
|
+
const clientId = clientBytes ? String.fromCharCode(...clientBytes) : "";
|
|
1488
|
+
if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) fail("key_client", "the key names no valid client");
|
|
1489
|
+
const secret = b64uDecode(parts[3]);
|
|
1490
|
+
if (parts[3].length !== 43 || !secret || secret.length !== 32) fail("key_secret", "the key's secret is not 32 bytes");
|
|
1491
|
+
return { issuer, clientId, secret: parts[3] };
|
|
1492
|
+
}
|
|
1493
|
+
function composeKey(issuer, clientId, secret) {
|
|
1494
|
+
const body = `${KEY_PREFIX}.${b64uEncode(enc.encode(issuer))}.${b64uEncode(enc.encode(clientId))}.${secret}`;
|
|
1495
|
+
const key = `${body}.${checksum(body)}`;
|
|
1496
|
+
parseKey(key);
|
|
1497
|
+
return key;
|
|
1498
|
+
}
|
|
1499
|
+
function constantTimeEqual(a, b) {
|
|
1500
|
+
if (a.length !== b.length) return false;
|
|
1501
|
+
let d = 0;
|
|
1502
|
+
for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1503
|
+
return d === 0;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// src/webhook.ts
|
|
1281
1507
|
var CauthyWebhookError = class extends Error {
|
|
1282
1508
|
constructor(reason) {
|
|
1283
1509
|
super(`@cauthyhq/node: webhook refused: ${reason}`);
|
|
@@ -1312,7 +1538,8 @@ function keysOf(issuer) {
|
|
|
1312
1538
|
}
|
|
1313
1539
|
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
1314
1540
|
async function verifyWebhook(request, options = {}) {
|
|
1315
|
-
const
|
|
1541
|
+
const key = typeof process !== "undefined" ? process.env?.CAUTHY_KEY : void 0;
|
|
1542
|
+
const env4 = key ? parseKey(key).issuer : typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
1316
1543
|
const issuer = withoutTrailingSlashes(options.issuer ?? env4 ?? "");
|
|
1317
1544
|
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
1318
1545
|
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
@@ -1441,7 +1668,7 @@ var LIMIT = 2n ** 53n;
|
|
|
1441
1668
|
function parseStrict(text2, integerMembers = []) {
|
|
1442
1669
|
let i = 0;
|
|
1443
1670
|
let depth = 0;
|
|
1444
|
-
const
|
|
1671
|
+
const fail2 = (what) => {
|
|
1445
1672
|
throw new SyntaxError(`${what} at ${i}`);
|
|
1446
1673
|
};
|
|
1447
1674
|
const skip = () => {
|
|
@@ -1460,19 +1687,19 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1460
1687
|
return v;
|
|
1461
1688
|
}
|
|
1462
1689
|
}
|
|
1463
|
-
return
|
|
1690
|
+
return fail2("unexpected character");
|
|
1464
1691
|
};
|
|
1465
1692
|
const string = () => {
|
|
1466
1693
|
const start = i;
|
|
1467
1694
|
i++;
|
|
1468
1695
|
while (i < text2.length && text2[i] !== '"') i += text2[i] === "\\" ? 2 : 1;
|
|
1469
|
-
if (text2[i] !== '"')
|
|
1696
|
+
if (text2[i] !== '"') fail2("unterminated string");
|
|
1470
1697
|
i++;
|
|
1471
1698
|
return JSON.parse(text2.slice(start, i));
|
|
1472
1699
|
};
|
|
1473
1700
|
const number = (integer) => {
|
|
1474
1701
|
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text2.slice(i));
|
|
1475
|
-
if (!m) return
|
|
1702
|
+
if (!m) return fail2("bad number");
|
|
1476
1703
|
i += m[0].length;
|
|
1477
1704
|
if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
|
|
1478
1705
|
if (m[2] === void 0 && m[3] === void 0) {
|
|
@@ -1499,7 +1726,7 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1499
1726
|
i++;
|
|
1500
1727
|
depth--;
|
|
1501
1728
|
return out2;
|
|
1502
|
-
} else
|
|
1729
|
+
} else fail2("expected , or ]");
|
|
1503
1730
|
}
|
|
1504
1731
|
};
|
|
1505
1732
|
const object = () => {
|
|
@@ -1515,12 +1742,12 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1515
1742
|
}
|
|
1516
1743
|
for (; ; ) {
|
|
1517
1744
|
skip();
|
|
1518
|
-
if (text2[i] !== '"')
|
|
1745
|
+
if (text2[i] !== '"') fail2("expected a name");
|
|
1519
1746
|
const name = string();
|
|
1520
|
-
if (seen.has(name))
|
|
1747
|
+
if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
|
|
1521
1748
|
seen.add(name);
|
|
1522
1749
|
skip();
|
|
1523
|
-
if (text2[i] !== ":")
|
|
1750
|
+
if (text2[i] !== ":") fail2("expected :");
|
|
1524
1751
|
i++;
|
|
1525
1752
|
Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
|
|
1526
1753
|
skip();
|
|
@@ -1529,15 +1756,175 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1529
1756
|
i++;
|
|
1530
1757
|
depth--;
|
|
1531
1758
|
return out2;
|
|
1532
|
-
} else
|
|
1759
|
+
} else fail2("expected , or }");
|
|
1533
1760
|
}
|
|
1534
1761
|
};
|
|
1535
1762
|
const result = value();
|
|
1536
1763
|
skip();
|
|
1537
|
-
if (i < text2.length)
|
|
1764
|
+
if (i < text2.length) fail2("trailing characters");
|
|
1538
1765
|
return result;
|
|
1539
1766
|
}
|
|
1540
1767
|
|
|
1768
|
+
// src/deprecate.ts
|
|
1769
|
+
var told = /* @__PURE__ */ new Set();
|
|
1770
|
+
function deprecated(what, instead) {
|
|
1771
|
+
if (told.has(what)) return;
|
|
1772
|
+
told.add(what);
|
|
1773
|
+
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1774
|
+
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1775
|
+
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1776
|
+
} else {
|
|
1777
|
+
console.warn(message);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// src/keyderive.ts
|
|
1782
|
+
var enc2 = new TextEncoder();
|
|
1783
|
+
var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
|
1784
|
+
var ASSERTION_TYPE2 = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
1785
|
+
async function seedOf(secret) {
|
|
1786
|
+
const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
|
|
1787
|
+
return new Uint8Array(await crypto.subtle.deriveBits(
|
|
1788
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: enc2.encode("cauthy/sign/v1") },
|
|
1789
|
+
ikm,
|
|
1790
|
+
256
|
|
1791
|
+
));
|
|
1792
|
+
}
|
|
1793
|
+
async function thumbprint(x) {
|
|
1794
|
+
return b64uEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", enc2.encode(`{"crv":"Ed25519","kty":"OKP","x":"${x}"}`))));
|
|
1795
|
+
}
|
|
1796
|
+
async function signerOf(secret) {
|
|
1797
|
+
const seed = await seedOf(secret);
|
|
1798
|
+
const der = new Uint8Array(PKCS8_ED25519.length + 32);
|
|
1799
|
+
der.set(PKCS8_ED25519);
|
|
1800
|
+
der.set(seed, PKCS8_ED25519.length);
|
|
1801
|
+
const readable = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, true, ["sign"]);
|
|
1802
|
+
const { x } = await crypto.subtle.exportKey("jwk", readable);
|
|
1803
|
+
const signing = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, false, ["sign"]);
|
|
1804
|
+
der.fill(0);
|
|
1805
|
+
seed.fill(0);
|
|
1806
|
+
const publicJwk = { kty: "OKP", crv: "Ed25519", x };
|
|
1807
|
+
const kid = await thumbprint(publicJwk.x);
|
|
1808
|
+
const header = b64uEncode(enc2.encode(JSON.stringify({ alg: "EdDSA", kid })));
|
|
1809
|
+
return {
|
|
1810
|
+
kid,
|
|
1811
|
+
publicJwk,
|
|
1812
|
+
async assertion(clientId, tokenEndpoint, jti = crypto.randomUUID(), iat = Math.floor(Date.now() / 1e3)) {
|
|
1813
|
+
const claims = { iss: clientId, sub: clientId, aud: tokenEndpoint, jti, iat, exp: iat + 120 };
|
|
1814
|
+
const input = `${header}.${b64uEncode(enc2.encode(JSON.stringify(claims)))}`;
|
|
1815
|
+
const sig = new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, signing, enc2.encode(input)));
|
|
1816
|
+
return `${input}.${b64uEncode(sig)}`;
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
function deriveFromKey(key, previous) {
|
|
1821
|
+
const other = previous && previous.secret !== key.secret ? previous : void 0;
|
|
1822
|
+
const rings = deriveKeys(key.secret, other?.secret);
|
|
1823
|
+
return {
|
|
1824
|
+
cookie: rings.cookie,
|
|
1825
|
+
flow: rings.flow,
|
|
1826
|
+
signer: signerOf(key.secret),
|
|
1827
|
+
...other ? { previousJwk: signerOf(other.secret).then((s) => ({ ...s.publicJwk, kid: s.kid })) } : {}
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// src/identity.ts
|
|
1832
|
+
var REVALIDATE_MS = 3e5;
|
|
1833
|
+
var sleep = (ms) => new Promise((resolve2) => {
|
|
1834
|
+
const t = setTimeout(resolve2, ms);
|
|
1835
|
+
t.unref?.();
|
|
1836
|
+
});
|
|
1837
|
+
function createIdentity(o) {
|
|
1838
|
+
const doFetch = o.fetch ?? globalThis.fetch;
|
|
1839
|
+
const now = o.now ?? Date.now;
|
|
1840
|
+
const retry = o.retryMs ?? [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
1841
|
+
let held;
|
|
1842
|
+
let etag;
|
|
1843
|
+
let checkedAt = 0;
|
|
1844
|
+
let first;
|
|
1845
|
+
let refused;
|
|
1846
|
+
let refreshing;
|
|
1847
|
+
let failed = false;
|
|
1848
|
+
let attemptEnded;
|
|
1849
|
+
let attemptOut;
|
|
1850
|
+
const kid = () => (typeof o.kid === "function" ? o.kid() : o.kid) ?? "?";
|
|
1851
|
+
async function ask() {
|
|
1852
|
+
const headers = { authorization: `Bearer ${await o.token()}`, accept: "application/json" };
|
|
1853
|
+
if (etag && held) headers["if-none-match"] = etag;
|
|
1854
|
+
const res = await doFetch(`${o.issuer}/v1/connect/self`, { headers });
|
|
1855
|
+
if (res.status === 304 && held) return "same";
|
|
1856
|
+
if (res.status === 410) throw Object.assign(new Error("client_inactive"), { code: "client_inactive" });
|
|
1857
|
+
if (!res.ok) throw new CauthyApiError(503, "unavailable", `Cauthy answered ${res.status} to /v1/connect/self`);
|
|
1858
|
+
const body = await res.json();
|
|
1859
|
+
if (!body?.data || typeof body.data.client_id !== "string") throw new CauthyApiError(503, "unavailable", "Cauthy's answer to /v1/connect/self has no identity");
|
|
1860
|
+
etag = res.headers.get("etag") ?? void 0;
|
|
1861
|
+
return body.data;
|
|
1862
|
+
}
|
|
1863
|
+
function refusalOf(e) {
|
|
1864
|
+
const code = e?.code;
|
|
1865
|
+
if (code !== "invalid_client" && code !== "client_inactive") return void 0;
|
|
1866
|
+
const message = code === "client_inactive" ? `Cauthy says this application's client is no longer active (kid ${kid()}). Connect it again in Cauthy's dashboard.` : `Cauthy refuses this application's key (kid ${kid()}): it was revoked or never registered. Make a new key in Cauthy's dashboard.`;
|
|
1867
|
+
return new CauthyApiError(503, "misconfigured", message);
|
|
1868
|
+
}
|
|
1869
|
+
function refuse(err) {
|
|
1870
|
+
if (refused === void 0) o.log?.(err.message.replace(/^@cauthyhq\/node: /, ""));
|
|
1871
|
+
refused = err;
|
|
1872
|
+
checkedAt = now();
|
|
1873
|
+
return err;
|
|
1874
|
+
}
|
|
1875
|
+
async function firstAnswer() {
|
|
1876
|
+
for (let attempt = 0; ; attempt++) {
|
|
1877
|
+
attemptOut = new Promise((resolve2) => {
|
|
1878
|
+
attemptEnded = resolve2;
|
|
1879
|
+
});
|
|
1880
|
+
try {
|
|
1881
|
+
const got = await ask();
|
|
1882
|
+
if (got !== "same") held = got;
|
|
1883
|
+
checkedAt = now();
|
|
1884
|
+
failed = false;
|
|
1885
|
+
attemptEnded?.(true);
|
|
1886
|
+
return held;
|
|
1887
|
+
} catch (e) {
|
|
1888
|
+
const r = refusalOf(e);
|
|
1889
|
+
failed = true;
|
|
1890
|
+
attemptEnded?.(false);
|
|
1891
|
+
if (r) throw refuse(r);
|
|
1892
|
+
await sleep(retry[Math.min(attempt, retry.length - 1)]);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
async function refresh() {
|
|
1897
|
+
checkedAt = now();
|
|
1898
|
+
try {
|
|
1899
|
+
const got = await ask();
|
|
1900
|
+
if (got !== "same") held = got;
|
|
1901
|
+
refused = void 0;
|
|
1902
|
+
} catch (e) {
|
|
1903
|
+
const r = refusalOf(e);
|
|
1904
|
+
if (r) refuse(r);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
return {
|
|
1908
|
+
ready() {
|
|
1909
|
+
if ((held || refused) && now() - checkedAt >= REVALIDATE_MS) {
|
|
1910
|
+
refreshing ??= refresh().finally(() => {
|
|
1911
|
+
refreshing = void 0;
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
if (refused) return Promise.reject(refused);
|
|
1915
|
+
if (held) return Promise.resolve(held);
|
|
1916
|
+
first ??= firstAnswer();
|
|
1917
|
+
return first;
|
|
1918
|
+
},
|
|
1919
|
+
current: () => held,
|
|
1920
|
+
refresh: () => refreshing ??= refresh().finally(() => {
|
|
1921
|
+
refreshing = void 0;
|
|
1922
|
+
}),
|
|
1923
|
+
failing: () => held === void 0 && failed && refused === void 0,
|
|
1924
|
+
attempt: () => held !== void 0 ? Promise.resolve(true) : attemptOut ?? Promise.resolve(false)
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1541
1928
|
// src/verify.ts
|
|
1542
1929
|
var CauthyUnavailableError = class extends Error {
|
|
1543
1930
|
constructor(detail) {
|
|
@@ -1680,6 +2067,10 @@ function decodeJson(segment) {
|
|
|
1680
2067
|
return parseStrict(Buffer.from(padded, "base64").toString("utf8"));
|
|
1681
2068
|
}
|
|
1682
2069
|
function createTokenVerifier(options = {}) {
|
|
2070
|
+
const fromEnv = ["CAUTHY_ISSUER", "CAUTHY_AUDIENCE", "CAUTHY_TENANT_ID"].filter((name, i) => [options.issuer, options.audience, options.tenantId][i] === void 0 && (env2(name) ?? "") !== "");
|
|
2071
|
+
if (fromEnv.length > 0) {
|
|
2072
|
+
deprecated("Reading CAUTHY_ISSUER, CAUTHY_AUDIENCE and CAUTHY_TENANT_ID to verify tokens", "Set CAUTHY_KEY and use createVerifierFromEnv().");
|
|
2073
|
+
}
|
|
1683
2074
|
const issuerGiven = options.issuer ?? env2("CAUTHY_ISSUER") ?? "";
|
|
1684
2075
|
if (issuerGiven === "") throw new CauthyConfigError("issuer (CAUTHY_ISSUER) is not set");
|
|
1685
2076
|
const issuer = issuerAddress(issuerGiven, "issuer");
|
|
@@ -1745,6 +2136,7 @@ function createTokenVerifier(options = {}) {
|
|
|
1745
2136
|
if (claims.roles !== void 0 && claims.roles !== null && !(Array.isArray(claims.roles) && claims.roles.every((r) => typeof r === "string"))) bad("roles");
|
|
1746
2137
|
if (claims.email !== void 0 && typeof claims.email !== "string") bad("email");
|
|
1747
2138
|
for (const name of ["email_verified", "anon"]) if (claims[name] !== void 0 && typeof claims[name] !== "boolean") bad(name);
|
|
2139
|
+
for (const name of ["token_use", "azp"]) if (claims[name] !== void 0 && typeof claims[name] !== "string") bad(name);
|
|
1748
2140
|
if (!issuers.includes(claims.iss)) throw new CauthyTokenError("wrong_issuer");
|
|
1749
2141
|
if (!(Array.isArray(aud) ? aud.includes(audience) : aud === audience)) throw new CauthyTokenError("wrong_audience");
|
|
1750
2142
|
const now = (options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
@@ -1753,7 +2145,9 @@ function createTokenVerifier(options = {}) {
|
|
|
1753
2145
|
if (now < claims.iat - leeway) throw new CauthyTokenError("issued_in_future");
|
|
1754
2146
|
if (claims.tid !== tenant) throw new CauthyTokenError("wrong_tenant");
|
|
1755
2147
|
if ("sys" in claims && !options.allowSystem) throw new CauthyTokenError("system_token");
|
|
2148
|
+
if ("token_use" in claims && !options.allowClientTokens) throw new CauthyTokenError("client_token");
|
|
1756
2149
|
if (claims.anon === true && !options.allowAnonymous) throw new CauthyTokenError("anonymous_token");
|
|
2150
|
+
if (options.clientId && typeof claims.azp === "string" && claims.azp !== "" && claims.azp !== options.clientId) throw new CauthyTokenError("wrong_client");
|
|
1757
2151
|
return {
|
|
1758
2152
|
userId: claims.sub,
|
|
1759
2153
|
tenantId: claims.tid,
|
|
@@ -1762,6 +2156,8 @@ function createTokenVerifier(options = {}) {
|
|
|
1762
2156
|
roles: Array.isArray(claims.roles) ? claims.roles : [],
|
|
1763
2157
|
...typeof claims.sys === "string" && claims.sys !== "" ? { system: claims.sys } : {},
|
|
1764
2158
|
...claims.anon === true ? { anonymous: true } : {},
|
|
2159
|
+
...typeof claims.azp === "string" && claims.azp !== "" ? { authorizedParty: claims.azp } : {},
|
|
2160
|
+
...typeof claims.client_id === "string" && claims.client_id !== "" ? { clientId: claims.client_id } : {},
|
|
1765
2161
|
expiresAt: new Date(claims.exp * 1e3).toISOString(),
|
|
1766
2162
|
token
|
|
1767
2163
|
};
|
|
@@ -1770,18 +2166,18 @@ function createTokenVerifier(options = {}) {
|
|
|
1770
2166
|
async function verifyToken(from, options = {}) {
|
|
1771
2167
|
return createTokenVerifier(options)(from);
|
|
1772
2168
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1780
|
-
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1781
|
-
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1782
|
-
} else {
|
|
1783
|
-
console.warn(message);
|
|
2169
|
+
async function createVerifierFromEnv(options = {}) {
|
|
2170
|
+
const text2 = options.key ?? env2("CAUTHY_KEY");
|
|
2171
|
+
if (text2 === void 0 || text2 === "") return createTokenVerifier(options);
|
|
2172
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2173
|
+
for (const name of ["issuer", "audience", "tenantId"]) {
|
|
2174
|
+
if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
1784
2175
|
}
|
|
2176
|
+
const key = parseKey(text2);
|
|
2177
|
+
const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
|
|
2178
|
+
const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
|
|
2179
|
+
const { key: _, ...rest } = options;
|
|
2180
|
+
return createTokenVerifier({ ...rest, issuer: id.issuer, audience: id.audience, tenantId: id.tenant.id });
|
|
1785
2181
|
}
|
|
1786
2182
|
|
|
1787
2183
|
// src/mount.ts
|
|
@@ -1874,6 +2270,63 @@ function redirect(location, cookies = []) {
|
|
|
1874
2270
|
function conditionalOf(c) {
|
|
1875
2271
|
return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
|
|
1876
2272
|
}
|
|
2273
|
+
function byKey(given) {
|
|
2274
|
+
const text2 = given.key ?? env3("CAUTHY_KEY");
|
|
2275
|
+
if (text2 === void 0 || text2 === "") return void 0;
|
|
2276
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2277
|
+
for (const [name, value] of [
|
|
2278
|
+
["issuer", given.issuer],
|
|
2279
|
+
["clientId", given.clientId],
|
|
2280
|
+
["clientSecret", given.clientSecret],
|
|
2281
|
+
["secret", given.secret],
|
|
2282
|
+
["system", given.system],
|
|
2283
|
+
["tenant", given.tenant],
|
|
2284
|
+
["audience", given.audience],
|
|
2285
|
+
["tenantId", given.tenantId],
|
|
2286
|
+
["systemKey", given.systemKey],
|
|
2287
|
+
["rootKey", given.rootKey]
|
|
2288
|
+
]) {
|
|
2289
|
+
if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
2290
|
+
}
|
|
2291
|
+
const key = parseKey(text2);
|
|
2292
|
+
const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
|
|
2293
|
+
let previous;
|
|
2294
|
+
if (prevText) {
|
|
2295
|
+
try {
|
|
2296
|
+
previous = parseKey(prevText);
|
|
2297
|
+
} catch (e) {
|
|
2298
|
+
if (e instanceof CauthyKeyError) throw new CauthyKeyError(e.reason, `CAUTHY_KEY_PREVIOUS is refused: ${e.message.replace(/^@cauthyhq\/node: CAUTHY_KEY: /, "").replace(/ \(key_[a-z_]+\)$/, "")}`);
|
|
2299
|
+
throw e;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
if (previous && (previous.issuer !== key.issuer || previous.clientId !== key.clientId)) {
|
|
2303
|
+
throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
|
|
2304
|
+
}
|
|
2305
|
+
const legacySecret = env3("CAUTHY_SECRET") || void 0;
|
|
2306
|
+
const legacyRoot = env3("PACT_ROOT_KEY") || void 0;
|
|
2307
|
+
if (legacySecret) deprecated("CAUTHY_SECRET beside CAUTHY_KEY", "It is read only for sessions made before the move: remove it after the rotation window.");
|
|
2308
|
+
if (legacyRoot) deprecated("PACT_ROOT_KEY beside CAUTHY_KEY", "It is read only for sessions made before the move: remove it, the keys option and its table after the rotation window.");
|
|
2309
|
+
if (given.keys && !(legacyRoot && given.contract)) {
|
|
2310
|
+
throw new CauthyConfigError("with CAUTHY_KEY the application keeps no key store: remove keys (it is read only beside PACT_ROOT_KEY and a contract, while moving)");
|
|
2311
|
+
}
|
|
2312
|
+
return { key, previous, legacySecret, legacyRoot };
|
|
2313
|
+
}
|
|
2314
|
+
var UnknownOriginError = class extends Error {
|
|
2315
|
+
constructor(origin, registered) {
|
|
2316
|
+
super(`${origin} is not one of the application's addresses in Cauthy (${registered.join(", ") || "none"}): add it on the application's card, or set baseURL`);
|
|
2317
|
+
this.origin = origin;
|
|
2318
|
+
this.registered = registered;
|
|
2319
|
+
}
|
|
2320
|
+
origin;
|
|
2321
|
+
registered;
|
|
2322
|
+
};
|
|
2323
|
+
function contractOnKey(given, keyed) {
|
|
2324
|
+
const contract = readContract(given.contract);
|
|
2325
|
+
if (contract.issuer !== keyed.key.issuer) {
|
|
2326
|
+
throw new CauthyKeyError("key_contract_issuer", `the key names ${keyed.key.issuer}, and the contract's Cauthy is ${contract.issuer}: make the key in that Cauthy's dashboard`);
|
|
2327
|
+
}
|
|
2328
|
+
return contract;
|
|
2329
|
+
}
|
|
1877
2330
|
function byContract(given) {
|
|
1878
2331
|
failClosed(typeof process !== "undefined" ? process.env : void 0);
|
|
1879
2332
|
for (const [name, value] of [
|
|
@@ -1892,27 +2345,48 @@ function byContract(given) {
|
|
|
1892
2345
|
if (contract.serviceClientId && !given.keys) {
|
|
1893
2346
|
throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
|
|
1894
2347
|
}
|
|
2348
|
+
deprecated("PACT_ROOT_KEY", "Make the application's CAUTHY_KEY in Cauthy's dashboard (the application's card), then remove PACT_ROOT_KEY, the keys option and its table.");
|
|
1895
2349
|
return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
|
|
1896
2350
|
}
|
|
1897
2351
|
var toldOfHeader = false;
|
|
1898
2352
|
function createCauthy(given = {}) {
|
|
1899
|
-
const
|
|
1900
|
-
const
|
|
1901
|
-
const
|
|
1902
|
-
const
|
|
1903
|
-
const
|
|
1904
|
-
const
|
|
1905
|
-
const
|
|
1906
|
-
const
|
|
2353
|
+
const keyed = byKey(given);
|
|
2354
|
+
const pacted = given.contract && !keyed ? byContract(given) : void 0;
|
|
2355
|
+
const contract = keyed && given.contract ? contractOnKey(given, keyed) : pacted?.contract;
|
|
2356
|
+
const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? contract?.issuer ?? env3("CAUTHY_ISSUER") ?? "");
|
|
2357
|
+
const clientId = contract?.clientId ?? keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
|
|
2358
|
+
const clientSecret = contract || keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
|
|
2359
|
+
const secret = contract || keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
|
|
2360
|
+
const askSelf = keyed !== void 0 && contract === void 0;
|
|
2361
|
+
const system = askSelf ? "" : contract?.system ?? given.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
2362
|
+
const tenant = askSelf ? "" : contract?.tenant ?? given.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
1907
2363
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
1908
2364
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
1909
|
-
if (!contract && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2365
|
+
if (!contract && !keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2366
|
+
const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
|
|
2367
|
+
let signingKid;
|
|
2368
|
+
fromKey?.signer.then((s) => {
|
|
2369
|
+
signingKid = s.kid;
|
|
2370
|
+
}, () => void 0);
|
|
1910
2371
|
const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
|
|
1911
|
-
const single = derived ? void 0 : keyOf(secret).then((k) => [k]);
|
|
1912
|
-
const
|
|
1913
|
-
const
|
|
1914
|
-
const
|
|
1915
|
-
|
|
2372
|
+
const single = derived || fromKey ? void 0 : keyOf(secret).then((k) => [k]);
|
|
2373
|
+
const legacyRings = keyed?.legacyRoot ? deriveKeys(keyed.legacyRoot) : void 0;
|
|
2374
|
+
const legacySingle = keyed?.legacySecret ? keyOf(keyed.legacySecret) : void 0;
|
|
2375
|
+
const withLegacy = async (own, purpose) => [
|
|
2376
|
+
...await own,
|
|
2377
|
+
...legacyRings ? [(await legacyRings[purpose])[0]] : [],
|
|
2378
|
+
...legacySingle ? [await legacySingle] : []
|
|
2379
|
+
];
|
|
2380
|
+
const cookieKeys = fromKey ? withLegacy(fromKey.cookie, "cookie") : derived?.cookie ?? single;
|
|
2381
|
+
const flowKeys = fromKey ? withLegacy(fromKey.flow, "flow") : derived?.flow ?? single;
|
|
2382
|
+
if (!keyed && !pacted) {
|
|
2383
|
+
deprecated(
|
|
2384
|
+
"Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
|
|
2385
|
+
"Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
|
|
2389
|
+
const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
|
|
1916
2390
|
if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
|
|
1917
2391
|
throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
|
|
1918
2392
|
}
|
|
@@ -1948,18 +2422,65 @@ function createCauthy(given = {}) {
|
|
|
1948
2422
|
acceptInviteUrl,
|
|
1949
2423
|
fetch: given.fetch,
|
|
1950
2424
|
timeoutMs: systemTimeout,
|
|
1951
|
-
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {}
|
|
2425
|
+
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {},
|
|
2426
|
+
// With a contract the key proves the link's service client; without, the key's own client.
|
|
2427
|
+
...fromKey ? {
|
|
2428
|
+
signer: { clientId: contract ? contract.serviceClientId ?? keyed.key.clientId : clientId, signer: fromKey.signer },
|
|
2429
|
+
currentSystem: () => identity?.current()?.system ?? ""
|
|
2430
|
+
} : {}
|
|
1952
2431
|
});
|
|
1953
|
-
const
|
|
1954
|
-
|
|
2432
|
+
const selfOptions = () => ({
|
|
2433
|
+
issuer,
|
|
2434
|
+
token: () => ownSystem.token(),
|
|
2435
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2436
|
+
kid: () => signingKid,
|
|
2437
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2438
|
+
});
|
|
2439
|
+
const identity = askSelf ? createIdentity({
|
|
2440
|
+
issuer,
|
|
2441
|
+
token: () => ownSystem.token(),
|
|
2442
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2443
|
+
kid: () => signingKid,
|
|
2444
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2445
|
+
}) : void 0;
|
|
2446
|
+
identity?.ready().catch(() => {
|
|
2447
|
+
});
|
|
2448
|
+
const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
|
|
2449
|
+
const systemNow = () => identity?.current()?.system ?? system;
|
|
2450
|
+
const requestOrigin = (request) => {
|
|
1955
2451
|
const url = new URL(request.url);
|
|
1956
2452
|
const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
|
|
1957
2453
|
return proto === "https" || proto === "http" ? `${proto}://${url.host}` : url.origin;
|
|
1958
2454
|
};
|
|
1959
|
-
const
|
|
2455
|
+
const originOf = (request) => {
|
|
2456
|
+
if (baseURL) return withoutTrailingSlashes(baseURL);
|
|
2457
|
+
const asked = requestOrigin(request);
|
|
2458
|
+
if (!identity) return asked;
|
|
2459
|
+
const urls2 = identity.current()?.app_urls ?? [];
|
|
2460
|
+
if (urls2.includes(asked)) return asked;
|
|
2461
|
+
if (urls2.length === 1) return urls2[0];
|
|
2462
|
+
throw new UnknownOriginError(asked, urls2);
|
|
2463
|
+
};
|
|
2464
|
+
const safeOrigin = (request) => {
|
|
2465
|
+
try {
|
|
2466
|
+
return originOf(request);
|
|
2467
|
+
} catch {
|
|
2468
|
+
return requestOrigin(request);
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
|
|
2472
|
+
async function clientAuth(form) {
|
|
2473
|
+
if (fromKey && !contract) {
|
|
2474
|
+
const s = await fromKey.signer;
|
|
2475
|
+
form.set("client_assertion_type", ASSERTION_TYPE2);
|
|
2476
|
+
form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
|
|
2477
|
+
} else if (clientSecret) {
|
|
2478
|
+
form.set("client_secret", clientSecret);
|
|
2479
|
+
}
|
|
2480
|
+
return form;
|
|
2481
|
+
}
|
|
1960
2482
|
async function token(form, proof = {}) {
|
|
1961
|
-
const body2 = new URLSearchParams({ client_id: clientId, ...form });
|
|
1962
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2483
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
|
|
1963
2484
|
try {
|
|
1964
2485
|
const res = await doFetch(`${issuer}/v1/oauth/token`, {
|
|
1965
2486
|
method: "POST",
|
|
@@ -1998,8 +2519,7 @@ function createCauthy(given = {}) {
|
|
|
1998
2519
|
}
|
|
1999
2520
|
const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
|
|
2000
2521
|
async function revoke(refreshToken) {
|
|
2001
|
-
const form = new URLSearchParams({ client_id: clientId, token: refreshToken });
|
|
2002
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
2522
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: refreshToken }));
|
|
2003
2523
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2004
2524
|
}
|
|
2005
2525
|
const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
|
|
@@ -2091,7 +2611,8 @@ function createCauthy(given = {}) {
|
|
|
2091
2611
|
async function organizations(session) {
|
|
2092
2612
|
const hit = orgCache.get(session.accessToken);
|
|
2093
2613
|
if (hit && Date.now() - hit.at < ORGANIZATIONS_TTL_MS) return hit.value;
|
|
2094
|
-
const
|
|
2614
|
+
const sys = systemNow();
|
|
2615
|
+
const url = `${issuer}/v1/oauth/organizations${sys ? `?system=${encodeURIComponent(sys)}` : ""}`;
|
|
2095
2616
|
const res = await doFetch(url, { headers: { authorization: `Bearer ${session.accessToken}` } });
|
|
2096
2617
|
if (!res.ok) throw new Error(`@cauthyhq/node: reading organizations: ${res.status}`);
|
|
2097
2618
|
const body2 = await res.json();
|
|
@@ -2245,8 +2766,7 @@ function createCauthy(given = {}) {
|
|
|
2245
2766
|
async function logout(request) {
|
|
2246
2767
|
const { had, value } = await read(request);
|
|
2247
2768
|
if (value !== null) {
|
|
2248
|
-
const body2 = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2249
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2769
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2250
2770
|
await doFetch(`${issuer}/v1/oauth/revoke`, {
|
|
2251
2771
|
method: "POST",
|
|
2252
2772
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
@@ -2370,7 +2890,7 @@ function createCauthy(given = {}) {
|
|
|
2370
2890
|
const res = await doFetch(`${issuer}${path}`, {
|
|
2371
2891
|
method,
|
|
2372
2892
|
headers: {
|
|
2373
|
-
"x-tenant-key":
|
|
2893
|
+
"x-tenant-key": tenantNow(),
|
|
2374
2894
|
accept: "application/json",
|
|
2375
2895
|
// A link Cauthy mails opens the application's page for it when it
|
|
2376
2896
|
// has one; otherwise Cauthy's own. Either way in the reader's language.
|
|
@@ -2516,7 +3036,7 @@ function createCauthy(given = {}) {
|
|
|
2516
3036
|
}
|
|
2517
3037
|
if (route === "/sign-in/anonymous" || route === "/me/upgrade") deprecated(`POST ${route}`, "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2518
3038
|
if (route.startsWith("/me/upgrade/social/")) deprecated("GET /me/upgrade/social/{provider}", "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2519
|
-
if (
|
|
3039
|
+
if (tenantNow() === "") {
|
|
2520
3040
|
return problem(500, "not_configured", "@cauthyhq/node: tenant (CAUTHY_TENANT) is required for the application's own sign-in screens");
|
|
2521
3041
|
}
|
|
2522
3042
|
if (route === "/look" && !post) {
|
|
@@ -2751,7 +3271,7 @@ function createCauthy(given = {}) {
|
|
|
2751
3271
|
let upstream;
|
|
2752
3272
|
try {
|
|
2753
3273
|
upstream = await doFetch(`${issuer}/v1/auth/mfa/push/events`, {
|
|
2754
|
-
headers: { "x-tenant-key":
|
|
3274
|
+
headers: { "x-tenant-key": tenantNow(), "x-mfa-token": step.token, accept: "text/event-stream" },
|
|
2755
3275
|
signal: request.signal
|
|
2756
3276
|
});
|
|
2757
3277
|
} catch {
|
|
@@ -2771,8 +3291,7 @@ function createCauthy(given = {}) {
|
|
|
2771
3291
|
if (route === "/sign-out") {
|
|
2772
3292
|
const { had, value } = await read(request);
|
|
2773
3293
|
if (value !== null) {
|
|
2774
|
-
const form = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2775
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
3294
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2776
3295
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2777
3296
|
orgCache.delete(value.at);
|
|
2778
3297
|
}
|
|
@@ -3171,14 +3690,38 @@ function createCauthy(given = {}) {
|
|
|
3171
3690
|
const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
|
|
3172
3691
|
const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
|
|
3173
3692
|
const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
|
|
3693
|
+
if (identity && route !== null) {
|
|
3694
|
+
const unready = await identityGate();
|
|
3695
|
+
if (unready) return finish(unready, route);
|
|
3696
|
+
}
|
|
3174
3697
|
try {
|
|
3175
3698
|
return finish(await serve(request), route, published3);
|
|
3176
3699
|
} catch (err) {
|
|
3700
|
+
if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
|
|
3177
3701
|
if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
|
|
3178
3702
|
const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
|
|
3179
3703
|
return finish(wantsPage ? new Response("Sign-in refused (email_not_confirmed). Confirm your email address first.", { status: 403, headers: { "content-type": "text/plain; charset=utf-8" } }) : problem(403, "email_not_confirmed", "Confirm your email address first."), route);
|
|
3180
3704
|
}
|
|
3181
3705
|
}
|
|
3706
|
+
async function identityGate() {
|
|
3707
|
+
if (!identity) return null;
|
|
3708
|
+
const unavailable = () => problem(503, "unavailable", "Cauthy cannot be reached yet. Try again in a moment.");
|
|
3709
|
+
if (identity.failing()) return unavailable();
|
|
3710
|
+
let timer;
|
|
3711
|
+
const waited = new Promise((resolve3) => {
|
|
3712
|
+
timer = setTimeout(() => resolve3("timeout"), requestTimeout);
|
|
3713
|
+
});
|
|
3714
|
+
try {
|
|
3715
|
+
const ready = identity.ready();
|
|
3716
|
+
const got = await Promise.race([ready.then(() => "ok"), identity.attempt().then((ok) => ok ? "ok" : "failed"), waited]);
|
|
3717
|
+
if (got === "failed") await Promise.race([ready, Promise.resolve()]);
|
|
3718
|
+
return got === "ok" ? null : unavailable();
|
|
3719
|
+
} catch (e) {
|
|
3720
|
+
return e.code === "misconfigured" ? problem(503, "misconfigured", "Cauthy refuses this application's key: make a new one in Cauthy's dashboard.") : unavailable();
|
|
3721
|
+
} finally {
|
|
3722
|
+
clearTimeout(timer);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3182
3725
|
async function serve(request) {
|
|
3183
3726
|
const url = new URL(request.url);
|
|
3184
3727
|
if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
|
|
@@ -3261,15 +3804,65 @@ function createCauthy(given = {}) {
|
|
|
3261
3804
|
return { response: redirect(to) };
|
|
3262
3805
|
}
|
|
3263
3806
|
const replay = given.webhookReplay ?? memoryReplayStore();
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3807
|
+
let lateSelf;
|
|
3808
|
+
const tokenVerifier = (o = {}) => {
|
|
3809
|
+
if (keyed) {
|
|
3810
|
+
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3811
|
+
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3812
|
+
}
|
|
3813
|
+
let built;
|
|
3814
|
+
return async (from) => {
|
|
3815
|
+
const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
|
|
3816
|
+
if (built?.from !== id) {
|
|
3817
|
+
built = { from: id, verify: createTokenVerifier({
|
|
3818
|
+
issuer,
|
|
3819
|
+
tenantId: contract?.tenantId ?? id.tenant.id,
|
|
3820
|
+
audience: id.audience,
|
|
3821
|
+
fetch: given.fetch,
|
|
3822
|
+
...contract ? { clientId: contract.clientId } : {},
|
|
3823
|
+
...o
|
|
3824
|
+
}) };
|
|
3825
|
+
}
|
|
3826
|
+
return built.verify(from);
|
|
3827
|
+
};
|
|
3828
|
+
}
|
|
3829
|
+
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3830
|
+
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3831
|
+
}
|
|
3832
|
+
return createTokenVerifier({
|
|
3833
|
+
issuer,
|
|
3834
|
+
tenantId: contract?.tenantId ?? given.tenantId,
|
|
3835
|
+
audience: given.audience,
|
|
3836
|
+
fetch: given.fetch,
|
|
3837
|
+
...contract ? { clientId: contract.clientId } : {},
|
|
3838
|
+
...o
|
|
3839
|
+
});
|
|
3840
|
+
};
|
|
3271
3841
|
const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now, replay });
|
|
3272
|
-
|
|
3842
|
+
async function keyJwks() {
|
|
3843
|
+
const s = await fromKey.signer;
|
|
3844
|
+
const keys2 = [{ ...s.publicJwk, kid: s.kid, alg: "EdDSA", use: "sig" }];
|
|
3845
|
+
if (fromKey.previousJwk) keys2.push({ ...await fromKey.previousJwk, alg: "EdDSA", use: "sig" });
|
|
3846
|
+
if (ownKeys) keys2.push(...(await ownKeys.published()).keys);
|
|
3847
|
+
return new Response(JSON.stringify({ keys: keys2 }), {
|
|
3848
|
+
headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
const noStore = () => {
|
|
3852
|
+
throw new CauthyConfigError("on CAUTHY_KEY the application keeps no key of its own: make a new key on the application's card in Cauthy's dashboard");
|
|
3853
|
+
};
|
|
3854
|
+
const keyOnly = {
|
|
3855
|
+
current: async () => noStore(),
|
|
3856
|
+
published: async () => ({ keys: [] }),
|
|
3857
|
+
rotate: async () => noStore(),
|
|
3858
|
+
reseal: async () => 0
|
|
3859
|
+
};
|
|
3860
|
+
const pact = contract && fromKey ? {
|
|
3861
|
+
contract,
|
|
3862
|
+
keys: ownKeys ?? keyOnly,
|
|
3863
|
+
jwks: keyJwks,
|
|
3864
|
+
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3865
|
+
} : contract && ownKeys ? {
|
|
3273
3866
|
contract,
|
|
3274
3867
|
keys: ownKeys,
|
|
3275
3868
|
async jwks() {
|
|
@@ -3279,8 +3872,25 @@ function createCauthy(given = {}) {
|
|
|
3279
3872
|
},
|
|
3280
3873
|
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3281
3874
|
} : void 0;
|
|
3875
|
+
const configured = () => ({
|
|
3876
|
+
issuer,
|
|
3877
|
+
audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
|
|
3878
|
+
tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
|
|
3879
|
+
system,
|
|
3880
|
+
client_id: clientId,
|
|
3881
|
+
sign_in_client_id: clientId,
|
|
3882
|
+
app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
|
|
3883
|
+
callback_path: `${basePath}/callback`,
|
|
3884
|
+
key: null,
|
|
3885
|
+
contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
|
|
3886
|
+
});
|
|
3282
3887
|
return {
|
|
3283
3888
|
handler,
|
|
3889
|
+
ready: async () => {
|
|
3890
|
+
if (identity) return identity.ready();
|
|
3891
|
+
const id = configured();
|
|
3892
|
+
return fromKey ? { ...id, key: { kid: (await fromKey.signer).kid, retires_at: null } } : id;
|
|
3893
|
+
},
|
|
3284
3894
|
checkMount: (target) => checkMount(basePath, target),
|
|
3285
3895
|
webhook,
|
|
3286
3896
|
tokenVerifier,
|
|
@@ -3370,10 +3980,12 @@ async function connectSystem(o) {
|
|
|
3370
3980
|
export {
|
|
3371
3981
|
CauthyApiError,
|
|
3372
3982
|
CauthyConfigError,
|
|
3983
|
+
CauthyKeyError,
|
|
3373
3984
|
CauthyTokenError,
|
|
3374
3985
|
CauthyUnavailableError,
|
|
3375
3986
|
CauthyWebhookError,
|
|
3376
3987
|
DEFAULT_PASSWORD_POLICY,
|
|
3988
|
+
KEY_REPLACES,
|
|
3377
3989
|
MIN_PASSWORD,
|
|
3378
3990
|
PROTOCOL_VERSION,
|
|
3379
3991
|
STANDARD_VARIABLES,
|
|
@@ -3383,15 +3995,18 @@ export {
|
|
|
3383
3995
|
checkName,
|
|
3384
3996
|
checkNewPassword,
|
|
3385
3997
|
checkPassword,
|
|
3998
|
+
composeKey,
|
|
3386
3999
|
connectSystem,
|
|
3387
4000
|
createCauthy,
|
|
3388
4001
|
createSystem,
|
|
3389
4002
|
createTokenVerifier,
|
|
4003
|
+
createVerifierFromEnv,
|
|
3390
4004
|
definePolicy,
|
|
3391
4005
|
describePasswordPolicy,
|
|
3392
4006
|
explainPasswordProblems,
|
|
3393
4007
|
failClosed,
|
|
3394
4008
|
memoryReplayStore,
|
|
4009
|
+
parseKey,
|
|
3395
4010
|
passwordPolicyOf,
|
|
3396
4011
|
passwordProblems,
|
|
3397
4012
|
policySource,
|