@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.js
CHANGED
|
@@ -22,10 +22,12 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
CauthyApiError: () => CauthyApiError,
|
|
24
24
|
CauthyConfigError: () => CauthyConfigError,
|
|
25
|
+
CauthyKeyError: () => CauthyKeyError,
|
|
25
26
|
CauthyTokenError: () => CauthyTokenError,
|
|
26
27
|
CauthyUnavailableError: () => CauthyUnavailableError,
|
|
27
28
|
CauthyWebhookError: () => CauthyWebhookError,
|
|
28
29
|
DEFAULT_PASSWORD_POLICY: () => DEFAULT_PASSWORD_POLICY,
|
|
30
|
+
KEY_REPLACES: () => KEY_REPLACES,
|
|
29
31
|
MIN_PASSWORD: () => MIN_PASSWORD,
|
|
30
32
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
31
33
|
STANDARD_VARIABLES: () => STANDARD_VARIABLES,
|
|
@@ -35,15 +37,18 @@ __export(src_exports, {
|
|
|
35
37
|
checkName: () => checkName,
|
|
36
38
|
checkNewPassword: () => checkNewPassword,
|
|
37
39
|
checkPassword: () => checkPassword,
|
|
40
|
+
composeKey: () => composeKey,
|
|
38
41
|
connectSystem: () => connectSystem,
|
|
39
42
|
createCauthy: () => createCauthy,
|
|
40
43
|
createSystem: () => createSystem,
|
|
41
44
|
createTokenVerifier: () => createTokenVerifier,
|
|
45
|
+
createVerifierFromEnv: () => createVerifierFromEnv,
|
|
42
46
|
definePolicy: () => definePolicy,
|
|
43
47
|
describePasswordPolicy: () => describePasswordPolicy,
|
|
44
48
|
explainPasswordProblems: () => explainPasswordProblems,
|
|
45
49
|
failClosed: () => failClosed,
|
|
46
50
|
memoryReplayStore: () => memoryReplayStore,
|
|
51
|
+
parseKey: () => parseKey,
|
|
47
52
|
passwordPolicyOf: () => passwordPolicyOf,
|
|
48
53
|
passwordProblems: () => passwordProblems,
|
|
49
54
|
policySource: () => policySource,
|
|
@@ -131,6 +136,14 @@ var CauthyConfigError = class extends Error {
|
|
|
131
136
|
this.name = "CauthyConfigError";
|
|
132
137
|
}
|
|
133
138
|
};
|
|
139
|
+
var CauthyKeyError = class extends CauthyConfigError {
|
|
140
|
+
constructor(reason, message) {
|
|
141
|
+
super(`CAUTHY_KEY: ${message} (${reason})`);
|
|
142
|
+
this.reason = reason;
|
|
143
|
+
this.name = "CauthyKeyError";
|
|
144
|
+
}
|
|
145
|
+
reason;
|
|
146
|
+
};
|
|
134
147
|
|
|
135
148
|
// src/url.ts
|
|
136
149
|
function withoutTrailingSlashes(s) {
|
|
@@ -1041,6 +1054,7 @@ function createSystem(given = {}) {
|
|
|
1041
1054
|
};
|
|
1042
1055
|
const tokenEndpoint = `${issuer}/v1/oauth/token`;
|
|
1043
1056
|
function configured() {
|
|
1057
|
+
if (given.signer) return;
|
|
1044
1058
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
1045
1059
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
1046
1060
|
if (given.keys) {
|
|
@@ -1053,7 +1067,7 @@ function createSystem(given = {}) {
|
|
|
1053
1067
|
}
|
|
1054
1068
|
let imported;
|
|
1055
1069
|
const signingKey = () => imported ??= loadKey(key);
|
|
1056
|
-
async function
|
|
1070
|
+
async function fail2(res, what) {
|
|
1057
1071
|
const body = await res.json().catch(() => null);
|
|
1058
1072
|
const first = body?.errors?.[0];
|
|
1059
1073
|
const code = typeof body?.error === "string" ? body.error : first?.code;
|
|
@@ -1064,9 +1078,13 @@ function createSystem(given = {}) {
|
|
|
1064
1078
|
let asking;
|
|
1065
1079
|
async function ask() {
|
|
1066
1080
|
configured();
|
|
1067
|
-
const self = given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1081
|
+
const self = given.signer ? given.signer.clientId : given.keys ? given.keysClientId : key?.clientId || clientId;
|
|
1068
1082
|
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
|
|
1069
|
-
if (given.
|
|
1083
|
+
if (given.signer) {
|
|
1084
|
+
const s = await given.signer.signer;
|
|
1085
|
+
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
1086
|
+
form.set("client_assertion", await s.assertion(self, tokenEndpoint));
|
|
1087
|
+
} else if (given.keys || key) {
|
|
1070
1088
|
const signer = given.keys ? await given.keys.current() : { kid: key.keyId, privateKey: await signingKey() };
|
|
1071
1089
|
const assertion = await new import_jose3.SignJWT({}).setProtectedHeader({ alg: "RS256", kid: signer.kid }).setIssuer(self).setSubject(self).setAudience(tokenEndpoint).setJti(crypto.randomUUID()).setIssuedAt().setExpirationTime("2m").sign(signer.privateKey);
|
|
1072
1090
|
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
@@ -1075,7 +1093,7 @@ function createSystem(given = {}) {
|
|
|
1075
1093
|
form.set("client_secret", clientSecret);
|
|
1076
1094
|
}
|
|
1077
1095
|
const res = await doFetch(tokenEndpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
|
|
1078
|
-
if (!res.ok) await
|
|
1096
|
+
if (!res.ok) await fail2(res, "the application's own token");
|
|
1079
1097
|
const t = await res.json();
|
|
1080
1098
|
held = { value: t.access_token, until: Date.now() + t.expires_in * 1e3 - SKEW_MS };
|
|
1081
1099
|
return t.access_token;
|
|
@@ -1097,7 +1115,7 @@ function createSystem(given = {}) {
|
|
|
1097
1115
|
held = void 0;
|
|
1098
1116
|
res = await send();
|
|
1099
1117
|
}
|
|
1100
|
-
if (!res.ok) await
|
|
1118
|
+
if (!res.ok) await fail2(res, `${method} ${path}`);
|
|
1101
1119
|
return res.status === 204 ? void 0 : res.json();
|
|
1102
1120
|
}
|
|
1103
1121
|
const data = async (method, path, body, extra) => (await call(method, path, body, extra))?.data;
|
|
@@ -1171,7 +1189,7 @@ function createSystem(given = {}) {
|
|
|
1171
1189
|
}
|
|
1172
1190
|
},
|
|
1173
1191
|
async check(q) {
|
|
1174
|
-
const system = q.system ?? ownSystem;
|
|
1192
|
+
const system = q.system ?? (ownSystem || given.currentSystem?.() || "");
|
|
1175
1193
|
if (system === "") throw new CauthyConfigError("name the system to check in (system, or CAUTHY_SYSTEM)");
|
|
1176
1194
|
return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
|
|
1177
1195
|
},
|
|
@@ -1341,6 +1359,219 @@ function firstProblem(...checks) {
|
|
|
1341
1359
|
|
|
1342
1360
|
// src/webhook.ts
|
|
1343
1361
|
var import_jose4 = require("jose");
|
|
1362
|
+
|
|
1363
|
+
// src/sha256.ts
|
|
1364
|
+
var K = new Uint32Array([
|
|
1365
|
+
1116352408,
|
|
1366
|
+
1899447441,
|
|
1367
|
+
3049323471,
|
|
1368
|
+
3921009573,
|
|
1369
|
+
961987163,
|
|
1370
|
+
1508970993,
|
|
1371
|
+
2453635748,
|
|
1372
|
+
2870763221,
|
|
1373
|
+
3624381080,
|
|
1374
|
+
310598401,
|
|
1375
|
+
607225278,
|
|
1376
|
+
1426881987,
|
|
1377
|
+
1925078388,
|
|
1378
|
+
2162078206,
|
|
1379
|
+
2614888103,
|
|
1380
|
+
3248222580,
|
|
1381
|
+
3835390401,
|
|
1382
|
+
4022224774,
|
|
1383
|
+
264347078,
|
|
1384
|
+
604807628,
|
|
1385
|
+
770255983,
|
|
1386
|
+
1249150122,
|
|
1387
|
+
1555081692,
|
|
1388
|
+
1996064986,
|
|
1389
|
+
2554220882,
|
|
1390
|
+
2821834349,
|
|
1391
|
+
2952996808,
|
|
1392
|
+
3210313671,
|
|
1393
|
+
3336571891,
|
|
1394
|
+
3584528711,
|
|
1395
|
+
113926993,
|
|
1396
|
+
338241895,
|
|
1397
|
+
666307205,
|
|
1398
|
+
773529912,
|
|
1399
|
+
1294757372,
|
|
1400
|
+
1396182291,
|
|
1401
|
+
1695183700,
|
|
1402
|
+
1986661051,
|
|
1403
|
+
2177026350,
|
|
1404
|
+
2456956037,
|
|
1405
|
+
2730485921,
|
|
1406
|
+
2820302411,
|
|
1407
|
+
3259730800,
|
|
1408
|
+
3345764771,
|
|
1409
|
+
3516065817,
|
|
1410
|
+
3600352804,
|
|
1411
|
+
4094571909,
|
|
1412
|
+
275423344,
|
|
1413
|
+
430227734,
|
|
1414
|
+
506948616,
|
|
1415
|
+
659060556,
|
|
1416
|
+
883997877,
|
|
1417
|
+
958139571,
|
|
1418
|
+
1322822218,
|
|
1419
|
+
1537002063,
|
|
1420
|
+
1747873779,
|
|
1421
|
+
1955562222,
|
|
1422
|
+
2024104815,
|
|
1423
|
+
2227730452,
|
|
1424
|
+
2361852424,
|
|
1425
|
+
2428436474,
|
|
1426
|
+
2756734187,
|
|
1427
|
+
3204031479,
|
|
1428
|
+
3329325298
|
|
1429
|
+
]);
|
|
1430
|
+
function sha256(data) {
|
|
1431
|
+
const h = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
1432
|
+
const len = data.length;
|
|
1433
|
+
const padded = new Uint8Array(len + 9 + 63 >> 6 << 6);
|
|
1434
|
+
padded.set(data);
|
|
1435
|
+
padded[len] = 128;
|
|
1436
|
+
const view = new DataView(padded.buffer);
|
|
1437
|
+
view.setUint32(padded.length - 8, Math.floor(len / 536870912));
|
|
1438
|
+
view.setUint32(padded.length - 4, len << 3 >>> 0);
|
|
1439
|
+
const w = new Uint32Array(64);
|
|
1440
|
+
const rotr = (x, n) => x >>> n | x << 32 - n;
|
|
1441
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
1442
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
|
|
1443
|
+
for (let i = 16; i < 64; i++) {
|
|
1444
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
1445
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
1446
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
1447
|
+
}
|
|
1448
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
1449
|
+
for (let i = 0; i < 64; i++) {
|
|
1450
|
+
const t1 = hh + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + (e & f ^ ~e & g) + K[i] + w[i] >>> 0;
|
|
1451
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
|
|
1452
|
+
hh = g;
|
|
1453
|
+
g = f;
|
|
1454
|
+
f = e;
|
|
1455
|
+
e = d + t1 >>> 0;
|
|
1456
|
+
d = c;
|
|
1457
|
+
c = b;
|
|
1458
|
+
b = a;
|
|
1459
|
+
a = t1 + t2 >>> 0;
|
|
1460
|
+
}
|
|
1461
|
+
h[0] += a;
|
|
1462
|
+
h[1] += b;
|
|
1463
|
+
h[2] += c;
|
|
1464
|
+
h[3] += d;
|
|
1465
|
+
h[4] += e;
|
|
1466
|
+
h[5] += f;
|
|
1467
|
+
h[6] += g;
|
|
1468
|
+
h[7] += hh;
|
|
1469
|
+
}
|
|
1470
|
+
const out2 = new Uint8Array(32);
|
|
1471
|
+
const ov = new DataView(out2.buffer);
|
|
1472
|
+
for (let i = 0; i < 8; i++) ov.setUint32(i * 4, h[i]);
|
|
1473
|
+
return out2;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/cauthykey.ts
|
|
1477
|
+
var KEY_PREFIX = "cauthy1";
|
|
1478
|
+
var KEY_REPLACES = [
|
|
1479
|
+
"CAUTHY_ISSUER",
|
|
1480
|
+
"CAUTHY_CLIENT_ID",
|
|
1481
|
+
"CAUTHY_CLIENT_SECRET",
|
|
1482
|
+
"CAUTHY_SYSTEM",
|
|
1483
|
+
"CAUTHY_TENANT",
|
|
1484
|
+
"CAUTHY_SYSTEM_KEY",
|
|
1485
|
+
"CAUTHY_SYSTEM_KEY_ID",
|
|
1486
|
+
"CAUTHY_BASE_URL",
|
|
1487
|
+
"CAUTHY_AUDIENCE",
|
|
1488
|
+
"CAUTHY_TENANT_ID"
|
|
1489
|
+
];
|
|
1490
|
+
function refuseBesideKey(environment) {
|
|
1491
|
+
const set = KEY_REPLACES.filter((n) => (environment?.[n] ?? "") !== "");
|
|
1492
|
+
if (set.length > 0) {
|
|
1493
|
+
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.`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
var MAX_LEN = 512;
|
|
1497
|
+
var enc = new TextEncoder();
|
|
1498
|
+
var dec = new TextDecoder("utf-8", { fatal: true });
|
|
1499
|
+
function b64uEncode(b) {
|
|
1500
|
+
let bin = "";
|
|
1501
|
+
for (const x of b) bin += String.fromCharCode(x);
|
|
1502
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1503
|
+
}
|
|
1504
|
+
function b64uDecode(s) {
|
|
1505
|
+
if (!/^[A-Za-z0-9_-]*$/.test(s) || s.length % 4 === 1) return null;
|
|
1506
|
+
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4));
|
|
1507
|
+
const out2 = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
1508
|
+
return b64uEncode(out2) === s ? out2 : null;
|
|
1509
|
+
}
|
|
1510
|
+
function checksum(body) {
|
|
1511
|
+
return b64uEncode(sha256(enc.encode(body)).slice(0, 6));
|
|
1512
|
+
}
|
|
1513
|
+
function printableAscii(s) {
|
|
1514
|
+
return /^[\x21-\x7e]*$/.test(s);
|
|
1515
|
+
}
|
|
1516
|
+
function validIssuer(s) {
|
|
1517
|
+
if (s === "" || !printableAscii(s)) return false;
|
|
1518
|
+
let u;
|
|
1519
|
+
try {
|
|
1520
|
+
u = new URL(s);
|
|
1521
|
+
} catch {
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
if (u.origin !== s) return false;
|
|
1525
|
+
if (/^\[::ffff:/i.test(u.host)) return false;
|
|
1526
|
+
if (u.protocol === "https:") return true;
|
|
1527
|
+
return u.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname);
|
|
1528
|
+
}
|
|
1529
|
+
var fail = (reason, message) => {
|
|
1530
|
+
throw new CauthyKeyError(reason, message);
|
|
1531
|
+
};
|
|
1532
|
+
var NOT_A_KEY = "not a Cauthy key (copy it again from the dashboard)";
|
|
1533
|
+
function parseKey(text2) {
|
|
1534
|
+
const s = text2.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
|
|
1535
|
+
if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
|
|
1536
|
+
const head = s.split(".", 1)[0];
|
|
1537
|
+
if (head !== KEY_PREFIX) {
|
|
1538
|
+
if (/^cauthy[0-9]+$/.test(head)) fail("key_version", `this key is for a newer SDK (${head}): update @cauthyhq/node`);
|
|
1539
|
+
fail("key_format", NOT_A_KEY);
|
|
1540
|
+
}
|
|
1541
|
+
const parts = s.split(".");
|
|
1542
|
+
if (parts.length !== 5 || parts.some((p) => p === "")) fail("key_format", NOT_A_KEY);
|
|
1543
|
+
if (!constantTimeEqual(checksum(parts.slice(0, 4).join(".")), parts[4])) {
|
|
1544
|
+
fail("key_checksum", "the key is damaged (part of it is missing or changed): copy it again from the dashboard");
|
|
1545
|
+
}
|
|
1546
|
+
const issuerBytes = b64uDecode(parts[1]);
|
|
1547
|
+
let issuer = "";
|
|
1548
|
+
try {
|
|
1549
|
+
issuer = issuerBytes ? dec.decode(issuerBytes) : "";
|
|
1550
|
+
} catch {
|
|
1551
|
+
issuer = "";
|
|
1552
|
+
}
|
|
1553
|
+
if (!validIssuer(issuer)) fail("key_issuer", "the key names no valid Cauthy address");
|
|
1554
|
+
const clientBytes = b64uDecode(parts[2]);
|
|
1555
|
+
const clientId = clientBytes ? String.fromCharCode(...clientBytes) : "";
|
|
1556
|
+
if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) fail("key_client", "the key names no valid client");
|
|
1557
|
+
const secret = b64uDecode(parts[3]);
|
|
1558
|
+
if (parts[3].length !== 43 || !secret || secret.length !== 32) fail("key_secret", "the key's secret is not 32 bytes");
|
|
1559
|
+
return { issuer, clientId, secret: parts[3] };
|
|
1560
|
+
}
|
|
1561
|
+
function composeKey(issuer, clientId, secret) {
|
|
1562
|
+
const body = `${KEY_PREFIX}.${b64uEncode(enc.encode(issuer))}.${b64uEncode(enc.encode(clientId))}.${secret}`;
|
|
1563
|
+
const key = `${body}.${checksum(body)}`;
|
|
1564
|
+
parseKey(key);
|
|
1565
|
+
return key;
|
|
1566
|
+
}
|
|
1567
|
+
function constantTimeEqual(a, b) {
|
|
1568
|
+
if (a.length !== b.length) return false;
|
|
1569
|
+
let d = 0;
|
|
1570
|
+
for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1571
|
+
return d === 0;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// src/webhook.ts
|
|
1344
1575
|
var CauthyWebhookError = class extends Error {
|
|
1345
1576
|
constructor(reason) {
|
|
1346
1577
|
super(`@cauthyhq/node: webhook refused: ${reason}`);
|
|
@@ -1375,7 +1606,8 @@ function keysOf(issuer) {
|
|
|
1375
1606
|
}
|
|
1376
1607
|
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
1377
1608
|
async function verifyWebhook(request, options = {}) {
|
|
1378
|
-
const
|
|
1609
|
+
const key = typeof process !== "undefined" ? process.env?.CAUTHY_KEY : void 0;
|
|
1610
|
+
const env4 = key ? parseKey(key).issuer : typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
1379
1611
|
const issuer = withoutTrailingSlashes(options.issuer ?? env4 ?? "");
|
|
1380
1612
|
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
1381
1613
|
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
@@ -1504,7 +1736,7 @@ var LIMIT = 2n ** 53n;
|
|
|
1504
1736
|
function parseStrict(text2, integerMembers = []) {
|
|
1505
1737
|
let i = 0;
|
|
1506
1738
|
let depth = 0;
|
|
1507
|
-
const
|
|
1739
|
+
const fail2 = (what) => {
|
|
1508
1740
|
throw new SyntaxError(`${what} at ${i}`);
|
|
1509
1741
|
};
|
|
1510
1742
|
const skip = () => {
|
|
@@ -1523,19 +1755,19 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1523
1755
|
return v;
|
|
1524
1756
|
}
|
|
1525
1757
|
}
|
|
1526
|
-
return
|
|
1758
|
+
return fail2("unexpected character");
|
|
1527
1759
|
};
|
|
1528
1760
|
const string = () => {
|
|
1529
1761
|
const start = i;
|
|
1530
1762
|
i++;
|
|
1531
1763
|
while (i < text2.length && text2[i] !== '"') i += text2[i] === "\\" ? 2 : 1;
|
|
1532
|
-
if (text2[i] !== '"')
|
|
1764
|
+
if (text2[i] !== '"') fail2("unterminated string");
|
|
1533
1765
|
i++;
|
|
1534
1766
|
return JSON.parse(text2.slice(start, i));
|
|
1535
1767
|
};
|
|
1536
1768
|
const number = (integer) => {
|
|
1537
1769
|
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text2.slice(i));
|
|
1538
|
-
if (!m) return
|
|
1770
|
+
if (!m) return fail2("bad number");
|
|
1539
1771
|
i += m[0].length;
|
|
1540
1772
|
if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
|
|
1541
1773
|
if (m[2] === void 0 && m[3] === void 0) {
|
|
@@ -1562,7 +1794,7 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1562
1794
|
i++;
|
|
1563
1795
|
depth--;
|
|
1564
1796
|
return out2;
|
|
1565
|
-
} else
|
|
1797
|
+
} else fail2("expected , or ]");
|
|
1566
1798
|
}
|
|
1567
1799
|
};
|
|
1568
1800
|
const object = () => {
|
|
@@ -1578,12 +1810,12 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1578
1810
|
}
|
|
1579
1811
|
for (; ; ) {
|
|
1580
1812
|
skip();
|
|
1581
|
-
if (text2[i] !== '"')
|
|
1813
|
+
if (text2[i] !== '"') fail2("expected a name");
|
|
1582
1814
|
const name = string();
|
|
1583
|
-
if (seen.has(name))
|
|
1815
|
+
if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
|
|
1584
1816
|
seen.add(name);
|
|
1585
1817
|
skip();
|
|
1586
|
-
if (text2[i] !== ":")
|
|
1818
|
+
if (text2[i] !== ":") fail2("expected :");
|
|
1587
1819
|
i++;
|
|
1588
1820
|
Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
|
|
1589
1821
|
skip();
|
|
@@ -1592,15 +1824,175 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1592
1824
|
i++;
|
|
1593
1825
|
depth--;
|
|
1594
1826
|
return out2;
|
|
1595
|
-
} else
|
|
1827
|
+
} else fail2("expected , or }");
|
|
1596
1828
|
}
|
|
1597
1829
|
};
|
|
1598
1830
|
const result = value();
|
|
1599
1831
|
skip();
|
|
1600
|
-
if (i < text2.length)
|
|
1832
|
+
if (i < text2.length) fail2("trailing characters");
|
|
1601
1833
|
return result;
|
|
1602
1834
|
}
|
|
1603
1835
|
|
|
1836
|
+
// src/deprecate.ts
|
|
1837
|
+
var told = /* @__PURE__ */ new Set();
|
|
1838
|
+
function deprecated(what, instead) {
|
|
1839
|
+
if (told.has(what)) return;
|
|
1840
|
+
told.add(what);
|
|
1841
|
+
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1842
|
+
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1843
|
+
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1844
|
+
} else {
|
|
1845
|
+
console.warn(message);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// src/keyderive.ts
|
|
1850
|
+
var enc2 = new TextEncoder();
|
|
1851
|
+
var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
|
1852
|
+
var ASSERTION_TYPE2 = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
1853
|
+
async function seedOf(secret) {
|
|
1854
|
+
const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
|
|
1855
|
+
return new Uint8Array(await crypto.subtle.deriveBits(
|
|
1856
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: enc2.encode("cauthy/sign/v1") },
|
|
1857
|
+
ikm,
|
|
1858
|
+
256
|
|
1859
|
+
));
|
|
1860
|
+
}
|
|
1861
|
+
async function thumbprint(x) {
|
|
1862
|
+
return b64uEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", enc2.encode(`{"crv":"Ed25519","kty":"OKP","x":"${x}"}`))));
|
|
1863
|
+
}
|
|
1864
|
+
async function signerOf(secret) {
|
|
1865
|
+
const seed = await seedOf(secret);
|
|
1866
|
+
const der = new Uint8Array(PKCS8_ED25519.length + 32);
|
|
1867
|
+
der.set(PKCS8_ED25519);
|
|
1868
|
+
der.set(seed, PKCS8_ED25519.length);
|
|
1869
|
+
const readable = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, true, ["sign"]);
|
|
1870
|
+
const { x } = await crypto.subtle.exportKey("jwk", readable);
|
|
1871
|
+
const signing = await crypto.subtle.importKey("pkcs8", der, { name: "Ed25519" }, false, ["sign"]);
|
|
1872
|
+
der.fill(0);
|
|
1873
|
+
seed.fill(0);
|
|
1874
|
+
const publicJwk = { kty: "OKP", crv: "Ed25519", x };
|
|
1875
|
+
const kid = await thumbprint(publicJwk.x);
|
|
1876
|
+
const header = b64uEncode(enc2.encode(JSON.stringify({ alg: "EdDSA", kid })));
|
|
1877
|
+
return {
|
|
1878
|
+
kid,
|
|
1879
|
+
publicJwk,
|
|
1880
|
+
async assertion(clientId, tokenEndpoint, jti = crypto.randomUUID(), iat = Math.floor(Date.now() / 1e3)) {
|
|
1881
|
+
const claims = { iss: clientId, sub: clientId, aud: tokenEndpoint, jti, iat, exp: iat + 120 };
|
|
1882
|
+
const input = `${header}.${b64uEncode(enc2.encode(JSON.stringify(claims)))}`;
|
|
1883
|
+
const sig = new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, signing, enc2.encode(input)));
|
|
1884
|
+
return `${input}.${b64uEncode(sig)}`;
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
function deriveFromKey(key, previous) {
|
|
1889
|
+
const other = previous && previous.secret !== key.secret ? previous : void 0;
|
|
1890
|
+
const rings = deriveKeys(key.secret, other?.secret);
|
|
1891
|
+
return {
|
|
1892
|
+
cookie: rings.cookie,
|
|
1893
|
+
flow: rings.flow,
|
|
1894
|
+
signer: signerOf(key.secret),
|
|
1895
|
+
...other ? { previousJwk: signerOf(other.secret).then((s) => ({ ...s.publicJwk, kid: s.kid })) } : {}
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// src/identity.ts
|
|
1900
|
+
var REVALIDATE_MS = 3e5;
|
|
1901
|
+
var sleep = (ms) => new Promise((resolve2) => {
|
|
1902
|
+
const t = setTimeout(resolve2, ms);
|
|
1903
|
+
t.unref?.();
|
|
1904
|
+
});
|
|
1905
|
+
function createIdentity(o) {
|
|
1906
|
+
const doFetch = o.fetch ?? globalThis.fetch;
|
|
1907
|
+
const now = o.now ?? Date.now;
|
|
1908
|
+
const retry = o.retryMs ?? [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
1909
|
+
let held;
|
|
1910
|
+
let etag;
|
|
1911
|
+
let checkedAt = 0;
|
|
1912
|
+
let first;
|
|
1913
|
+
let refused;
|
|
1914
|
+
let refreshing;
|
|
1915
|
+
let failed = false;
|
|
1916
|
+
let attemptEnded;
|
|
1917
|
+
let attemptOut;
|
|
1918
|
+
const kid = () => (typeof o.kid === "function" ? o.kid() : o.kid) ?? "?";
|
|
1919
|
+
async function ask() {
|
|
1920
|
+
const headers = { authorization: `Bearer ${await o.token()}`, accept: "application/json" };
|
|
1921
|
+
if (etag && held) headers["if-none-match"] = etag;
|
|
1922
|
+
const res = await doFetch(`${o.issuer}/v1/connect/self`, { headers });
|
|
1923
|
+
if (res.status === 304 && held) return "same";
|
|
1924
|
+
if (res.status === 410) throw Object.assign(new Error("client_inactive"), { code: "client_inactive" });
|
|
1925
|
+
if (!res.ok) throw new CauthyApiError(503, "unavailable", `Cauthy answered ${res.status} to /v1/connect/self`);
|
|
1926
|
+
const body = await res.json();
|
|
1927
|
+
if (!body?.data || typeof body.data.client_id !== "string") throw new CauthyApiError(503, "unavailable", "Cauthy's answer to /v1/connect/self has no identity");
|
|
1928
|
+
etag = res.headers.get("etag") ?? void 0;
|
|
1929
|
+
return body.data;
|
|
1930
|
+
}
|
|
1931
|
+
function refusalOf(e) {
|
|
1932
|
+
const code = e?.code;
|
|
1933
|
+
if (code !== "invalid_client" && code !== "client_inactive") return void 0;
|
|
1934
|
+
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.`;
|
|
1935
|
+
return new CauthyApiError(503, "misconfigured", message);
|
|
1936
|
+
}
|
|
1937
|
+
function refuse(err) {
|
|
1938
|
+
if (refused === void 0) o.log?.(err.message.replace(/^@cauthyhq\/node: /, ""));
|
|
1939
|
+
refused = err;
|
|
1940
|
+
checkedAt = now();
|
|
1941
|
+
return err;
|
|
1942
|
+
}
|
|
1943
|
+
async function firstAnswer() {
|
|
1944
|
+
for (let attempt = 0; ; attempt++) {
|
|
1945
|
+
attemptOut = new Promise((resolve2) => {
|
|
1946
|
+
attemptEnded = resolve2;
|
|
1947
|
+
});
|
|
1948
|
+
try {
|
|
1949
|
+
const got = await ask();
|
|
1950
|
+
if (got !== "same") held = got;
|
|
1951
|
+
checkedAt = now();
|
|
1952
|
+
failed = false;
|
|
1953
|
+
attemptEnded?.(true);
|
|
1954
|
+
return held;
|
|
1955
|
+
} catch (e) {
|
|
1956
|
+
const r = refusalOf(e);
|
|
1957
|
+
failed = true;
|
|
1958
|
+
attemptEnded?.(false);
|
|
1959
|
+
if (r) throw refuse(r);
|
|
1960
|
+
await sleep(retry[Math.min(attempt, retry.length - 1)]);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
async function refresh() {
|
|
1965
|
+
checkedAt = now();
|
|
1966
|
+
try {
|
|
1967
|
+
const got = await ask();
|
|
1968
|
+
if (got !== "same") held = got;
|
|
1969
|
+
refused = void 0;
|
|
1970
|
+
} catch (e) {
|
|
1971
|
+
const r = refusalOf(e);
|
|
1972
|
+
if (r) refuse(r);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
return {
|
|
1976
|
+
ready() {
|
|
1977
|
+
if ((held || refused) && now() - checkedAt >= REVALIDATE_MS) {
|
|
1978
|
+
refreshing ??= refresh().finally(() => {
|
|
1979
|
+
refreshing = void 0;
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
if (refused) return Promise.reject(refused);
|
|
1983
|
+
if (held) return Promise.resolve(held);
|
|
1984
|
+
first ??= firstAnswer();
|
|
1985
|
+
return first;
|
|
1986
|
+
},
|
|
1987
|
+
current: () => held,
|
|
1988
|
+
refresh: () => refreshing ??= refresh().finally(() => {
|
|
1989
|
+
refreshing = void 0;
|
|
1990
|
+
}),
|
|
1991
|
+
failing: () => held === void 0 && failed && refused === void 0,
|
|
1992
|
+
attempt: () => held !== void 0 ? Promise.resolve(true) : attemptOut ?? Promise.resolve(false)
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1604
1996
|
// src/verify.ts
|
|
1605
1997
|
var CauthyUnavailableError = class extends Error {
|
|
1606
1998
|
constructor(detail) {
|
|
@@ -1743,6 +2135,10 @@ function decodeJson(segment) {
|
|
|
1743
2135
|
return parseStrict(Buffer.from(padded, "base64").toString("utf8"));
|
|
1744
2136
|
}
|
|
1745
2137
|
function createTokenVerifier(options = {}) {
|
|
2138
|
+
const fromEnv = ["CAUTHY_ISSUER", "CAUTHY_AUDIENCE", "CAUTHY_TENANT_ID"].filter((name, i) => [options.issuer, options.audience, options.tenantId][i] === void 0 && (env2(name) ?? "") !== "");
|
|
2139
|
+
if (fromEnv.length > 0) {
|
|
2140
|
+
deprecated("Reading CAUTHY_ISSUER, CAUTHY_AUDIENCE and CAUTHY_TENANT_ID to verify tokens", "Set CAUTHY_KEY and use createVerifierFromEnv().");
|
|
2141
|
+
}
|
|
1746
2142
|
const issuerGiven = options.issuer ?? env2("CAUTHY_ISSUER") ?? "";
|
|
1747
2143
|
if (issuerGiven === "") throw new CauthyConfigError("issuer (CAUTHY_ISSUER) is not set");
|
|
1748
2144
|
const issuer = issuerAddress(issuerGiven, "issuer");
|
|
@@ -1808,6 +2204,7 @@ function createTokenVerifier(options = {}) {
|
|
|
1808
2204
|
if (claims.roles !== void 0 && claims.roles !== null && !(Array.isArray(claims.roles) && claims.roles.every((r) => typeof r === "string"))) bad("roles");
|
|
1809
2205
|
if (claims.email !== void 0 && typeof claims.email !== "string") bad("email");
|
|
1810
2206
|
for (const name of ["email_verified", "anon"]) if (claims[name] !== void 0 && typeof claims[name] !== "boolean") bad(name);
|
|
2207
|
+
for (const name of ["token_use", "azp"]) if (claims[name] !== void 0 && typeof claims[name] !== "string") bad(name);
|
|
1811
2208
|
if (!issuers.includes(claims.iss)) throw new CauthyTokenError("wrong_issuer");
|
|
1812
2209
|
if (!(Array.isArray(aud) ? aud.includes(audience) : aud === audience)) throw new CauthyTokenError("wrong_audience");
|
|
1813
2210
|
const now = (options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
@@ -1816,7 +2213,9 @@ function createTokenVerifier(options = {}) {
|
|
|
1816
2213
|
if (now < claims.iat - leeway) throw new CauthyTokenError("issued_in_future");
|
|
1817
2214
|
if (claims.tid !== tenant) throw new CauthyTokenError("wrong_tenant");
|
|
1818
2215
|
if ("sys" in claims && !options.allowSystem) throw new CauthyTokenError("system_token");
|
|
2216
|
+
if ("token_use" in claims && !options.allowClientTokens) throw new CauthyTokenError("client_token");
|
|
1819
2217
|
if (claims.anon === true && !options.allowAnonymous) throw new CauthyTokenError("anonymous_token");
|
|
2218
|
+
if (options.clientId && typeof claims.azp === "string" && claims.azp !== "" && claims.azp !== options.clientId) throw new CauthyTokenError("wrong_client");
|
|
1820
2219
|
return {
|
|
1821
2220
|
userId: claims.sub,
|
|
1822
2221
|
tenantId: claims.tid,
|
|
@@ -1825,6 +2224,8 @@ function createTokenVerifier(options = {}) {
|
|
|
1825
2224
|
roles: Array.isArray(claims.roles) ? claims.roles : [],
|
|
1826
2225
|
...typeof claims.sys === "string" && claims.sys !== "" ? { system: claims.sys } : {},
|
|
1827
2226
|
...claims.anon === true ? { anonymous: true } : {},
|
|
2227
|
+
...typeof claims.azp === "string" && claims.azp !== "" ? { authorizedParty: claims.azp } : {},
|
|
2228
|
+
...typeof claims.client_id === "string" && claims.client_id !== "" ? { clientId: claims.client_id } : {},
|
|
1828
2229
|
expiresAt: new Date(claims.exp * 1e3).toISOString(),
|
|
1829
2230
|
token
|
|
1830
2231
|
};
|
|
@@ -1833,18 +2234,18 @@ function createTokenVerifier(options = {}) {
|
|
|
1833
2234
|
async function verifyToken(from, options = {}) {
|
|
1834
2235
|
return createTokenVerifier(options)(from);
|
|
1835
2236
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
const message = `@cauthyhq/node: ${what} is deprecated and leaves in 2.0. ${instead}`;
|
|
1843
|
-
if (typeof process !== "undefined" && typeof process.emitWarning === "function") {
|
|
1844
|
-
process.emitWarning(message, { type: "DeprecationWarning", code: "CAUTHY_DEPRECATED" });
|
|
1845
|
-
} else {
|
|
1846
|
-
console.warn(message);
|
|
2237
|
+
async function createVerifierFromEnv(options = {}) {
|
|
2238
|
+
const text2 = options.key ?? env2("CAUTHY_KEY");
|
|
2239
|
+
if (text2 === void 0 || text2 === "") return createTokenVerifier(options);
|
|
2240
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2241
|
+
for (const name of ["issuer", "audience", "tenantId"]) {
|
|
2242
|
+
if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
1847
2243
|
}
|
|
2244
|
+
const key = parseKey(text2);
|
|
2245
|
+
const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
|
|
2246
|
+
const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
|
|
2247
|
+
const { key: _, ...rest } = options;
|
|
2248
|
+
return createTokenVerifier({ ...rest, issuer: id.issuer, audience: id.audience, tenantId: id.tenant.id });
|
|
1848
2249
|
}
|
|
1849
2250
|
|
|
1850
2251
|
// src/mount.ts
|
|
@@ -1937,6 +2338,63 @@ function redirect(location, cookies = []) {
|
|
|
1937
2338
|
function conditionalOf(c) {
|
|
1938
2339
|
return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
|
|
1939
2340
|
}
|
|
2341
|
+
function byKey(given) {
|
|
2342
|
+
const text2 = given.key ?? env3("CAUTHY_KEY");
|
|
2343
|
+
if (text2 === void 0 || text2 === "") return void 0;
|
|
2344
|
+
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2345
|
+
for (const [name, value] of [
|
|
2346
|
+
["issuer", given.issuer],
|
|
2347
|
+
["clientId", given.clientId],
|
|
2348
|
+
["clientSecret", given.clientSecret],
|
|
2349
|
+
["secret", given.secret],
|
|
2350
|
+
["system", given.system],
|
|
2351
|
+
["tenant", given.tenant],
|
|
2352
|
+
["audience", given.audience],
|
|
2353
|
+
["tenantId", given.tenantId],
|
|
2354
|
+
["systemKey", given.systemKey],
|
|
2355
|
+
["rootKey", given.rootKey]
|
|
2356
|
+
]) {
|
|
2357
|
+
if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
2358
|
+
}
|
|
2359
|
+
const key = parseKey(text2);
|
|
2360
|
+
const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
|
|
2361
|
+
let previous;
|
|
2362
|
+
if (prevText) {
|
|
2363
|
+
try {
|
|
2364
|
+
previous = parseKey(prevText);
|
|
2365
|
+
} catch (e) {
|
|
2366
|
+
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_]+\)$/, "")}`);
|
|
2367
|
+
throw e;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
if (previous && (previous.issuer !== key.issuer || previous.clientId !== key.clientId)) {
|
|
2371
|
+
throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
|
|
2372
|
+
}
|
|
2373
|
+
const legacySecret = env3("CAUTHY_SECRET") || void 0;
|
|
2374
|
+
const legacyRoot = env3("PACT_ROOT_KEY") || void 0;
|
|
2375
|
+
if (legacySecret) deprecated("CAUTHY_SECRET beside CAUTHY_KEY", "It is read only for sessions made before the move: remove it after the rotation window.");
|
|
2376
|
+
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.");
|
|
2377
|
+
if (given.keys && !(legacyRoot && given.contract)) {
|
|
2378
|
+
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)");
|
|
2379
|
+
}
|
|
2380
|
+
return { key, previous, legacySecret, legacyRoot };
|
|
2381
|
+
}
|
|
2382
|
+
var UnknownOriginError = class extends Error {
|
|
2383
|
+
constructor(origin, registered) {
|
|
2384
|
+
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`);
|
|
2385
|
+
this.origin = origin;
|
|
2386
|
+
this.registered = registered;
|
|
2387
|
+
}
|
|
2388
|
+
origin;
|
|
2389
|
+
registered;
|
|
2390
|
+
};
|
|
2391
|
+
function contractOnKey(given, keyed) {
|
|
2392
|
+
const contract = readContract(given.contract);
|
|
2393
|
+
if (contract.issuer !== keyed.key.issuer) {
|
|
2394
|
+
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`);
|
|
2395
|
+
}
|
|
2396
|
+
return contract;
|
|
2397
|
+
}
|
|
1940
2398
|
function byContract(given) {
|
|
1941
2399
|
failClosed(typeof process !== "undefined" ? process.env : void 0);
|
|
1942
2400
|
for (const [name, value] of [
|
|
@@ -1955,27 +2413,48 @@ function byContract(given) {
|
|
|
1955
2413
|
if (contract.serviceClientId && !given.keys) {
|
|
1956
2414
|
throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
|
|
1957
2415
|
}
|
|
2416
|
+
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.");
|
|
1958
2417
|
return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
|
|
1959
2418
|
}
|
|
1960
2419
|
var toldOfHeader = false;
|
|
1961
2420
|
function createCauthy(given = {}) {
|
|
1962
|
-
const
|
|
1963
|
-
const
|
|
1964
|
-
const
|
|
1965
|
-
const
|
|
1966
|
-
const
|
|
1967
|
-
const
|
|
1968
|
-
const
|
|
1969
|
-
const
|
|
2421
|
+
const keyed = byKey(given);
|
|
2422
|
+
const pacted = given.contract && !keyed ? byContract(given) : void 0;
|
|
2423
|
+
const contract = keyed && given.contract ? contractOnKey(given, keyed) : pacted?.contract;
|
|
2424
|
+
const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? contract?.issuer ?? env3("CAUTHY_ISSUER") ?? "");
|
|
2425
|
+
const clientId = contract?.clientId ?? keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
|
|
2426
|
+
const clientSecret = contract || keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
|
|
2427
|
+
const secret = contract || keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
|
|
2428
|
+
const askSelf = keyed !== void 0 && contract === void 0;
|
|
2429
|
+
const system = askSelf ? "" : contract?.system ?? given.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
2430
|
+
const tenant = askSelf ? "" : contract?.tenant ?? given.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
1970
2431
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
1971
2432
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
1972
|
-
if (!contract && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2433
|
+
if (!contract && !keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2434
|
+
const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
|
|
2435
|
+
let signingKid;
|
|
2436
|
+
fromKey?.signer.then((s) => {
|
|
2437
|
+
signingKid = s.kid;
|
|
2438
|
+
}, () => void 0);
|
|
1973
2439
|
const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
|
|
1974
|
-
const single = derived ? void 0 : keyOf(secret).then((k) => [k]);
|
|
1975
|
-
const
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
|
|
2440
|
+
const single = derived || fromKey ? void 0 : keyOf(secret).then((k) => [k]);
|
|
2441
|
+
const legacyRings = keyed?.legacyRoot ? deriveKeys(keyed.legacyRoot) : void 0;
|
|
2442
|
+
const legacySingle = keyed?.legacySecret ? keyOf(keyed.legacySecret) : void 0;
|
|
2443
|
+
const withLegacy = async (own, purpose) => [
|
|
2444
|
+
...await own,
|
|
2445
|
+
...legacyRings ? [(await legacyRings[purpose])[0]] : [],
|
|
2446
|
+
...legacySingle ? [await legacySingle] : []
|
|
2447
|
+
];
|
|
2448
|
+
const cookieKeys = fromKey ? withLegacy(fromKey.cookie, "cookie") : derived?.cookie ?? single;
|
|
2449
|
+
const flowKeys = fromKey ? withLegacy(fromKey.flow, "flow") : derived?.flow ?? single;
|
|
2450
|
+
if (!keyed && !pacted) {
|
|
2451
|
+
deprecated(
|
|
2452
|
+
"Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
|
|
2453
|
+
"Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
|
|
2457
|
+
const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
|
|
1979
2458
|
if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
|
|
1980
2459
|
throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
|
|
1981
2460
|
}
|
|
@@ -2011,18 +2490,65 @@ function createCauthy(given = {}) {
|
|
|
2011
2490
|
acceptInviteUrl,
|
|
2012
2491
|
fetch: given.fetch,
|
|
2013
2492
|
timeoutMs: systemTimeout,
|
|
2014
|
-
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {}
|
|
2493
|
+
...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {},
|
|
2494
|
+
// With a contract the key proves the link's service client; without, the key's own client.
|
|
2495
|
+
...fromKey ? {
|
|
2496
|
+
signer: { clientId: contract ? contract.serviceClientId ?? keyed.key.clientId : clientId, signer: fromKey.signer },
|
|
2497
|
+
currentSystem: () => identity?.current()?.system ?? ""
|
|
2498
|
+
} : {}
|
|
2015
2499
|
});
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2500
|
+
const selfOptions = () => ({
|
|
2501
|
+
issuer,
|
|
2502
|
+
token: () => ownSystem.token(),
|
|
2503
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2504
|
+
kid: () => signingKid,
|
|
2505
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2506
|
+
});
|
|
2507
|
+
const identity = askSelf ? createIdentity({
|
|
2508
|
+
issuer,
|
|
2509
|
+
token: () => ownSystem.token(),
|
|
2510
|
+
fetch: deadline(given.fetch, requestTimeout),
|
|
2511
|
+
kid: () => signingKid,
|
|
2512
|
+
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2513
|
+
}) : void 0;
|
|
2514
|
+
identity?.ready().catch(() => {
|
|
2515
|
+
});
|
|
2516
|
+
const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
|
|
2517
|
+
const systemNow = () => identity?.current()?.system ?? system;
|
|
2518
|
+
const requestOrigin = (request) => {
|
|
2018
2519
|
const url = new URL(request.url);
|
|
2019
2520
|
const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
|
|
2020
2521
|
return proto === "https" || proto === "http" ? `${proto}://${url.host}` : url.origin;
|
|
2021
2522
|
};
|
|
2022
|
-
const
|
|
2523
|
+
const originOf = (request) => {
|
|
2524
|
+
if (baseURL) return withoutTrailingSlashes(baseURL);
|
|
2525
|
+
const asked = requestOrigin(request);
|
|
2526
|
+
if (!identity) return asked;
|
|
2527
|
+
const urls2 = identity.current()?.app_urls ?? [];
|
|
2528
|
+
if (urls2.includes(asked)) return asked;
|
|
2529
|
+
if (urls2.length === 1) return urls2[0];
|
|
2530
|
+
throw new UnknownOriginError(asked, urls2);
|
|
2531
|
+
};
|
|
2532
|
+
const safeOrigin = (request) => {
|
|
2533
|
+
try {
|
|
2534
|
+
return originOf(request);
|
|
2535
|
+
} catch {
|
|
2536
|
+
return requestOrigin(request);
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
|
|
2540
|
+
async function clientAuth(form) {
|
|
2541
|
+
if (fromKey && !contract) {
|
|
2542
|
+
const s = await fromKey.signer;
|
|
2543
|
+
form.set("client_assertion_type", ASSERTION_TYPE2);
|
|
2544
|
+
form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
|
|
2545
|
+
} else if (clientSecret) {
|
|
2546
|
+
form.set("client_secret", clientSecret);
|
|
2547
|
+
}
|
|
2548
|
+
return form;
|
|
2549
|
+
}
|
|
2023
2550
|
async function token(form, proof = {}) {
|
|
2024
|
-
const body2 = new URLSearchParams({ client_id: clientId, ...form });
|
|
2025
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2551
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
|
|
2026
2552
|
try {
|
|
2027
2553
|
const res = await doFetch(`${issuer}/v1/oauth/token`, {
|
|
2028
2554
|
method: "POST",
|
|
@@ -2061,8 +2587,7 @@ function createCauthy(given = {}) {
|
|
|
2061
2587
|
}
|
|
2062
2588
|
const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
|
|
2063
2589
|
async function revoke(refreshToken) {
|
|
2064
|
-
const form = new URLSearchParams({ client_id: clientId, token: refreshToken });
|
|
2065
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
2590
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: refreshToken }));
|
|
2066
2591
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2067
2592
|
}
|
|
2068
2593
|
const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
|
|
@@ -2154,7 +2679,8 @@ function createCauthy(given = {}) {
|
|
|
2154
2679
|
async function organizations(session) {
|
|
2155
2680
|
const hit = orgCache.get(session.accessToken);
|
|
2156
2681
|
if (hit && Date.now() - hit.at < ORGANIZATIONS_TTL_MS) return hit.value;
|
|
2157
|
-
const
|
|
2682
|
+
const sys = systemNow();
|
|
2683
|
+
const url = `${issuer}/v1/oauth/organizations${sys ? `?system=${encodeURIComponent(sys)}` : ""}`;
|
|
2158
2684
|
const res = await doFetch(url, { headers: { authorization: `Bearer ${session.accessToken}` } });
|
|
2159
2685
|
if (!res.ok) throw new Error(`@cauthyhq/node: reading organizations: ${res.status}`);
|
|
2160
2686
|
const body2 = await res.json();
|
|
@@ -2308,8 +2834,7 @@ function createCauthy(given = {}) {
|
|
|
2308
2834
|
async function logout(request) {
|
|
2309
2835
|
const { had, value } = await read(request);
|
|
2310
2836
|
if (value !== null) {
|
|
2311
|
-
const body2 = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2312
|
-
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
2837
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2313
2838
|
await doFetch(`${issuer}/v1/oauth/revoke`, {
|
|
2314
2839
|
method: "POST",
|
|
2315
2840
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
@@ -2433,7 +2958,7 @@ function createCauthy(given = {}) {
|
|
|
2433
2958
|
const res = await doFetch(`${issuer}${path}`, {
|
|
2434
2959
|
method,
|
|
2435
2960
|
headers: {
|
|
2436
|
-
"x-tenant-key":
|
|
2961
|
+
"x-tenant-key": tenantNow(),
|
|
2437
2962
|
accept: "application/json",
|
|
2438
2963
|
// A link Cauthy mails opens the application's page for it when it
|
|
2439
2964
|
// has one; otherwise Cauthy's own. Either way in the reader's language.
|
|
@@ -2579,7 +3104,7 @@ function createCauthy(given = {}) {
|
|
|
2579
3104
|
}
|
|
2580
3105
|
if (route === "/sign-in/anonymous" || route === "/me/upgrade") deprecated(`POST ${route}`, "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2581
3106
|
if (route.startsWith("/me/upgrade/social/")) deprecated("GET /me/upgrade/social/{provider}", "Guest sessions leave the auth-bff protocol in version 2.");
|
|
2582
|
-
if (
|
|
3107
|
+
if (tenantNow() === "") {
|
|
2583
3108
|
return problem(500, "not_configured", "@cauthyhq/node: tenant (CAUTHY_TENANT) is required for the application's own sign-in screens");
|
|
2584
3109
|
}
|
|
2585
3110
|
if (route === "/look" && !post) {
|
|
@@ -2814,7 +3339,7 @@ function createCauthy(given = {}) {
|
|
|
2814
3339
|
let upstream;
|
|
2815
3340
|
try {
|
|
2816
3341
|
upstream = await doFetch(`${issuer}/v1/auth/mfa/push/events`, {
|
|
2817
|
-
headers: { "x-tenant-key":
|
|
3342
|
+
headers: { "x-tenant-key": tenantNow(), "x-mfa-token": step.token, accept: "text/event-stream" },
|
|
2818
3343
|
signal: request.signal
|
|
2819
3344
|
});
|
|
2820
3345
|
} catch {
|
|
@@ -2834,8 +3359,7 @@ function createCauthy(given = {}) {
|
|
|
2834
3359
|
if (route === "/sign-out") {
|
|
2835
3360
|
const { had, value } = await read(request);
|
|
2836
3361
|
if (value !== null) {
|
|
2837
|
-
const form = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
2838
|
-
if (clientSecret) form.set("client_secret", clientSecret);
|
|
3362
|
+
const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
|
|
2839
3363
|
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
2840
3364
|
orgCache.delete(value.at);
|
|
2841
3365
|
}
|
|
@@ -3234,14 +3758,38 @@ function createCauthy(given = {}) {
|
|
|
3234
3758
|
const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
|
|
3235
3759
|
const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
|
|
3236
3760
|
const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
|
|
3761
|
+
if (identity && route !== null) {
|
|
3762
|
+
const unready = await identityGate();
|
|
3763
|
+
if (unready) return finish(unready, route);
|
|
3764
|
+
}
|
|
3237
3765
|
try {
|
|
3238
3766
|
return finish(await serve(request), route, published3);
|
|
3239
3767
|
} catch (err) {
|
|
3768
|
+
if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
|
|
3240
3769
|
if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
|
|
3241
3770
|
const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
|
|
3242
3771
|
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);
|
|
3243
3772
|
}
|
|
3244
3773
|
}
|
|
3774
|
+
async function identityGate() {
|
|
3775
|
+
if (!identity) return null;
|
|
3776
|
+
const unavailable = () => problem(503, "unavailable", "Cauthy cannot be reached yet. Try again in a moment.");
|
|
3777
|
+
if (identity.failing()) return unavailable();
|
|
3778
|
+
let timer;
|
|
3779
|
+
const waited = new Promise((resolve3) => {
|
|
3780
|
+
timer = setTimeout(() => resolve3("timeout"), requestTimeout);
|
|
3781
|
+
});
|
|
3782
|
+
try {
|
|
3783
|
+
const ready = identity.ready();
|
|
3784
|
+
const got = await Promise.race([ready.then(() => "ok"), identity.attempt().then((ok) => ok ? "ok" : "failed"), waited]);
|
|
3785
|
+
if (got === "failed") await Promise.race([ready, Promise.resolve()]);
|
|
3786
|
+
return got === "ok" ? null : unavailable();
|
|
3787
|
+
} catch (e) {
|
|
3788
|
+
return e.code === "misconfigured" ? problem(503, "misconfigured", "Cauthy refuses this application's key: make a new one in Cauthy's dashboard.") : unavailable();
|
|
3789
|
+
} finally {
|
|
3790
|
+
clearTimeout(timer);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3245
3793
|
async function serve(request) {
|
|
3246
3794
|
const url = new URL(request.url);
|
|
3247
3795
|
if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
|
|
@@ -3324,15 +3872,65 @@ function createCauthy(given = {}) {
|
|
|
3324
3872
|
return { response: redirect(to) };
|
|
3325
3873
|
}
|
|
3326
3874
|
const replay = given.webhookReplay ?? memoryReplayStore();
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3875
|
+
let lateSelf;
|
|
3876
|
+
const tokenVerifier = (o = {}) => {
|
|
3877
|
+
if (keyed) {
|
|
3878
|
+
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3879
|
+
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3880
|
+
}
|
|
3881
|
+
let built;
|
|
3882
|
+
return async (from) => {
|
|
3883
|
+
const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
|
|
3884
|
+
if (built?.from !== id) {
|
|
3885
|
+
built = { from: id, verify: createTokenVerifier({
|
|
3886
|
+
issuer,
|
|
3887
|
+
tenantId: contract?.tenantId ?? id.tenant.id,
|
|
3888
|
+
audience: id.audience,
|
|
3889
|
+
fetch: given.fetch,
|
|
3890
|
+
...contract ? { clientId: contract.clientId } : {},
|
|
3891
|
+
...o
|
|
3892
|
+
}) };
|
|
3893
|
+
}
|
|
3894
|
+
return built.verify(from);
|
|
3895
|
+
};
|
|
3896
|
+
}
|
|
3897
|
+
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3898
|
+
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3899
|
+
}
|
|
3900
|
+
return createTokenVerifier({
|
|
3901
|
+
issuer,
|
|
3902
|
+
tenantId: contract?.tenantId ?? given.tenantId,
|
|
3903
|
+
audience: given.audience,
|
|
3904
|
+
fetch: given.fetch,
|
|
3905
|
+
...contract ? { clientId: contract.clientId } : {},
|
|
3906
|
+
...o
|
|
3907
|
+
});
|
|
3908
|
+
};
|
|
3334
3909
|
const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now, replay });
|
|
3335
|
-
|
|
3910
|
+
async function keyJwks() {
|
|
3911
|
+
const s = await fromKey.signer;
|
|
3912
|
+
const keys2 = [{ ...s.publicJwk, kid: s.kid, alg: "EdDSA", use: "sig" }];
|
|
3913
|
+
if (fromKey.previousJwk) keys2.push({ ...await fromKey.previousJwk, alg: "EdDSA", use: "sig" });
|
|
3914
|
+
if (ownKeys) keys2.push(...(await ownKeys.published()).keys);
|
|
3915
|
+
return new Response(JSON.stringify({ keys: keys2 }), {
|
|
3916
|
+
headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
const noStore = () => {
|
|
3920
|
+
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");
|
|
3921
|
+
};
|
|
3922
|
+
const keyOnly = {
|
|
3923
|
+
current: async () => noStore(),
|
|
3924
|
+
published: async () => ({ keys: [] }),
|
|
3925
|
+
rotate: async () => noStore(),
|
|
3926
|
+
reseal: async () => 0
|
|
3927
|
+
};
|
|
3928
|
+
const pact = contract && fromKey ? {
|
|
3929
|
+
contract,
|
|
3930
|
+
keys: ownKeys ?? keyOnly,
|
|
3931
|
+
jwks: keyJwks,
|
|
3932
|
+
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3933
|
+
} : contract && ownKeys ? {
|
|
3336
3934
|
contract,
|
|
3337
3935
|
keys: ownKeys,
|
|
3338
3936
|
async jwks() {
|
|
@@ -3342,8 +3940,25 @@ function createCauthy(given = {}) {
|
|
|
3342
3940
|
},
|
|
3343
3941
|
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3344
3942
|
} : void 0;
|
|
3943
|
+
const configured = () => ({
|
|
3944
|
+
issuer,
|
|
3945
|
+
audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
|
|
3946
|
+
tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
|
|
3947
|
+
system,
|
|
3948
|
+
client_id: clientId,
|
|
3949
|
+
sign_in_client_id: clientId,
|
|
3950
|
+
app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
|
|
3951
|
+
callback_path: `${basePath}/callback`,
|
|
3952
|
+
key: null,
|
|
3953
|
+
contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
|
|
3954
|
+
});
|
|
3345
3955
|
return {
|
|
3346
3956
|
handler,
|
|
3957
|
+
ready: async () => {
|
|
3958
|
+
if (identity) return identity.ready();
|
|
3959
|
+
const id = configured();
|
|
3960
|
+
return fromKey ? { ...id, key: { kid: (await fromKey.signer).kid, retires_at: null } } : id;
|
|
3961
|
+
},
|
|
3347
3962
|
checkMount: (target) => checkMount(basePath, target),
|
|
3348
3963
|
webhook,
|
|
3349
3964
|
tokenVerifier,
|
|
@@ -3434,10 +4049,12 @@ async function connectSystem(o) {
|
|
|
3434
4049
|
0 && (module.exports = {
|
|
3435
4050
|
CauthyApiError,
|
|
3436
4051
|
CauthyConfigError,
|
|
4052
|
+
CauthyKeyError,
|
|
3437
4053
|
CauthyTokenError,
|
|
3438
4054
|
CauthyUnavailableError,
|
|
3439
4055
|
CauthyWebhookError,
|
|
3440
4056
|
DEFAULT_PASSWORD_POLICY,
|
|
4057
|
+
KEY_REPLACES,
|
|
3441
4058
|
MIN_PASSWORD,
|
|
3442
4059
|
PROTOCOL_VERSION,
|
|
3443
4060
|
STANDARD_VARIABLES,
|
|
@@ -3447,15 +4064,18 @@ async function connectSystem(o) {
|
|
|
3447
4064
|
checkName,
|
|
3448
4065
|
checkNewPassword,
|
|
3449
4066
|
checkPassword,
|
|
4067
|
+
composeKey,
|
|
3450
4068
|
connectSystem,
|
|
3451
4069
|
createCauthy,
|
|
3452
4070
|
createSystem,
|
|
3453
4071
|
createTokenVerifier,
|
|
4072
|
+
createVerifierFromEnv,
|
|
3454
4073
|
definePolicy,
|
|
3455
4074
|
describePasswordPolicy,
|
|
3456
4075
|
explainPasswordProblems,
|
|
3457
4076
|
failClosed,
|
|
3458
4077
|
memoryReplayStore,
|
|
4078
|
+
parseKey,
|
|
3459
4079
|
passwordPolicyOf,
|
|
3460
4080
|
passwordProblems,
|
|
3461
4081
|
policySource,
|