@cauthyhq/node 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +79 -24
- package/dist/{cauthy-Wy-YDGrM.d.ts → cauthy-C7gZ-pbv.d.ts} +64 -2
- package/dist/{cauthy-Bn-i7cKD.d.mts → cauthy-CCSSj3Lg.d.mts} +64 -2
- 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 +688 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +683 -67
- 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 +36 -0
- package/dist/testing.d.ts +36 -0
- package/dist/testing.js +395 -0
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +395 -0
- package/dist/testing.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -54,9 +54,12 @@ async function unseal(sealed, key) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function unsealAny(sealed, ring) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
return (await unsealIndexed(sealed, ring))?.value ?? null;
|
|
58
|
+
}
|
|
59
|
+
async function unsealIndexed(sealed, ring) {
|
|
60
|
+
for (let index = 0; index < ring.length; index++) {
|
|
61
|
+
const value = await unseal(sealed, ring[index]);
|
|
62
|
+
if (value !== null) return { value, index };
|
|
60
63
|
}
|
|
61
64
|
return null;
|
|
62
65
|
}
|
|
@@ -68,6 +71,14 @@ var CauthyConfigError = class extends Error {
|
|
|
68
71
|
this.name = "CauthyConfigError";
|
|
69
72
|
}
|
|
70
73
|
};
|
|
74
|
+
var CauthyKeyError = class extends CauthyConfigError {
|
|
75
|
+
constructor(reason, message) {
|
|
76
|
+
super(`CAUTHY_KEY: ${message} (${reason})`);
|
|
77
|
+
this.reason = reason;
|
|
78
|
+
this.name = "CauthyKeyError";
|
|
79
|
+
}
|
|
80
|
+
reason;
|
|
81
|
+
};
|
|
71
82
|
|
|
72
83
|
// src/url.ts
|
|
73
84
|
function withoutTrailingSlashes(s) {
|
|
@@ -978,6 +989,7 @@ function createSystem(given = {}) {
|
|
|
978
989
|
};
|
|
979
990
|
const tokenEndpoint = `${issuer}/v1/oauth/token`;
|
|
980
991
|
function configured() {
|
|
992
|
+
if (given.signer) return;
|
|
981
993
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
982
994
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
983
995
|
if (given.keys) {
|
|
@@ -990,7 +1002,7 @@ function createSystem(given = {}) {
|
|
|
990
1002
|
}
|
|
991
1003
|
let imported;
|
|
992
1004
|
const signingKey = () => imported ??= loadKey(key);
|
|
993
|
-
async function
|
|
1005
|
+
async function fail2(res, what) {
|
|
994
1006
|
const body = await res.json().catch(() => null);
|
|
995
1007
|
const first = body?.errors?.[0];
|
|
996
1008
|
const code = typeof body?.error === "string" ? body.error : first?.code;
|
|
@@ -1001,9 +1013,13 @@ function createSystem(given = {}) {
|
|
|
1001
1013
|
let asking;
|
|
1002
1014
|
async function ask() {
|
|
1003
1015
|
configured();
|
|
1004
|
-
const self = given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1016
|
+
const self = given.signer ? given.signer.clientId : given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1005
1017
|
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
|
|
1006
|
-
if (given.
|
|
1018
|
+
if (given.signer) {
|
|
1019
|
+
const s = await given.signer.signer;
|
|
1020
|
+
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
1021
|
+
form.set("client_assertion", await s.assertion(self, tokenEndpoint));
|
|
1022
|
+
} else if (given.keys || key) {
|
|
1007
1023
|
const signer = given.keys ? await given.keys.current() : { kid: key.keyId, privateKey: await signingKey() };
|
|
1008
1024
|
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
1025
|
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
@@ -1012,7 +1028,7 @@ function createSystem(given = {}) {
|
|
|
1012
1028
|
form.set("client_secret", clientSecret);
|
|
1013
1029
|
}
|
|
1014
1030
|
const res = await doFetch(tokenEndpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
|
|
1015
|
-
if (!res.ok) await
|
|
1031
|
+
if (!res.ok) await fail2(res, "the application's own token");
|
|
1016
1032
|
const t = await res.json();
|
|
1017
1033
|
held = { value: t.access_token, until: Date.now() + t.expires_in * 1e3 - SKEW_MS };
|
|
1018
1034
|
return t.access_token;
|
|
@@ -1034,7 +1050,7 @@ function createSystem(given = {}) {
|
|
|
1034
1050
|
held = void 0;
|
|
1035
1051
|
res = await send();
|
|
1036
1052
|
}
|
|
1037
|
-
if (!res.ok) await
|
|
1053
|
+
if (!res.ok) await fail2(res, `${method} ${path}`);
|
|
1038
1054
|
return res.status === 204 ? void 0 : res.json();
|
|
1039
1055
|
}
|
|
1040
1056
|
const data = async (method, path, body, extra) => (await call(method, path, body, extra))?.data;
|
|
@@ -1108,7 +1124,7 @@ function createSystem(given = {}) {
|
|
|
1108
1124
|
}
|
|
1109
1125
|
},
|
|
1110
1126
|
async check(q) {
|
|
1111
|
-
const system = q.system ?? ownSystem;
|
|
1127
|
+
const system = q.system ?? (ownSystem || given.currentSystem?.() || "");
|
|
1112
1128
|
if (system === "") throw new CauthyConfigError("name the system to check in (system, or CAUTHY_SYSTEM)");
|
|
1113
1129
|
return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
|
|
1114
1130
|
},
|
|
@@ -1278,6 +1294,219 @@ function firstProblem(...checks) {
|
|
|
1278
1294
|
|
|
1279
1295
|
// src/webhook.ts
|
|
1280
1296
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
1297
|
+
|
|
1298
|
+
// src/sha256.ts
|
|
1299
|
+
var K = new Uint32Array([
|
|
1300
|
+
1116352408,
|
|
1301
|
+
1899447441,
|
|
1302
|
+
3049323471,
|
|
1303
|
+
3921009573,
|
|
1304
|
+
961987163,
|
|
1305
|
+
1508970993,
|
|
1306
|
+
2453635748,
|
|
1307
|
+
2870763221,
|
|
1308
|
+
3624381080,
|
|
1309
|
+
310598401,
|
|
1310
|
+
607225278,
|
|
1311
|
+
1426881987,
|
|
1312
|
+
1925078388,
|
|
1313
|
+
2162078206,
|
|
1314
|
+
2614888103,
|
|
1315
|
+
3248222580,
|
|
1316
|
+
3835390401,
|
|
1317
|
+
4022224774,
|
|
1318
|
+
264347078,
|
|
1319
|
+
604807628,
|
|
1320
|
+
770255983,
|
|
1321
|
+
1249150122,
|
|
1322
|
+
1555081692,
|
|
1323
|
+
1996064986,
|
|
1324
|
+
2554220882,
|
|
1325
|
+
2821834349,
|
|
1326
|
+
2952996808,
|
|
1327
|
+
3210313671,
|
|
1328
|
+
3336571891,
|
|
1329
|
+
3584528711,
|
|
1330
|
+
113926993,
|
|
1331
|
+
338241895,
|
|
1332
|
+
666307205,
|
|
1333
|
+
773529912,
|
|
1334
|
+
1294757372,
|
|
1335
|
+
1396182291,
|
|
1336
|
+
1695183700,
|
|
1337
|
+
1986661051,
|
|
1338
|
+
2177026350,
|
|
1339
|
+
2456956037,
|
|
1340
|
+
2730485921,
|
|
1341
|
+
2820302411,
|
|
1342
|
+
3259730800,
|
|
1343
|
+
3345764771,
|
|
1344
|
+
3516065817,
|
|
1345
|
+
3600352804,
|
|
1346
|
+
4094571909,
|
|
1347
|
+
275423344,
|
|
1348
|
+
430227734,
|
|
1349
|
+
506948616,
|
|
1350
|
+
659060556,
|
|
1351
|
+
883997877,
|
|
1352
|
+
958139571,
|
|
1353
|
+
1322822218,
|
|
1354
|
+
1537002063,
|
|
1355
|
+
1747873779,
|
|
1356
|
+
1955562222,
|
|
1357
|
+
2024104815,
|
|
1358
|
+
2227730452,
|
|
1359
|
+
2361852424,
|
|
1360
|
+
2428436474,
|
|
1361
|
+
2756734187,
|
|
1362
|
+
3204031479,
|
|
1363
|
+
3329325298
|
|
1364
|
+
]);
|
|
1365
|
+
function sha256(data) {
|
|
1366
|
+
const h = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
1367
|
+
const len = data.length;
|
|
1368
|
+
const padded = new Uint8Array(len + 9 + 63 >> 6 << 6);
|
|
1369
|
+
padded.set(data);
|
|
1370
|
+
padded[len] = 128;
|
|
1371
|
+
const view = new DataView(padded.buffer);
|
|
1372
|
+
view.setUint32(padded.length - 8, Math.floor(len / 536870912));
|
|
1373
|
+
view.setUint32(padded.length - 4, len << 3 >>> 0);
|
|
1374
|
+
const w = new Uint32Array(64);
|
|
1375
|
+
const rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1376
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
1377
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
|
|
1378
|
+
for (let i = 16; i < 64; i++) {
|
|
1379
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
1380
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
1381
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
1382
|
+
}
|
|
1383
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
1384
|
+
for (let i = 0; i < 64; i++) {
|
|
1385
|
+
const t1 = hh + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[i] + w[i] >>> 0;
|
|
1386
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
1387
|
+
hh = g;
|
|
1388
|
+
g = f;
|
|
1389
|
+
f = e;
|
|
1390
|
+
e = d + t1 >>> 0;
|
|
1391
|
+
d = c;
|
|
1392
|
+
c = b;
|
|
1393
|
+
b = a;
|
|
1394
|
+
a = t1 + t2 >>> 0;
|
|
1395
|
+
}
|
|
1396
|
+
h[0] += a;
|
|
1397
|
+
h[1] += b;
|
|
1398
|
+
h[2] += c;
|
|
1399
|
+
h[3] += d;
|
|
1400
|
+
h[4] += e;
|
|
1401
|
+
h[5] += f;
|
|
1402
|
+
h[6] += g;
|
|
1403
|
+
h[7] += hh;
|
|
1404
|
+
}
|
|
1405
|
+
const out2 = new Uint8Array(32);
|
|
1406
|
+
const ov = new DataView(out2.buffer);
|
|
1407
|
+
for (let i = 0; i < 8; i++) ov.setUint32(i * 4, h[i]);
|
|
1408
|
+
return out2;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// src/cauthykey.ts
|
|
1412
|
+
var KEY_PREFIX = "cauthy1";
|
|
1413
|
+
var KEY_REPLACES = [
|
|
1414
|
+
"CAUTHY_ISSUER",
|
|
1415
|
+
"CAUTHY_CLIENT_ID",
|
|
1416
|
+
"CAUTHY_CLIENT_SECRET",
|
|
1417
|
+
"CAUTHY_SYSTEM",
|
|
1418
|
+
"CAUTHY_TENANT",
|
|
1419
|
+
"CAUTHY_SYSTEM_KEY",
|
|
1420
|
+
"CAUTHY_SYSTEM_KEY_ID",
|
|
1421
|
+
"CAUTHY_BASE_URL",
|
|
1422
|
+
"CAUTHY_AUDIENCE",
|
|
1423
|
+
"CAUTHY_TENANT_ID"
|
|
1424
|
+
];
|
|
1425
|
+
function refuseBesideKey(environment) {
|
|
1426
|
+
const set = KEY_REPLACES.filter((n) => (environment?.[n] ?? "") !== "");
|
|
1427
|
+
if (set.length > 0) {
|
|
1428
|
+
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.`);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
var MAX_LEN = 512;
|
|
1432
|
+
var enc = new TextEncoder();
|
|
1433
|
+
var dec = new TextDecoder("utf-8", { fatal: true });
|
|
1434
|
+
function b64uEncode(b) {
|
|
1435
|
+
let bin = "";
|
|
1436
|
+
for (const x of b) bin += String.fromCharCode(x);
|
|
1437
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1438
|
+
}
|
|
1439
|
+
function b64uDecode(s) {
|
|
1440
|
+
if (!/^[A-Za-z0-9_-]*$/.test(s) || s.length % 4 === 1) return null;
|
|
1441
|
+
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4));
|
|
1442
|
+
const out2 = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
1443
|
+
return b64uEncode(out2) === s ? out2 : null;
|
|
1444
|
+
}
|
|
1445
|
+
function checksum(body) {
|
|
1446
|
+
return b64uEncode(sha256(enc.encode(body)).slice(0, 6));
|
|
1447
|
+
}
|
|
1448
|
+
function printableAscii(s) {
|
|
1449
|
+
return /^[\x21-\x7e]*$/.test(s);
|
|
1450
|
+
}
|
|
1451
|
+
function validIssuer(s) {
|
|
1452
|
+
if (s === "" || !printableAscii(s)) return false;
|
|
1453
|
+
let u;
|
|
1454
|
+
try {
|
|
1455
|
+
u = new URL(s);
|
|
1456
|
+
} catch {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
if (u.origin !== s) return false;
|
|
1460
|
+
if (/^\[::ffff:/i.test(u.host)) return false;
|
|
1461
|
+
if (u.protocol === "https:") return true;
|
|
1462
|
+
return u.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname);
|
|
1463
|
+
}
|
|
1464
|
+
var fail = (reason, message) => {
|
|
1465
|
+
throw new CauthyKeyError(reason, message);
|
|
1466
|
+
};
|
|
1467
|
+
var NOT_A_KEY = "not a Cauthy key (copy it again from the dashboard)";
|
|
1468
|
+
function parseKey(text2) {
|
|
1469
|
+
const s = text2.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
|
|
1470
|
+
if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
|
|
1471
|
+
const head = s.split(".", 1)[0];
|
|
1472
|
+
if (head !== KEY_PREFIX) {
|
|
1473
|
+
if (/^cauthy[0-9]+$/.test(head)) fail("key_version", `this key is for a newer SDK (${head}): update @cauthyhq/node`);
|
|
1474
|
+
fail("key_format", NOT_A_KEY);
|
|
1475
|
+
}
|
|
1476
|
+
const parts = s.split(".");
|
|
1477
|
+
if (parts.length !== 5 || parts.some((p) => p === "")) fail("key_format", NOT_A_KEY);
|
|
1478
|
+
if (!constantTimeEqual(checksum(parts.slice(0, 4).join(".")), parts[4])) {
|
|
1479
|
+
fail("key_checksum", "the key is damaged (part of it is missing or changed): copy it again from the dashboard");
|
|
1480
|
+
}
|
|
1481
|
+
const issuerBytes = b64uDecode(parts[1]);
|
|
1482
|
+
let issuer = "";
|
|
1483
|
+
try {
|
|
1484
|
+
issuer = issuerBytes ? dec.decode(issuerBytes) : "";
|
|
1485
|
+
} catch {
|
|
1486
|
+
issuer = "";
|
|
1487
|
+
}
|
|
1488
|
+
if (!validIssuer(issuer)) fail("key_issuer", "the key names no valid Cauthy address");
|
|
1489
|
+
const clientBytes = b64uDecode(parts[2]);
|
|
1490
|
+
const clientId = clientBytes ? String.fromCharCode(...clientBytes) : "";
|
|
1491
|
+
if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) fail("key_client", "the key names no valid client");
|
|
1492
|
+
const secret = b64uDecode(parts[3]);
|
|
1493
|
+
if (parts[3].length !== 43 || !secret || secret.length !== 32) fail("key_secret", "the key's secret is not 32 bytes");
|
|
1494
|
+
return { issuer, clientId, secret: parts[3] };
|
|
1495
|
+
}
|
|
1496
|
+
function composeKey(issuer, clientId, secret) {
|
|
1497
|
+
const body = `${KEY_PREFIX}.${b64uEncode(enc.encode(issuer))}.${b64uEncode(enc.encode(clientId))}.${secret}`;
|
|
1498
|
+
const key = `${body}.${checksum(body)}`;
|
|
1499
|
+
parseKey(key);
|
|
1500
|
+
return key;
|
|
1501
|
+
}
|
|
1502
|
+
function constantTimeEqual(a, b) {
|
|
1503
|
+
if (a.length !== b.length) return false;
|
|
1504
|
+
let d = 0;
|
|
1505
|
+
for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1506
|
+
return d === 0;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// src/webhook.ts
|
|
1281
1510
|
var CauthyWebhookError = class extends Error {
|
|
1282
1511
|
constructor(reason) {
|
|
1283
1512
|
super(`@cauthyhq/node: webhook refused: ${reason}`);
|
|
@@ -1312,7 +1541,8 @@ function keysOf(issuer) {
|
|
|
1312
1541
|
}
|
|
1313
1542
|
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
1314
1543
|
async function verifyWebhook(request, options = {}) {
|
|
1315
|
-
const
|
|
1544
|
+
const key = typeof process !== "undefined" ? process.env?.CAUTHY_KEY : void 0;
|
|
1545
|
+
const env4 = key ? parseKey(key).issuer : typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
1316
1546
|
const issuer = withoutTrailingSlashes(options.issuer ?? env4 ?? "");
|
|
1317
1547
|
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
1318
1548
|
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
@@ -1441,7 +1671,7 @@ var LIMIT = 2n ** 53n;
|
|
|
1441
1671
|
function parseStrict(text2, integerMembers = []) {
|
|
1442
1672
|
let i = 0;
|
|
1443
1673
|
let depth = 0;
|
|
1444
|
-
const
|
|
1674
|
+
const fail2 = (what) => {
|
|
1445
1675
|
throw new SyntaxError(`${what} at ${i}`);
|
|
1446
1676
|
};
|
|
1447
1677
|
const skip = () => {
|
|
@@ -1460,19 +1690,19 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1460
1690
|
return v;
|
|
1461
1691
|
}
|
|
1462
1692
|
}
|
|
1463
|
-
return
|
|
1693
|
+
return fail2("unexpected character");
|
|
1464
1694
|
};
|
|
1465
1695
|
const string = () => {
|
|
1466
1696
|
const start = i;
|
|
1467
1697
|
i++;
|
|
1468
1698
|
while (i < text2.length && text2[i] !== '"') i += text2[i] === "\\" ? 2 : 1;
|
|
1469
|
-
if (text2[i] !== '"')
|
|
1699
|
+
if (text2[i] !== '"') fail2("unterminated string");
|
|
1470
1700
|
i++;
|
|
1471
1701
|
return JSON.parse(text2.slice(start, i));
|
|
1472
1702
|
};
|
|
1473
1703
|
const number = (integer) => {
|
|
1474
1704
|
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text2.slice(i));
|
|
1475
|
-
if (!m) return
|
|
1705
|
+
if (!m) return fail2("bad number");
|
|
1476
1706
|
i += m[0].length;
|
|
1477
1707
|
if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
|
|
1478
1708
|
if (m[2] === void 0 && m[3] === void 0) {
|
|
@@ -1499,7 +1729,7 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1499
1729
|
i++;
|
|
1500
1730
|
depth--;
|
|
1501
1731
|
return out2;
|
|
1502
|
-
} else
|
|
1732
|
+
} else fail2("expected , or ]");
|
|
1503
1733
|
}
|
|
1504
1734
|
};
|
|
1505
1735
|
const object = () => {
|
|
@@ -1515,12 +1745,12 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1515
1745
|
}
|
|
1516
1746
|
for (; ; ) {
|
|
1517
1747
|
skip();
|
|
1518
|
-
if (text2[i] !== '"')
|
|
1748
|
+
if (text2[i] !== '"') fail2("expected a name");
|
|
1519
1749
|
const name = string();
|
|
1520
|
-
if (seen.has(name))
|
|
1750
|
+
if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
|
|
1521
1751
|
seen.add(name);
|
|
1522
1752
|
skip();
|
|
1523
|
-
if (text2[i] !== ":")
|
|
1753
|
+
if (text2[i] !== ":") fail2("expected :");
|
|
1524
1754
|
i++;
|
|
1525
1755
|
Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
|
|
1526
1756
|
skip();
|
|
@@ -1529,15 +1759,175 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1529
1759
|
i++;
|
|
1530
1760
|
depth--;
|
|
1531
1761
|
return out2;
|
|
1532
|
-
} else
|
|
1762
|
+
} else fail2("expected , or }");
|
|
1533
1763
|
}
|
|
1534
1764
|
};
|
|
1535
1765
|
const result = value();
|
|
1536
1766
|
skip();
|
|
1537
|
-
if (i < text2.length)
|
|
1767
|
+
if (i < text2.length) fail2("trailing characters");
|
|
1538
1768
|
return result;
|
|
1539
1769
|
}
|
|
1540
1770
|
|
|
1771
|
+
// src/deprecate.ts
|
|
1772
|
+
var told = /* @__PURE__ */ new Set();
|
|
1773
|
+
function deprecated(what, instead) {
|
|
1774
|
+
if (told.has(what)) return;
|
|
1775
|
+
told.add(what);
|
|
1776
|
+
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1777
|
+
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1778
|
+
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1779
|
+
} else {
|
|
1780
|
+
console.warn(message);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// src/keyderive.ts
|
|
1785
|
+
var enc2 = new TextEncoder();
|
|
1786
|
+
var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
|
1787
|
+
var ASSERTION_TYPE2 = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
1788
|
+
async function seedOf(secret) {
|
|
1789
|
+
const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
|
|
1790
|
+
return new Uint8Array(await crypto.subtle.deriveBits(
|
|
1791
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: enc2.encode("cauthy/sign/v1") },
|
|
1792
|
+
ikm,
|
|
1793
|
+
256
|
|
1794
|
+
));
|
|
1795
|
+
}
|
|
1796
|
+
async function thumbprint(x) {
|
|
1797
|
+
return b64uEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", enc2.encode(`{"crv":"Ed25519","kty":"OKP","x":"${x}"}`))));
|
|
1798
|
+
}
|
|
1799
|
+
async function signerOf(secret) {
|
|
1800
|
+
const seed = await seedOf(secret);
|
|
1801
|
+
const der = new Uint8Array(PKCS8_ED25519.length + 32);
|
|
1802
|
+
der.set(PKCS8_ED25519);
|
|
1803
|
+
der.set(seed, PKCS8_ED25519.length);
|
|
1804
|
+
const readable = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, true, ["sign"]);
|
|
1805
|
+
const { x } = await crypto.subtle.exportKey("jwk", readable);
|
|
1806
|
+
const signing = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, false, ["sign"]);
|
|
1807
|
+
der.fill(0);
|
|
1808
|
+
seed.fill(0);
|
|
1809
|
+
const publicJwk = { kty: "OKP", crv: "Ed25519", x };
|
|
1810
|
+
const kid = await thumbprint(publicJwk.x);
|
|
1811
|
+
const header = b64uEncode(enc2.encode(JSON.stringify({ alg: "EdDSA", kid })));
|
|
1812
|
+
return {
|
|
1813
|
+
kid,
|
|
1814
|
+
publicJwk,
|
|
1815
|
+
async assertion(clientId, tokenEndpoint, jti = crypto.randomUUID(), iat = Math.floor(Date.now() / 1e3)) {
|
|
1816
|
+
const claims = { iss: clientId, sub: clientId, aud: tokenEndpoint, jti, iat, exp: iat + 120 };
|
|
1817
|
+
const input = `${header}.${b64uEncode(enc2.encode(JSON.stringify(claims)))}`;
|
|
1818
|
+
const sig = new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, signing, enc2.encode(input)));
|
|
1819
|
+
return `${input}.${b64uEncode(sig)}`;
|
|
1820
|
+
}
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
function deriveFromKey(key, previous) {
|
|
1824
|
+
const other = previous && previous.secret !== key.secret ? previous : void 0;
|
|
1825
|
+
const rings = deriveKeys(key.secret, other?.secret);
|
|
1826
|
+
return {
|
|
1827
|
+
cookie: rings.cookie,
|
|
1828
|
+
flow: rings.flow,
|
|
1829
|
+
signer: signerOf(key.secret),
|
|
1830
|
+
...other ? { previousJwk: signerOf(other.secret).then((s) => ({ ...s.publicJwk, kid: s.kid })) } : {}
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// src/identity.ts
|
|
1835
|
+
var REVALIDATE_MS = 3e5;
|
|
1836
|
+
var sleep = (ms) => new Promise((resolve2) => {
|
|
1837
|
+
const t = setTimeout(resolve2, ms);
|
|
1838
|
+
t.unref?.();
|
|
1839
|
+
});
|
|
1840
|
+
function createIdentity(o) {
|
|
1841
|
+
const doFetch = o.fetch ?? globalThis.fetch;
|
|
1842
|
+
const now = o.now ?? Date.now;
|
|
1843
|
+
const retry = o.retryMs ?? [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
1844
|
+
let held;
|
|
1845
|
+
let etag;
|
|
1846
|
+
let checkedAt = 0;
|
|
1847
|
+
let first;
|
|
1848
|
+
let refused;
|
|
1849
|
+
let refreshing;
|
|
1850
|
+
let failed = false;
|
|
1851
|
+
let attemptEnded;
|
|
1852
|
+
let attemptOut;
|
|
1853
|
+
const kid = () => (typeof o.kid === "function" ? o.kid() : o.kid) ?? "?";
|
|
1854
|
+
async function ask() {
|
|
1855
|
+
const headers = { authorization: `Bearer ${await o.token()}`, accept: "application/json" };
|
|
1856
|
+
if (etag && held) headers["if-none-match"] = etag;
|
|
1857
|
+
const res = await doFetch(`${o.issuer}/v1/connect/self`, { headers });
|
|
1858
|
+
if (res.status === 304 && held) return "same";
|
|
1859
|
+
if (res.status === 410) throw Object.assign(new Error("client_inactive"), { code: "client_inactive" });
|
|
1860
|
+
if (!res.ok) throw new CauthyApiError(503, "unavailable", `Cauthy answered ${res.status} to /v1/connect/self`);
|
|
1861
|
+
const body = await res.json();
|
|
1862
|
+
if (!body?.data || typeof body.data.client_id !== "string") throw new CauthyApiError(503, "unavailable", "Cauthy's answer to /v1/connect/self has no identity");
|
|
1863
|
+
etag = res.headers.get("etag") ?? void 0;
|
|
1864
|
+
return body.data;
|
|
1865
|
+
}
|
|
1866
|
+
function refusalOf(e) {
|
|
1867
|
+
const code = e?.code;
|
|
1868
|
+
if (code !== "invalid_client" && code !== "client_inactive") return void 0;
|
|
1869
|
+
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.`;
|
|
1870
|
+
return new CauthyApiError(503, "misconfigured", message);
|
|
1871
|
+
}
|
|
1872
|
+
function refuse(err) {
|
|
1873
|
+
if (refused === void 0) o.log?.(err.message.replace(/^@cauthyhq\/node: /, ""));
|
|
1874
|
+
refused = err;
|
|
1875
|
+
checkedAt = now();
|
|
1876
|
+
return err;
|
|
1877
|
+
}
|
|
1878
|
+
async function firstAnswer() {
|
|
1879
|
+
for (let attempt = 0; ; attempt++) {
|
|
1880
|
+
attemptOut = new Promise((resolve2) => {
|
|
1881
|
+
attemptEnded = resolve2;
|
|
1882
|
+
});
|
|
1883
|
+
try {
|
|
1884
|
+
const got = await ask();
|
|
1885
|
+
if (got !== "same") held = got;
|
|
1886
|
+
checkedAt = now();
|
|
1887
|
+
failed = false;
|
|
1888
|
+
attemptEnded?.(true);
|
|
1889
|
+
return held;
|
|
1890
|
+
} catch (e) {
|
|
1891
|
+
const r = refusalOf(e);
|
|
1892
|
+
failed = true;
|
|
1893
|
+
attemptEnded?.(false);
|
|
1894
|
+
if (r) throw refuse(r);
|
|
1895
|
+
await sleep(retry[Math.min(attempt, retry.length - 1)]);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
async function refresh() {
|
|
1900
|
+
checkedAt = now();
|
|
1901
|
+
try {
|
|
1902
|
+
const got = await ask();
|
|
1903
|
+
if (got !== "same") held = got;
|
|
1904
|
+
refused = void 0;
|
|
1905
|
+
} catch (e) {
|
|
1906
|
+
const r = refusalOf(e);
|
|
1907
|
+
if (r) refuse(r);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return {
|
|
1911
|
+
ready() {
|
|
1912
|
+
if ((held || refused) && now() - checkedAt >= REVALIDATE_MS) {
|
|
1913
|
+
refreshing ??= refresh().finally(() => {
|
|
1914
|
+
refreshing = void 0;
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
if (refused) return Promise.reject(refused);
|
|
1918
|
+
if (held) return Promise.resolve(held);
|
|
1919
|
+
first ??= firstAnswer();
|
|
1920
|
+
return first;
|
|
1921
|
+
},
|
|
1922
|
+
current: () => held,
|
|
1923
|
+
refresh: () => refreshing ??= refresh().finally(() => {
|
|
1924
|
+
refreshing = void 0;
|
|
1925
|
+
}),
|
|
1926
|
+
failing: () => held === void 0 && failed && refused === void 0,
|
|
1927
|
+
attempt: () => held !== void 0 ? Promise.resolve(true) : attemptOut ?? Promise.resolve(false)
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1541
1931
|
// src/verify.ts
|
|
1542
1932
|
var CauthyUnavailableError = class extends Error {
|
|
1543
1933
|
constructor(detail) {
|
|
@@ -1680,6 +2070,10 @@ function decodeJson(segment) {
|
|
|
1680
2070
|
return parseStrict(Buffer.from(padded, "base64").toString("utf8"));
|
|
1681
2071
|
}
|
|
1682
2072
|
function createTokenVerifier(options = {}) {
|
|
2073
|
+
const fromEnv = ["CAUTHY_ISSUER", "CAUTHY_AUDIENCE", "CAUTHY_TENANT_ID"].filter((name, i) => [options.issuer, options.audience, options.tenantId][i] === void 0 && (env2(name) ?? "") !== "");
|
|
2074
|
+
if (fromEnv.length > 0) {
|
|
2075
|
+
deprecated("Reading CAUTHY_ISSUER, CAUTHY_AUDIENCE and CAUTHY_TENANT_ID to verify tokens", "Set CAUTHY_KEY and use createVerifierFromEnv().");
|
|
2076
|
+
}
|
|
1683
2077
|
const issuerGiven = options.issuer ?? env2("CAUTHY_ISSUER") ?? "";
|
|
1684
2078
|
if (issuerGiven === "") throw new CauthyConfigError("issuer (CAUTHY_ISSUER) is not set");
|
|
1685
2079
|
const issuer = issuerAddress(issuerGiven, "issuer");
|
|
@@ -1775,18 +2169,18 @@ function createTokenVerifier(options = {}) {
|
|
|
1775
2169
|
async function verifyToken(from, options = {}) {
|
|
1776
2170
|
return createTokenVerifier(options)(from);
|
|
1777
2171
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1785
|
-
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1786
|
-
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1787
|
-
} else {
|
|
1788
|
-
console.warn(message);
|
|
2172
|
+
async function createVerifierFromEnv(options = {}) {
|
|
2173
|
+
const text2 = options.key ?? env2("CAUTHY_KEY");
|
|
2174
|
+
if (text2 === void 0 || text2 === "") return createTokenVerifier(options);
|
|
2175
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2176
|
+
for (const name of ["issuer", "audience", "tenantId"]) {
|
|
2177
|
+
if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
1789
2178
|
}
|
|
2179
|
+
const key = parseKey(text2);
|
|
2180
|
+
const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
|
|
2181
|
+
const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
|
|
2182
|
+
const { key: _, ...rest } = options;
|
|
2183
|
+
return createTokenVerifier({ ...rest, issuer: id.issuer, audience: id.audience, tenantId: id.tenant.id });
|
|
1790
2184
|
}
|
|
1791
2185
|
|
|
1792
2186
|
// src/mount.ts
|
|
@@ -1879,6 +2273,64 @@ function redirect(location, cookies = []) {
|
|
|
1879
2273
|
function conditionalOf(c) {
|
|
1880
2274
|
return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
|
|
1881
2275
|
}
|
|
2276
|
+
function byKey(given) {
|
|
2277
|
+
const text2 = given.key ?? env3("CAUTHY_KEY");
|
|
2278
|
+
if (text2 === void 0 || text2 === "") return void 0;
|
|
2279
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2280
|
+
for (const [name, value] of [
|
|
2281
|
+
["issuer", given.issuer],
|
|
2282
|
+
["clientId", given.clientId],
|
|
2283
|
+
["clientSecret", given.clientSecret],
|
|
2284
|
+
["secret", given.secret],
|
|
2285
|
+
["system", given.system],
|
|
2286
|
+
["tenant", given.tenant],
|
|
2287
|
+
["audience", given.audience],
|
|
2288
|
+
["tenantId", given.tenantId],
|
|
2289
|
+
["systemKey", given.systemKey],
|
|
2290
|
+
["rootKey", given.rootKey]
|
|
2291
|
+
]) {
|
|
2292
|
+
if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
2293
|
+
}
|
|
2294
|
+
const key = parseKey(text2);
|
|
2295
|
+
const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
|
|
2296
|
+
let previous;
|
|
2297
|
+
if (prevText) {
|
|
2298
|
+
try {
|
|
2299
|
+
previous = parseKey(prevText);
|
|
2300
|
+
} catch (e) {
|
|
2301
|
+
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_]+\)$/, "")}`);
|
|
2302
|
+
throw e;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
if (previous && (previous.issuer !== key.issuer || previous.clientId !== key.clientId)) {
|
|
2306
|
+
throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
|
|
2307
|
+
}
|
|
2308
|
+
const legacySecret = env3("CAUTHY_SECRET") || void 0;
|
|
2309
|
+
const legacyRoot = env3("PACT_ROOT_KEY") || void 0;
|
|
2310
|
+
const legacyRootPrevious = legacyRoot ? env3("PACT_ROOT_KEY_PREVIOUS") || void 0 : void 0;
|
|
2311
|
+
if (legacySecret) deprecated("CAUTHY_SECRET beside CAUTHY_KEY", "It is read only for sessions made before the move: remove it after the rotation window.");
|
|
2312
|
+
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.");
|
|
2313
|
+
if (given.keys && !(legacyRoot && given.contract)) {
|
|
2314
|
+
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)");
|
|
2315
|
+
}
|
|
2316
|
+
return { key, previous, legacySecret, legacyRoot, legacyRootPrevious };
|
|
2317
|
+
}
|
|
2318
|
+
var UnknownOriginError = class extends Error {
|
|
2319
|
+
constructor(origin, registered) {
|
|
2320
|
+
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`);
|
|
2321
|
+
this.origin = origin;
|
|
2322
|
+
this.registered = registered;
|
|
2323
|
+
}
|
|
2324
|
+
origin;
|
|
2325
|
+
registered;
|
|
2326
|
+
};
|
|
2327
|
+
function contractOnKey(given, keyed) {
|
|
2328
|
+
const contract = readContract(given.contract);
|
|
2329
|
+
if (contract.issuer !== keyed.key.issuer) {
|
|
2330
|
+
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`);
|
|
2331
|
+
}
|
|
2332
|
+
return contract;
|
|
2333
|
+
}
|
|
1882
2334
|
function byContract(given) {
|
|
1883
2335
|
failClosed(typeof process !== "undefined" ? process.env : void 0);
|
|
1884
2336
|
for (const [name, value] of [
|
|
@@ -1897,27 +2349,54 @@ function byContract(given) {
|
|
|
1897
2349
|
if (contract.serviceClientId && !given.keys) {
|
|
1898
2350
|
throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
|
|
1899
2351
|
}
|
|
2352
|
+
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.");
|
|
1900
2353
|
return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
|
|
1901
2354
|
}
|
|
1902
2355
|
var toldOfHeader = false;
|
|
2356
|
+
var toldOfUnopened = false;
|
|
2357
|
+
function toldUnopened() {
|
|
2358
|
+
if (toldOfUnopened) return;
|
|
2359
|
+
toldOfUnopened = true;
|
|
2360
|
+
console.warn("@cauthyhq/node: a session cookie arrived that no key of this application opens; it was cleared and the person is signed out. Moving to CAUTHY_KEY, keep the secret that sealed the old sessions (CAUTHY_SECRET, or PACT_ROOT_KEY and PACT_ROOT_KEY_PREVIOUS) beside it for the rotation window; rotating the key, set CAUTHY_KEY_PREVIOUS.");
|
|
2361
|
+
}
|
|
1903
2362
|
function createCauthy(given = {}) {
|
|
1904
|
-
const
|
|
1905
|
-
const
|
|
1906
|
-
const
|
|
1907
|
-
const
|
|
1908
|
-
const
|
|
1909
|
-
const
|
|
1910
|
-
const
|
|
1911
|
-
const
|
|
2363
|
+
const keyed = byKey(given);
|
|
2364
|
+
const pacted = given.contract && !keyed ? byContract(given) : void 0;
|
|
2365
|
+
const contract = keyed && given.contract ? contractOnKey(given, keyed) : pacted?.contract;
|
|
2366
|
+
const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? contract?.issuer ?? env3("CAUTHY_ISSUER") ?? "");
|
|
2367
|
+
const clientId = contract?.clientId ?? keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
|
|
2368
|
+
const clientSecret = contract || keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
|
|
2369
|
+
const secret = contract || keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
|
|
2370
|
+
const askSelf = keyed !== void 0 && contract === void 0;
|
|
2371
|
+
const system = askSelf ? "" : contract?.system ?? given.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
2372
|
+
const tenant = askSelf ? "" : contract?.tenant ?? given.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
1912
2373
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
1913
2374
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
1914
|
-
if (!contract && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2375
|
+
if (!contract && !keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2376
|
+
const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
|
|
2377
|
+
let signingKid;
|
|
2378
|
+
fromKey?.signer.then((s) => {
|
|
2379
|
+
signingKid = s.kid;
|
|
2380
|
+
}, () => void 0);
|
|
1915
2381
|
const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
|
|
1916
|
-
const single = derived ? void 0 : keyOf(secret).then((k) => [k]);
|
|
1917
|
-
const
|
|
1918
|
-
const
|
|
1919
|
-
const
|
|
1920
|
-
|
|
2382
|
+
const single = derived || fromKey ? void 0 : keyOf(secret).then((k) => [k]);
|
|
2383
|
+
const legacyRings = keyed?.legacyRoot ? deriveKeys(keyed.legacyRoot, keyed.legacyRootPrevious) : void 0;
|
|
2384
|
+
const legacySingle = keyed?.legacySecret ? keyOf(keyed.legacySecret) : void 0;
|
|
2385
|
+
const withLegacy = async (own, purpose) => [
|
|
2386
|
+
...await own,
|
|
2387
|
+
...legacyRings ? await legacyRings[purpose] : [],
|
|
2388
|
+
...legacySingle ? [await legacySingle] : []
|
|
2389
|
+
];
|
|
2390
|
+
const cookieKeys = fromKey ? withLegacy(fromKey.cookie, "cookie") : derived?.cookie ?? single;
|
|
2391
|
+
const flowKeys = fromKey ? withLegacy(fromKey.flow, "flow") : derived?.flow ?? single;
|
|
2392
|
+
if (!keyed && !pacted) {
|
|
2393
|
+
deprecated(
|
|
2394
|
+
"Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
|
|
2395
|
+
"Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
|
|
2399
|
+
const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
|
|
1921
2400
|
if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
|
|
1922
2401
|
throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
|
|
1923
2402
|
}
|
|
@@ -1953,18 +2432,65 @@ function createCauthy(given = {}) {
|
|
|
1953
2432
|
acceptInviteUrl,
|
|
1954
2433
|
fetch: given.fetch,
|
|
1955
2434
|
timeoutMs: systemTimeout,
|
|
1956
|
-
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {}
|
|
2435
|
+
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {},
|
|
2436
|
+
// With a contract the key proves the link's service client; without, the key's own client.
|
|
2437
|
+
...fromKey ? {
|
|
2438
|
+
signer: { clientId: contract ? contract.serviceClientId ?? keyed.key.clientId : clientId, signer: fromKey.signer },
|
|
2439
|
+
currentSystem: () => identity?.current()?.system ?? ""
|
|
2440
|
+
} : {}
|
|
1957
2441
|
});
|
|
1958
|
-
const
|
|
1959
|
-
|
|
2442
|
+
const selfOptions = () => ({
|
|
2443
|
+
issuer,
|
|
2444
|
+
token: () => ownSystem.token(),
|
|
2445
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2446
|
+
kid: () => signingKid,
|
|
2447
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2448
|
+
});
|
|
2449
|
+
const identity = askSelf ? createIdentity({
|
|
2450
|
+
issuer,
|
|
2451
|
+
token: () => ownSystem.token(),
|
|
2452
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2453
|
+
kid: () => signingKid,
|
|
2454
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2455
|
+
}) : void 0;
|
|
2456
|
+
identity?.ready().catch(() => {
|
|
2457
|
+
});
|
|
2458
|
+
const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
|
|
2459
|
+
const systemNow = () => identity?.current()?.system ?? system;
|
|
2460
|
+
const requestOrigin = (request) => {
|
|
1960
2461
|
const url = new URL(request.url);
|
|
1961
2462
|
const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
|
|
1962
2463
|
return proto === "https" || proto === "http" ? `${proto}://${url.host}` : url.origin;
|
|
1963
2464
|
};
|
|
1964
|
-
const
|
|
2465
|
+
const originOf = (request) => {
|
|
2466
|
+
if (baseURL) return withoutTrailingSlashes(baseURL);
|
|
2467
|
+
const asked = requestOrigin(request);
|
|
2468
|
+
if (!identity) return asked;
|
|
2469
|
+
const urls2 = identity.current()?.app_urls ?? [];
|
|
2470
|
+
if (urls2.includes(asked)) return asked;
|
|
2471
|
+
if (urls2.length === 1) return urls2[0];
|
|
2472
|
+
throw new UnknownOriginError(asked, urls2);
|
|
2473
|
+
};
|
|
2474
|
+
const safeOrigin = (request) => {
|
|
2475
|
+
try {
|
|
2476
|
+
return originOf(request);
|
|
2477
|
+
} catch {
|
|
2478
|
+
return requestOrigin(request);
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
|
|
2482
|
+
async function clientAuth(form) {
|
|
2483
|
+
if (fromKey && !contract) {
|
|
2484
|
+
const s = await fromKey.signer;
|
|
2485
|
+
form.set("client_assertion_type", ASSERTION_TYPE2);
|
|
2486
|
+
form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
|
|
2487
|
+
} else if (clientSecret) {
|
|
2488
|
+
form.set("client_secret", clientSecret);
|
|
2489
|
+
}
|
|
2490
|
+
return form;
|
|
2491
|
+
}
|
|
1965
2492
|
async function token(form, proof = {}) {
|
|
1966
|
-
const body2 = new URLSearchParams({ client_id: clientId, ...form });
|
|
1967
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2493
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
|
|
1968
2494
|
try {
|
|
1969
2495
|
const res = await doFetch(`${issuer}/v1/oauth/token`, {
|
|
1970
2496
|
method: "POST",
|
|
@@ -2003,8 +2529,7 @@ function createCauthy(given = {}) {
|
|
|
2003
2529
|
}
|
|
2004
2530
|
const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
|
|
2005
2531
|
async function revoke(refreshToken) {
|
|
2006
|
-
const form = new URLSearchParams({ client_id: clientId, token: refreshToken });
|
|
2007
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
2532
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: refreshToken }));
|
|
2008
2533
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2009
2534
|
}
|
|
2010
2535
|
const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
|
|
@@ -2025,10 +2550,11 @@ function createCauthy(given = {}) {
|
|
|
2025
2550
|
async function read(request) {
|
|
2026
2551
|
const had = readCookies(request.headers.get("cookie"));
|
|
2027
2552
|
const raw = readParts(had, cookieName);
|
|
2028
|
-
if (raw === null) return { had, value: null, present: false };
|
|
2029
|
-
const
|
|
2553
|
+
if (raw === null) return { had, value: null, present: false, stale: false };
|
|
2554
|
+
const opened = await unsealIndexed(raw, await cookieKeys);
|
|
2555
|
+
const value = opened?.value ?? null;
|
|
2030
2556
|
const sound = value !== null && typeof value.at === "string" && typeof value.rt === "string" && typeof value.exp === "number";
|
|
2031
|
-
return { had, value: sound ? value : null, present: true };
|
|
2557
|
+
return { had, value: sound ? value : null, present: true, stale: sound && opened.index > 0 };
|
|
2032
2558
|
}
|
|
2033
2559
|
async function write(request, s, had) {
|
|
2034
2560
|
return writeParts(cookieName, await seal(s, (await cookieKeys)[0]), attrs(request, s.p === 1 ? PERSISTENT_SECONDS : void 0), had);
|
|
@@ -2065,12 +2591,13 @@ function createCauthy(given = {}) {
|
|
|
2065
2591
|
return running;
|
|
2066
2592
|
}
|
|
2067
2593
|
async function resolve2(request) {
|
|
2068
|
-
const { had, value, present } = await read(request);
|
|
2594
|
+
const { had, value, present, stale } = await read(request);
|
|
2069
2595
|
if (value === null) {
|
|
2596
|
+
if (present) toldUnopened();
|
|
2070
2597
|
return { session: null, status: "unauthenticated", cookies: present ? clearParts(cookieName, attrs(request, 0), had) : [] };
|
|
2071
2598
|
}
|
|
2072
2599
|
if (unverified(value.u)) return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
|
|
2073
|
-
if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: [] };
|
|
2600
|
+
if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: stale ? await write(request, value, had) : [] };
|
|
2074
2601
|
const renewed = await renew(value);
|
|
2075
2602
|
if (renewed === "ended") {
|
|
2076
2603
|
return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
|
|
@@ -2096,7 +2623,8 @@ function createCauthy(given = {}) {
|
|
|
2096
2623
|
async function organizations(session) {
|
|
2097
2624
|
const hit = orgCache.get(session.accessToken);
|
|
2098
2625
|
if (hit && Date.now() - hit.at < ORGANIZATIONS_TTL_MS) return hit.value;
|
|
2099
|
-
const
|
|
2626
|
+
const sys = systemNow();
|
|
2627
|
+
const url = `${issuer}/v1/oauth/organizations${sys ? `?system=${encodeURIComponent(sys)}` : ""}`;
|
|
2100
2628
|
const res = await doFetch(url, { headers: { authorization: `Bearer ${session.accessToken}` } });
|
|
2101
2629
|
if (!res.ok) throw new Error(`@cauthyhq/node: reading organizations: ${res.status}`);
|
|
2102
2630
|
const body2 = await res.json();
|
|
@@ -2250,8 +2778,7 @@ function createCauthy(given = {}) {
|
|
|
2250
2778
|
async function logout(request) {
|
|
2251
2779
|
const { had, value } = await read(request);
|
|
2252
2780
|
if (value !== null) {
|
|
2253
|
-
const body2 = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2254
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2781
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2255
2782
|
await doFetch(`${issuer}/v1/oauth/revoke`, {
|
|
2256
2783
|
method: "POST",
|
|
2257
2784
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
@@ -2375,7 +2902,7 @@ function createCauthy(given = {}) {
|
|
|
2375
2902
|
const res = await doFetch(`${issuer}${path}`, {
|
|
2376
2903
|
method,
|
|
2377
2904
|
headers: {
|
|
2378
|
-
"x-tenant-key":
|
|
2905
|
+
"x-tenant-key": tenantNow(),
|
|
2379
2906
|
accept: "application/json",
|
|
2380
2907
|
// A link Cauthy mails opens the application's page for it when it
|
|
2381
2908
|
// has one; otherwise Cauthy's own. Either way in the reader's language.
|
|
@@ -2521,7 +3048,7 @@ function createCauthy(given = {}) {
|
|
|
2521
3048
|
}
|
|
2522
3049
|
if (route === "/sign-in/anonymous" || route === "/me/upgrade") deprecated(`POST ${route}`, "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2523
3050
|
if (route.startsWith("/me/upgrade/social/")) deprecated("GET /me/upgrade/social/{provider}", "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2524
|
-
if (
|
|
3051
|
+
if (tenantNow() === "") {
|
|
2525
3052
|
return problem(500, "not_configured", "@cauthyhq/node: tenant (CAUTHY_TENANT) is required for the application's own sign-in screens");
|
|
2526
3053
|
}
|
|
2527
3054
|
if (route === "/look" && !post) {
|
|
@@ -2756,7 +3283,7 @@ function createCauthy(given = {}) {
|
|
|
2756
3283
|
let upstream;
|
|
2757
3284
|
try {
|
|
2758
3285
|
upstream = await doFetch(`${issuer}/v1/auth/mfa/push/events`, {
|
|
2759
|
-
headers: { "x-tenant-key":
|
|
3286
|
+
headers: { "x-tenant-key": tenantNow(), "x-mfa-token": step.token, accept: "text/event-stream" },
|
|
2760
3287
|
signal: request.signal
|
|
2761
3288
|
});
|
|
2762
3289
|
} catch {
|
|
@@ -2776,8 +3303,7 @@ function createCauthy(given = {}) {
|
|
|
2776
3303
|
if (route === "/sign-out") {
|
|
2777
3304
|
const { had, value } = await read(request);
|
|
2778
3305
|
if (value !== null) {
|
|
2779
|
-
const form = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2780
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
3306
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2781
3307
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2782
3308
|
orgCache.delete(value.at);
|
|
2783
3309
|
}
|
|
@@ -3176,14 +3702,38 @@ function createCauthy(given = {}) {
|
|
|
3176
3702
|
const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
|
|
3177
3703
|
const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
|
|
3178
3704
|
const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
|
|
3705
|
+
if (identity && route !== null) {
|
|
3706
|
+
const unready = await identityGate();
|
|
3707
|
+
if (unready) return finish(unready, route);
|
|
3708
|
+
}
|
|
3179
3709
|
try {
|
|
3180
3710
|
return finish(await serve(request), route, published3);
|
|
3181
3711
|
} catch (err) {
|
|
3712
|
+
if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
|
|
3182
3713
|
if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
|
|
3183
3714
|
const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
|
|
3184
3715
|
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);
|
|
3185
3716
|
}
|
|
3186
3717
|
}
|
|
3718
|
+
async function identityGate() {
|
|
3719
|
+
if (!identity) return null;
|
|
3720
|
+
const unavailable = () => problem(503, "unavailable", "Cauthy cannot be reached yet. Try again in a moment.");
|
|
3721
|
+
if (identity.failing()) return unavailable();
|
|
3722
|
+
let timer;
|
|
3723
|
+
const waited = new Promise((resolve3) => {
|
|
3724
|
+
timer = setTimeout(() => resolve3("timeout"), requestTimeout);
|
|
3725
|
+
});
|
|
3726
|
+
try {
|
|
3727
|
+
const ready = identity.ready();
|
|
3728
|
+
const got = await Promise.race([ready.then(() => "ok"), identity.attempt().then((ok) => ok ? "ok" : "failed"), waited]);
|
|
3729
|
+
if (got === "failed") await Promise.race([ready, Promise.resolve()]);
|
|
3730
|
+
return got === "ok" ? null : unavailable();
|
|
3731
|
+
} catch (e) {
|
|
3732
|
+
return e.code === "misconfigured" ? problem(503, "misconfigured", "Cauthy refuses this application's key: make a new one in Cauthy's dashboard.") : unavailable();
|
|
3733
|
+
} finally {
|
|
3734
|
+
clearTimeout(timer);
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3187
3737
|
async function serve(request) {
|
|
3188
3738
|
const url = new URL(request.url);
|
|
3189
3739
|
if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
|
|
@@ -3266,7 +3816,28 @@ function createCauthy(given = {}) {
|
|
|
3266
3816
|
return { response: redirect(to) };
|
|
3267
3817
|
}
|
|
3268
3818
|
const replay = given.webhookReplay ?? memoryReplayStore();
|
|
3819
|
+
let lateSelf;
|
|
3269
3820
|
const tokenVerifier = (o = {}) => {
|
|
3821
|
+
if (keyed) {
|
|
3822
|
+
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3823
|
+
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3824
|
+
}
|
|
3825
|
+
let built;
|
|
3826
|
+
return async (from) => {
|
|
3827
|
+
const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
|
|
3828
|
+
if (built?.from !== id) {
|
|
3829
|
+
built = { from: id, verify: createTokenVerifier({
|
|
3830
|
+
issuer,
|
|
3831
|
+
tenantId: contract?.tenantId ?? id.tenant.id,
|
|
3832
|
+
audience: id.audience,
|
|
3833
|
+
fetch: given.fetch,
|
|
3834
|
+
...contract ? { clientId: contract.clientId } : {},
|
|
3835
|
+
...o
|
|
3836
|
+
}) };
|
|
3837
|
+
}
|
|
3838
|
+
return built.verify(from);
|
|
3839
|
+
};
|
|
3840
|
+
}
|
|
3270
3841
|
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3271
3842
|
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3272
3843
|
}
|
|
@@ -3280,7 +3851,30 @@ function createCauthy(given = {}) {
|
|
|
3280
3851
|
});
|
|
3281
3852
|
};
|
|
3282
3853
|
const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now, replay });
|
|
3283
|
-
|
|
3854
|
+
async function keyJwks() {
|
|
3855
|
+
const s = await fromKey.signer;
|
|
3856
|
+
const keys2 = [{ ...s.publicJwk, kid: s.kid, alg: "EdDSA", use: "sig" }];
|
|
3857
|
+
if (fromKey.previousJwk) keys2.push({ ...await fromKey.previousJwk, alg: "EdDSA", use: "sig" });
|
|
3858
|
+
if (ownKeys) keys2.push(...(await ownKeys.published()).keys);
|
|
3859
|
+
return new Response(JSON.stringify({ keys: keys2 }), {
|
|
3860
|
+
headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
const noStore = () => {
|
|
3864
|
+
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");
|
|
3865
|
+
};
|
|
3866
|
+
const keyOnly = {
|
|
3867
|
+
current: async () => noStore(),
|
|
3868
|
+
published: async () => ({ keys: [] }),
|
|
3869
|
+
rotate: async () => noStore(),
|
|
3870
|
+
reseal: async () => 0
|
|
3871
|
+
};
|
|
3872
|
+
const pact = contract && fromKey ? {
|
|
3873
|
+
contract,
|
|
3874
|
+
keys: ownKeys ?? keyOnly,
|
|
3875
|
+
jwks: keyJwks,
|
|
3876
|
+
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3877
|
+
} : contract && ownKeys ? {
|
|
3284
3878
|
contract,
|
|
3285
3879
|
keys: ownKeys,
|
|
3286
3880
|
async jwks() {
|
|
@@ -3290,8 +3884,25 @@ function createCauthy(given = {}) {
|
|
|
3290
3884
|
},
|
|
3291
3885
|
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3292
3886
|
} : void 0;
|
|
3887
|
+
const configured = () => ({
|
|
3888
|
+
issuer,
|
|
3889
|
+
audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
|
|
3890
|
+
tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
|
|
3891
|
+
system,
|
|
3892
|
+
client_id: clientId,
|
|
3893
|
+
sign_in_client_id: clientId,
|
|
3894
|
+
app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
|
|
3895
|
+
callback_path: `${basePath}/callback`,
|
|
3896
|
+
key: null,
|
|
3897
|
+
contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
|
|
3898
|
+
});
|
|
3293
3899
|
return {
|
|
3294
3900
|
handler,
|
|
3901
|
+
ready: async () => {
|
|
3902
|
+
if (identity) return identity.ready();
|
|
3903
|
+
const id = configured();
|
|
3904
|
+
return fromKey ? { ...id, key: { kid: (await fromKey.signer).kid, retires_at: null } } : id;
|
|
3905
|
+
},
|
|
3295
3906
|
checkMount: (target) => checkMount(basePath, target),
|
|
3296
3907
|
webhook,
|
|
3297
3908
|
tokenVerifier,
|
|
@@ -3381,10 +3992,12 @@ async function connectSystem(o) {
|
|
|
3381
3992
|
export {
|
|
3382
3993
|
CauthyApiError,
|
|
3383
3994
|
CauthyConfigError,
|
|
3995
|
+
CauthyKeyError,
|
|
3384
3996
|
CauthyTokenError,
|
|
3385
3997
|
CauthyUnavailableError,
|
|
3386
3998
|
CauthyWebhookError,
|
|
3387
3999
|
DEFAULT_PASSWORD_POLICY,
|
|
4000
|
+
KEY_REPLACES,
|
|
3388
4001
|
MIN_PASSWORD,
|
|
3389
4002
|
PROTOCOL_VERSION,
|
|
3390
4003
|
STANDARD_VARIABLES,
|
|
@@ -3394,15 +4007,18 @@ export {
|
|
|
3394
4007
|
checkName,
|
|
3395
4008
|
checkNewPassword,
|
|
3396
4009
|
checkPassword,
|
|
4010
|
+
composeKey,
|
|
3397
4011
|
connectSystem,
|
|
3398
4012
|
createCauthy,
|
|
3399
4013
|
createSystem,
|
|
3400
4014
|
createTokenVerifier,
|
|
4015
|
+
createVerifierFromEnv,
|
|
3401
4016
|
definePolicy,
|
|
3402
4017
|
describePasswordPolicy,
|
|
3403
4018
|
explainPasswordProblems,
|
|
3404
4019
|
failClosed,
|
|
3405
4020
|
memoryReplayStore,
|
|
4021
|
+
parseKey,
|
|
3406
4022
|
passwordPolicyOf,
|
|
3407
4023
|
passwordProblems,
|
|
3408
4024
|
policySource,
|