@cauthyhq/node 1.2.1 → 1.3.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +31 -7
  2. package/README.md +10 -29
  3. package/dist/{cauthy-CCSSj3Lg.d.mts → cauthy-DHRZLndK.d.mts} +31 -147
  4. package/dist/{cauthy-C7gZ-pbv.d.ts → cauthy-JSYh7J2o.d.ts} +31 -147
  5. package/dist/express.d.mts +2 -5
  6. package/dist/express.d.ts +2 -5
  7. package/dist/express.js +1 -2
  8. package/dist/express.js.map +1 -1
  9. package/dist/express.mjs +1 -2
  10. package/dist/express.mjs.map +1 -1
  11. package/dist/fastify.d.mts +1 -3
  12. package/dist/fastify.d.ts +1 -3
  13. package/dist/fastify.js +0 -2
  14. package/dist/fastify.js.map +1 -1
  15. package/dist/fastify.mjs +0 -2
  16. package/dist/fastify.mjs.map +1 -1
  17. package/dist/hono.d.mts +2 -5
  18. package/dist/hono.d.ts +2 -5
  19. package/dist/hono.js +1 -2
  20. package/dist/hono.js.map +1 -1
  21. package/dist/hono.mjs +1 -2
  22. package/dist/hono.mjs.map +1 -1
  23. package/dist/index.d.mts +1 -3
  24. package/dist/index.d.ts +1 -3
  25. package/dist/index.js +159 -524
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +152 -514
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/mcp.js.map +1 -1
  30. package/dist/mcp.mjs.map +1 -1
  31. package/dist/next.d.mts +1 -3
  32. package/dist/next.d.ts +1 -3
  33. package/dist/testing.d.mts +11 -5
  34. package/dist/testing.d.ts +11 -5
  35. package/dist/testing.js +51 -49
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.mjs +51 -49
  38. package/dist/testing.mjs.map +1 -1
  39. package/package.json +3 -15
  40. package/dist/identity-BTJ8NB_w.d.mts +0 -27
  41. package/dist/identity-BTJ8NB_w.d.ts +0 -27
  42. package/dist/keys-D5k0omTb.d.mts +0 -64
  43. package/dist/keys-D5k0omTb.d.ts +0 -64
  44. package/dist/keys.d.mts +0 -8
  45. package/dist/keys.d.ts +0 -8
  46. package/dist/keys.js +0 -157
  47. package/dist/keys.js.map +0 -1
  48. package/dist/keys.mjs +0 -129
  49. package/dist/keys.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -64,261 +64,6 @@ async function unsealIndexed(sealed, ring) {
64
64
  return null;
65
65
  }
66
66
 
67
- // src/types.ts
68
- var CauthyConfigError = class extends Error {
69
- constructor(message) {
70
- super(`@cauthyhq/node: ${message}`);
71
- this.name = "CauthyConfigError";
72
- }
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
- };
82
-
83
- // src/url.ts
84
- function withoutTrailingSlashes(s) {
85
- let end = s.length;
86
- while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
87
- return s.slice(0, end);
88
- }
89
-
90
- // src/contract.ts
91
- var STANDARD_VARIABLES = [
92
- "CAUTHY_ISSUER",
93
- "CAUTHY_CLIENT_ID",
94
- "CAUTHY_CLIENT_SECRET",
95
- "CAUTHY_SECRET",
96
- "CAUTHY_SYSTEM",
97
- "CAUTHY_TENANT",
98
- "CAUTHY_SYSTEM_KEY",
99
- "CAUTHY_SYSTEM_KEY_ID"
100
- ];
101
- function failClosed(environment) {
102
- const set = STANDARD_VARIABLES.filter((name) => (environment?.[name] ?? "") !== "");
103
- if (set.length > 0) {
104
- throw new CauthyConfigError(`the application runs by contract, and ${set.join(", ")} ${set.length === 1 ? "is" : "are"} also set: remove ` + (set.length === 1 ? "it" : "them") + " from the environment. Everything comes from the contract.");
105
- }
106
- }
107
- var SLUG = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/;
108
- var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
109
- var NIL_UUID = "00000000-0000-0000-0000-000000000000";
110
- var text = (v) => typeof v === "string" ? v : "";
111
- function httpsAddress(raw, what) {
112
- let u;
113
- try {
114
- u = new URL(raw);
115
- } catch {
116
- throw new CauthyConfigError(`the contract's ${what} is not an address: ${JSON.stringify(raw)}`);
117
- }
118
- const local = u.hostname === "localhost" || u.hostname === "127.0.0.1";
119
- if (u.protocol !== "https:" && !(u.protocol === "http:" && local)) {
120
- throw new CauthyConfigError(`the contract's ${what} must be https: ${raw}`);
121
- }
122
- return u;
123
- }
124
- function readContract(options) {
125
- const { system } = options;
126
- const provider = options.provider ?? "cauthy";
127
- if (!SLUG.test(system ?? "")) throw new CauthyConfigError(`contract.system must be the system's id in the graph, e.g. "acme-crm"`);
128
- const graph = options.graph && typeof options.graph === "object" && "default" in options.graph && !("edges" in options.graph) ? options.graph.default : options.graph;
129
- if (!graph || !Array.isArray(graph.edges) || !Array.isArray(graph.nodes)) {
130
- throw new CauthyConfigError("contract.graph is not a context graph: pass the graph.json that `cauthy contracts pull` writes");
131
- }
132
- const node = (id) => graph.nodes?.find((n) => n.type === "system" && n.id === id);
133
- const own = node(system);
134
- if (!own) throw new CauthyConfigError(`the graph has no system "${system}"`);
135
- const cauthy = node(provider);
136
- if (!cauthy) throw new CauthyConfigError(`the graph has no system "${provider}"`);
137
- const links = graph.edges.filter((e2) => e2.relation === "links" && e2.from === system && e2.to === provider && e2.sign_in).filter((e2) => options.link === void 0 || e2.link === options.link);
138
- if (links.length === 0) {
139
- throw new CauthyConfigError(options.link ? `the graph has no link "${options.link}" from ${system} to ${provider} with sign_in` : `no link of the graph signs ${system}'s people in with ${provider}: merge one in the contracts repository, then pull the graph again`);
140
- }
141
- if (links.length > 1) {
142
- throw new CauthyConfigError(`${system} has ${links.length} links to ${provider}: name the one to read (contract.link): ${links.map((l) => text(l.link)).join(", ")}`);
143
- }
144
- const e = links[0];
145
- const link = text(e.link);
146
- const named = text(e.identity?.tenant);
147
- const pinned = text(e.identity?.tenant_id);
148
- if (named === NIL_UUID || pinned === NIL_UUID) throw new CauthyConfigError(`the link ${link} names the nil UUID as its workspace: name the workspace by its slug`);
149
- let tenantId;
150
- let tenant;
151
- if (UUID.test(named)) {
152
- if (pinned !== "" && pinned.toLowerCase() !== named.toLowerCase()) {
153
- throw new CauthyConfigError(`the link ${link} names the workspace ${named} and its tenant_id ${pinned} does not match: pull the graph again`);
154
- }
155
- tenantId = named;
156
- tenant = text((cauthy.tenants ?? []).find((t) => t.id === named)?.slug);
157
- if (tenant === "") throw new CauthyConfigError(`the graph does not name the slug of the workspace ${named} that the link ${link} uses`);
158
- } else {
159
- if (!SLUG.test(named)) throw new CauthyConfigError(`the link ${link} names no workspace: identity.tenant is a UUID or a slug`);
160
- if (!UUID.test(pinned)) {
161
- throw new CauthyConfigError(e.identity?.create_tenant === true ? `the link ${link} asks Cauthy for a new workspace "${named}", and the graph has no tenant_id for it yet: once Cauthy has created it and core-contracts lists it, pull the graph again` : `the link ${link} names the workspace "${named}" by its slug, and the graph carries no tenant_id for it: pull the graph again`);
162
- }
163
- tenantId = pinned;
164
- tenant = named;
165
- }
166
- const issuer = withoutTrailingSlashes(httpsAddress(text(cauthy.url), `address of ${provider}`).href);
167
- const signIn = e.sign_in ?? {};
168
- const clientId = text(signIn.client_id) || `pact.${link}`;
169
- const redirectUris = Array.isArray(signIn.redirect_uris) ? signIn.redirect_uris.map(text).filter(Boolean) : [];
170
- if (redirectUris.length === 0) throw new CauthyConfigError(`the link ${link} names no redirect URI`);
171
- const proof = text(signIn.system_proof);
172
- if (signIn.system_proof !== void 0 && proof !== "off" && proof !== "auto" && proof !== "required") {
173
- throw new CauthyConfigError(`the link ${link} has system_proof ${JSON.stringify(signIn.system_proof)}: it is off, auto or required. Update @cauthyhq/node, or pull the graph again`);
174
- }
175
- const systemProof = proof === "off" || proof === "auto" || proof === "required" ? proof : signIn.require_system_proof === true ? "required" : "off";
176
- if (systemProof !== "off" && !e.service) {
177
- throw new CauthyConfigError(`the link ${link} asks for the system's proof (${systemProof}) and gives ${system} no identity to make it with: it needs a service block`);
178
- }
179
- const out2 = {
180
- system,
181
- link,
182
- issuer,
183
- tenantId,
184
- tenant,
185
- clientId,
186
- redirectUris,
187
- requireSystemProof: systemProof === "required",
188
- systemProof,
189
- requireVerifiedEmail: signIn.require_verified_email === true,
190
- persistentSessions: signIn.persistent_sessions === true
191
- };
192
- if (e.service) {
193
- out2.serviceClientId = text(e.service.client_id) || `pact.${link}.service`;
194
- const jwks = text(e.service.jwks_url) || text(own.jwks_url);
195
- if (jwks === "") throw new CauthyConfigError(`the link ${link} gives ${system} its own identity, and ${system} names no jwks_url`);
196
- out2.jwksUrl = httpsAddress(jwks, "jwks_url").href;
197
- out2.jwksPath = new URL(out2.jwksUrl).pathname;
198
- }
199
- if (text(own.events_url) !== "") out2.eventsUrl = httpsAddress(text(own.events_url), "events_url").href;
200
- return out2;
201
- }
202
-
203
- // src/keys.ts
204
- import { calculateJwkThumbprint, exportJWK, generateKeyPair, importJWK } from "jose";
205
- var RETIRE_AFTER_MS = 24 * 60 * 60 * 1e3;
206
- var FORGET_AFTER_MS = 7 * 24 * 60 * 60 * 1e3;
207
- var CURRENT_TTL_MS = 5 * 60 * 1e3;
208
- var JWKS_MAX_AGE_S = 300;
209
- var SIGN_AFTER_MS = (JWKS_MAX_AGE_S + 60) * 1e3;
210
- var SETTLING_TTL_MS = 5 * 1e3;
211
- var newestFirst = (keys2) => [...keys2].sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.kid.localeCompare(a.kid));
212
- function published(key) {
213
- const { kty, n, e } = key.publicJwk;
214
- return { kty, n, e, kid: key.kid, alg: "RS256", use: "sig" };
215
- }
216
- function pactKeys(store, sealKeys, now = Date.now) {
217
- let remembered;
218
- let making;
219
- async function make(after) {
220
- const { publicKey, privateKey } = await generateKeyPair("RS256", { modulusLength: 2048, extractable: true });
221
- const publicJwk = await exportJWK(publicKey);
222
- const kid = await calculateJwkThumbprint(publicJwk);
223
- const at = Math.max(now(), after ? Date.parse(after.createdAt) + 1 : 0);
224
- const key = { kid, publicJwk, sealed: await seal(await exportJWK(privateKey), (await sealKeys)[0]), createdAt: new Date(at).toISOString() };
225
- if (!after && store.addFirst) await store.addFirst(key);
226
- else await store.add(key);
227
- return key;
228
- }
229
- const age = (key) => now() - Date.parse(key.createdAt);
230
- function signer(kept) {
231
- const ripe = kept.find((k) => age(k) >= SIGN_AFTER_MS);
232
- if (!ripe) return { key: kept[kept.length - 1], until: now() + SETTLING_TTL_MS };
233
- const until = ripe === kept[0] ? now() + CURRENT_TTL_MS : Math.min(now() + CURRENT_TTL_MS, Date.parse(kept[0].createdAt) + SIGN_AFTER_MS);
234
- return { key: ripe, until };
235
- }
236
- async function open(key) {
237
- const ring = await sealKeys;
238
- for (let i = 0; i < ring.length; i++) {
239
- const jwk = await unseal(key.sealed, ring[i]);
240
- if (jwk !== null) return { jwk, fresh: i === 0 };
241
- }
242
- return null;
243
- }
244
- async function keptKeys() {
245
- const kept = newestFirst(await store.list());
246
- if (kept.length > 0) return kept;
247
- making ??= make().finally(() => {
248
- making = void 0;
249
- });
250
- await making;
251
- const again = newestFirst(await store.list());
252
- if (again.length === 0) throw new CauthyConfigError("the key store kept no key: check that it writes");
253
- return again;
254
- }
255
- return {
256
- async current() {
257
- if (remembered && remembered.until > now()) return remembered;
258
- const { key, until } = signer(await keptKeys());
259
- if (remembered?.kid === key.kid) {
260
- remembered.until = until;
261
- return remembered;
262
- }
263
- const opened = await open(key);
264
- if (opened === null) {
265
- throw new CauthyConfigError(`the key ${key.kid} cannot be opened with the root key (PACT_ROOT_KEY): if the root key was rotated, set the previous one as PACT_ROOT_KEY_PREVIOUS`);
266
- }
267
- remembered = { kid: key.kid, privateKey: await importJWK(opened.jwk, "RS256"), until };
268
- return remembered;
269
- },
270
- async published() {
271
- const kept = await keptKeys();
272
- const out2 = [];
273
- for (let i = 0; i < kept.length; i++) {
274
- if (i === 0 || now() - Date.parse(kept[i - 1].createdAt) < RETIRE_AFTER_MS) out2.push(published(kept[i]));
275
- else if (now() - Date.parse(kept[i - 1].createdAt) > FORGET_AFTER_MS) await store.remove(kept[i].kid);
276
- }
277
- return { keys: out2 };
278
- },
279
- async rotate() {
280
- const key = await make(newestFirst(await store.list())[0]);
281
- remembered = void 0;
282
- return key.kid;
283
- },
284
- async reseal() {
285
- let changed = 0;
286
- const ring = await sealKeys;
287
- for (const key of await store.list()) {
288
- const opened = await open(key);
289
- if (opened === null || opened.fresh) continue;
290
- await store.update({ ...key, sealed: await seal(opened.jwk, ring[0]) });
291
- changed++;
292
- }
293
- return changed;
294
- }
295
- };
296
- }
297
-
298
- // src/rootkey.ts
299
- var encoder2 = new TextEncoder();
300
- var MIN_ROOT_KEY = 32;
301
- async function derive(root, purpose) {
302
- const ikm = await crypto.subtle.importKey("raw", encoder2.encode(root), "HKDF", false, ["deriveBits"]);
303
- const bits = await crypto.subtle.deriveBits(
304
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder2.encode(`pact/${purpose}/v1`) },
305
- ikm,
306
- 256
307
- );
308
- return new Uint8Array(bits);
309
- }
310
- function deriveKeys(root, previous) {
311
- if (root.length < MIN_ROOT_KEY) {
312
- throw new CauthyConfigError(`rootKey (PACT_ROOT_KEY) must be at least ${MIN_ROOT_KEY} characters: 32 random bytes, e.g. openssl rand -base64 32`);
313
- }
314
- if (previous !== void 0 && previous !== "" && previous.length < MIN_ROOT_KEY) {
315
- throw new CauthyConfigError(`the previous root key (PACT_ROOT_KEY_PREVIOUS) must be at least ${MIN_ROOT_KEY} characters`);
316
- }
317
- const roots = previous && previous !== root ? [root, previous] : [root];
318
- const ring = (purpose) => Promise.all(roots.map((r) => derive(r, purpose)));
319
- return { seal: ring("seal"), cookie: ring("cookie"), flow: ring("flow") };
320
- }
321
-
322
67
  // src/deadline.ts
323
68
  var DEFAULT_DEADLINE_MS = 5e3;
324
69
  var isTimeout = (err) => err?.name === "TimeoutError";
@@ -352,6 +97,13 @@ function anySignal(signals) {
352
97
  return controller.signal;
353
98
  }
354
99
 
100
+ // src/url.ts
101
+ function withoutTrailingSlashes(s) {
102
+ let end = s.length;
103
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
104
+ return s.slice(0, end);
105
+ }
106
+
355
107
  // src/permissions.ts
356
108
  function matches(entry, resource, action) {
357
109
  const i = entry.lastIndexOf(":");
@@ -421,19 +173,19 @@ var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
421
173
  function policySource(system, read) {
422
174
  const roles = Array.isArray(read) ? read : read.roles;
423
175
  const catalog = Array.isArray(read) ? [] : read.catalog ?? [];
424
- const text2 = (v) => JSON.stringify(v);
425
- const key = (k) => IDENT.test(k) ? k : text2(k);
176
+ const text = (v) => JSON.stringify(v);
177
+ const key = (k) => IDENT.test(k) ? k : text(k);
426
178
  const one = (r, pad) => {
427
- const parts = [`role: ${text2(r.role)}`];
428
- if (r.name) parts.push(`name: ${text2(r.name)}`);
429
- if (r.description) parts.push(`description: ${text2(r.description)}`);
430
- if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text2).join(", ")}]`);
179
+ const parts = [`role: ${text(r.role)}`];
180
+ if (r.name) parts.push(`name: ${text(r.name)}`);
181
+ if (r.description) parts.push(`description: ${text(r.description)}`);
182
+ if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text).join(", ")}]`);
431
183
  if (r.permissions?.length) {
432
184
  const list = r.permissions.map((p) => `${pad} { ${[
433
- ...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text2(p.kind)}`] : [],
434
- `resource: ${text2(p.resource)}`,
435
- `action: ${text2(p.action)}`,
436
- ...p.condition ? [`condition: ${text2(p.condition)}`] : []
185
+ ...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text(p.kind)}`] : [],
186
+ `resource: ${text(p.resource)}`,
187
+ `action: ${text(p.action)}`,
188
+ ...p.condition ? [`condition: ${text(p.condition)}`] : []
437
189
  ].join(", ")} },`);
438
190
  parts.push(`permissions: [
439
191
  ${list.join("\n")}
@@ -445,14 +197,14 @@ ${pad} `)},
445
197
  ${pad} },`;
446
198
  };
447
199
  const resource = (c) => {
448
- const parts = [`resource: ${text2(c.resource)}`];
449
- if (c.description) parts.push(`description: ${text2(c.description)}`);
450
- parts.push(`actions: [${c.actions.map(text2).join(", ")}]`);
200
+ const parts = [`resource: ${text(c.resource)}`];
201
+ if (c.description) parts.push(`description: ${text(c.description)}`);
202
+ parts.push(`actions: [${c.actions.map(text).join(", ")}]`);
451
203
  const attrs = Object.entries(c.attributes ?? {});
452
- if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text2(v)}`).join(", ")} }`);
204
+ if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text(v)}`).join(", ")} }`);
453
205
  return ` { ${parts.join(", ")} },`;
454
206
  };
455
- const head = `// The catalog of roles of ${text2(system)}, as Cauthy has it.
207
+ const head = `// The catalog of roles of ${text(system)}, as Cauthy has it.
456
208
  // Written by \`pullPolicy\`: run it again after the catalog
457
209
  // changes, and do not edit this file by hand.
458
210
  `;
@@ -562,7 +314,7 @@ function organizationIdOf(o) {
562
314
  }
563
315
 
564
316
  // src/system.ts
565
- import { exportJWK as exportJWK2, importJWK as importJWK2, importPKCS8, SignJWT } from "jose";
317
+ import { exportJWK, importJWK, importPKCS8, SignJWT } from "jose";
566
318
 
567
319
  // src/theme.ts
568
320
  var MAX_RADIUS = 40;
@@ -579,7 +331,7 @@ function withoutComments(css) {
579
331
  }
580
332
  }
581
333
  function declarations(css) {
582
- const text2 = withoutComments(css);
334
+ const text = withoutComments(css);
583
335
  const out2 = [];
584
336
  const within = [];
585
337
  let chunk = "";
@@ -590,7 +342,7 @@ function declarations(css) {
590
342
  if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
591
343
  chunk = "";
592
344
  };
593
- for (const ch of text2) {
345
+ for (const ch of text) {
594
346
  if (quote) {
595
347
  chunk += ch;
596
348
  if (ch === quote) quote = "";
@@ -926,6 +678,22 @@ async function pushTheme(o) {
926
678
  }
927
679
  }
928
680
 
681
+ // src/types.ts
682
+ var CauthyConfigError = class extends Error {
683
+ constructor(message) {
684
+ super(`@cauthyhq/node: ${message}`);
685
+ this.name = "CauthyConfigError";
686
+ }
687
+ };
688
+ var CauthyKeyError = class extends CauthyConfigError {
689
+ constructor(reason, message) {
690
+ super(`CAUTHY_KEY: ${message} (${reason})`);
691
+ this.reason = reason;
692
+ this.name = "CauthyKeyError";
693
+ }
694
+ reason;
695
+ };
696
+
929
697
  // src/system.ts
930
698
  var ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
931
699
  var SKEW_MS = 3e4;
@@ -965,7 +733,7 @@ function asJwk(k) {
965
733
  }
966
734
  async function loadKey(k) {
967
735
  const jwk = asJwk(k);
968
- return jwk ? importJWK2(jwk, "RS256") : importPKCS8(k.privateKey, "RS256", { extractable: true });
736
+ return jwk ? importJWK(jwk, "RS256") : importPKCS8(k.privateKey, "RS256", { extractable: true });
969
737
  }
970
738
  function createSystem(given = {}) {
971
739
  const issuer = withoutTrailingSlashes(given.issuer ?? env("CAUTHY_ISSUER") ?? "");
@@ -992,10 +760,6 @@ function createSystem(given = {}) {
992
760
  if (given.signer) return;
993
761
  if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
994
762
  if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
995
- if (given.keys) {
996
- if (!given.keysClientId) throw new CauthyConfigError("the contract gives the system no identity of its own: its link needs a service block");
997
- return;
998
- }
999
763
  if (key ? key.keyId === "" : clientSecret === "") {
1000
764
  throw new CauthyConfigError(key ? "the key needs its id (CAUTHY_SYSTEM_KEY_ID)" : "to act on its own the application needs its secret (CAUTHY_CLIENT_SECRET) or a key (CAUTHY_SYSTEM_KEY)");
1001
765
  }
@@ -1013,14 +777,14 @@ function createSystem(given = {}) {
1013
777
  let asking;
1014
778
  async function ask() {
1015
779
  configured();
1016
- const self = given.signer ? given.signer.clientId : given.keys ? given.keysClientId : key?.clientId || clientId;
780
+ const self = given.signer ? given.signer.clientId : key?.clientId || clientId;
1017
781
  const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
1018
782
  if (given.signer) {
1019
783
  const s = await given.signer.signer;
1020
784
  form.set("client_assertion_type", ASSERTION_TYPE);
1021
785
  form.set("client_assertion", await s.assertion(self, tokenEndpoint));
1022
- } else if (given.keys || key) {
1023
- const signer = given.keys ? await given.keys.current() : { kid: key.keyId, privateKey: await signingKey() };
786
+ } else if (key) {
787
+ const signer = { kid: key.keyId, privateKey: await signingKey() };
1024
788
  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);
1025
789
  form.set("client_assertion_type", ASSERTION_TYPE);
1026
790
  form.set("client_assertion", assertion);
@@ -1129,9 +893,8 @@ function createSystem(given = {}) {
1129
893
  return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
1130
894
  },
1131
895
  async publicJwks() {
1132
- if (given.keys) return given.keys.published();
1133
896
  if (!key) return { keys: [] };
1134
- const { kty, n, e } = asJwk(key) ?? await exportJWK2(await signingKey());
897
+ const { kty, n, e } = asJwk(key) ?? await exportJWK(await signingKey());
1135
898
  return { keys: [{ kty, n, e, kid: key.keyId, alg: "RS256", use: "sig" }] };
1136
899
  }
1137
900
  };
@@ -1465,8 +1228,8 @@ var fail = (reason, message) => {
1465
1228
  throw new CauthyKeyError(reason, message);
1466
1229
  };
1467
1230
  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, "");
1231
+ function parseKey(text) {
1232
+ const s = text.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
1470
1233
  if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
1471
1234
  const head = s.split(".", 1)[0];
1472
1235
  if (head !== KEY_PREFIX) {
@@ -1649,10 +1412,10 @@ async function finish(res, route, cacheable = false) {
1649
1412
  if (!cacheable && !(headers.get("cache-control") ?? "").includes("no-store")) headers.set("cache-control", "no-store");
1650
1413
  let body = res.body;
1651
1414
  if (route !== null && SIGN_IN_ROUTES.has(route) && res.ok && (headers.get("content-type") ?? "").startsWith("application/json")) {
1652
- const text2 = await res.text();
1653
- body = text2;
1415
+ const text = await res.text();
1416
+ body = text;
1654
1417
  try {
1655
- const parsed = JSON.parse(text2);
1418
+ const parsed = JSON.parse(text);
1656
1419
  const next = nextOf(route, parsed?.status);
1657
1420
  if (parsed !== null && next !== void 0) body = JSON.stringify({ ...parsed, next });
1658
1421
  } catch {
@@ -1668,24 +1431,24 @@ import { compactVerify, createLocalJWKSet } from "jose";
1668
1431
  // src/json.ts
1669
1432
  var SPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r"]);
1670
1433
  var LIMIT = 2n ** 53n;
1671
- function parseStrict(text2, integerMembers = []) {
1434
+ function parseStrict(text, integerMembers = []) {
1672
1435
  let i = 0;
1673
1436
  let depth = 0;
1674
1437
  const fail2 = (what) => {
1675
1438
  throw new SyntaxError(`${what} at ${i}`);
1676
1439
  };
1677
1440
  const skip = () => {
1678
- while (i < text2.length && SPACE.has(text2[i])) i++;
1441
+ while (i < text.length && SPACE.has(text[i])) i++;
1679
1442
  };
1680
1443
  const value = (integer = false) => {
1681
1444
  skip();
1682
- const c = text2[i];
1445
+ const c = text[i];
1683
1446
  if (c === "{") return object();
1684
1447
  if (c === "[") return array();
1685
1448
  if (c === '"') return string();
1686
1449
  if (c === "-" || c >= "0" && c <= "9") return number(integer);
1687
1450
  for (const [word, v] of [["true", true], ["false", false], ["null", null]]) {
1688
- if (text2.startsWith(word, i)) {
1451
+ if (text.startsWith(word, i)) {
1689
1452
  i += word.length;
1690
1453
  return v;
1691
1454
  }
@@ -1695,13 +1458,13 @@ function parseStrict(text2, integerMembers = []) {
1695
1458
  const string = () => {
1696
1459
  const start = i;
1697
1460
  i++;
1698
- while (i < text2.length && text2[i] !== '"') i += text2[i] === "\\" ? 2 : 1;
1699
- if (text2[i] !== '"') fail2("unterminated string");
1461
+ while (i < text.length && text[i] !== '"') i += text[i] === "\\" ? 2 : 1;
1462
+ if (text[i] !== '"') fail2("unterminated string");
1700
1463
  i++;
1701
- return JSON.parse(text2.slice(start, i));
1464
+ return JSON.parse(text.slice(start, i));
1702
1465
  };
1703
1466
  const number = (integer) => {
1704
- const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text2.slice(i));
1467
+ const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i));
1705
1468
  if (!m) return fail2("bad number");
1706
1469
  i += m[0].length;
1707
1470
  if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
@@ -1716,7 +1479,7 @@ function parseStrict(text2, integerMembers = []) {
1716
1479
  depth++;
1717
1480
  const out2 = [];
1718
1481
  skip();
1719
- if (text2[i] === "]") {
1482
+ if (text[i] === "]") {
1720
1483
  i++;
1721
1484
  depth--;
1722
1485
  return out2;
@@ -1724,8 +1487,8 @@ function parseStrict(text2, integerMembers = []) {
1724
1487
  for (; ; ) {
1725
1488
  out2.push(value());
1726
1489
  skip();
1727
- if (text2[i] === ",") i++;
1728
- else if (text2[i] === "]") {
1490
+ if (text[i] === ",") i++;
1491
+ else if (text[i] === "]") {
1729
1492
  i++;
1730
1493
  depth--;
1731
1494
  return out2;
@@ -1738,24 +1501,24 @@ function parseStrict(text2, integerMembers = []) {
1738
1501
  const out2 = {};
1739
1502
  const seen = /* @__PURE__ */ new Set();
1740
1503
  skip();
1741
- if (text2[i] === "}") {
1504
+ if (text[i] === "}") {
1742
1505
  i++;
1743
1506
  depth--;
1744
1507
  return out2;
1745
1508
  }
1746
1509
  for (; ; ) {
1747
1510
  skip();
1748
- if (text2[i] !== '"') fail2("expected a name");
1511
+ if (text[i] !== '"') fail2("expected a name");
1749
1512
  const name = string();
1750
1513
  if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
1751
1514
  seen.add(name);
1752
1515
  skip();
1753
- if (text2[i] !== ":") fail2("expected :");
1516
+ if (text[i] !== ":") fail2("expected :");
1754
1517
  i++;
1755
1518
  Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
1756
1519
  skip();
1757
- if (text2[i] === ",") i++;
1758
- else if (text2[i] === "}") {
1520
+ if (text[i] === ",") i++;
1521
+ else if (text[i] === "}") {
1759
1522
  i++;
1760
1523
  depth--;
1761
1524
  return out2;
@@ -1764,7 +1527,7 @@ function parseStrict(text2, integerMembers = []) {
1764
1527
  };
1765
1528
  const result = value();
1766
1529
  skip();
1767
- if (i < text2.length) fail2("trailing characters");
1530
+ if (i < text.length) fail2("trailing characters");
1768
1531
  return result;
1769
1532
  }
1770
1533
 
@@ -1785,6 +1548,15 @@ function deprecated(what, instead) {
1785
1548
  var enc2 = new TextEncoder();
1786
1549
  var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
1787
1550
  var ASSERTION_TYPE2 = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
1551
+ async function ringKey(secret, purpose) {
1552
+ const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
1553
+ return new Uint8Array(await crypto.subtle.deriveBits(
1554
+ { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: enc2.encode(`pact/${purpose}/v1`) },
1555
+ ikm,
1556
+ 256
1557
+ ));
1558
+ }
1559
+ var ringOf = (secrets, purpose) => Promise.all(secrets.map((s) => ringKey(s, purpose)));
1788
1560
  async function seedOf(secret) {
1789
1561
  const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
1790
1562
  return new Uint8Array(await crypto.subtle.deriveBits(
@@ -1822,12 +1594,11 @@ async function signerOf(secret) {
1822
1594
  }
1823
1595
  function deriveFromKey(key, previous) {
1824
1596
  const other = previous && previous.secret !== key.secret ? previous : void 0;
1825
- const rings = deriveKeys(key.secret, other?.secret);
1597
+ const secrets = other ? [key.secret, other.secret] : [key.secret];
1826
1598
  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 })) } : {}
1599
+ cookie: ringOf(secrets, "cookie"),
1600
+ flow: ringOf(secrets, "flow"),
1601
+ signer: signerOf(key.secret)
1831
1602
  };
1832
1603
  }
1833
1604
 
@@ -1946,8 +1717,8 @@ var CauthyTokenError = class extends Error {
1946
1717
  detail;
1947
1718
  };
1948
1719
  var MAX_TOKEN_BYTES = 8192;
1949
- var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1950
- var NIL_UUID2 = "00000000-0000-0000-0000-000000000000";
1720
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1721
+ var NIL_UUID = "00000000-0000-0000-0000-000000000000";
1951
1722
  var SEGMENT = /^[A-Za-z0-9_-]+$/;
1952
1723
  var MAX_KEYS_BYTES = 64 * 1024;
1953
1724
  var KEYS_TIMEOUT_MS = 5e3;
@@ -2027,7 +1798,7 @@ async function readCapped(res, max) {
2027
1798
  }
2028
1799
  return out2;
2029
1800
  }
2030
- var published2 = /* @__PURE__ */ new Map();
1801
+ var published = /* @__PURE__ */ new Map();
2031
1802
  var withFetch = /* @__PURE__ */ new WeakMap();
2032
1803
  function remoteKeysFor(issuer, fetchKeys) {
2033
1804
  let byIssuer = withFetch.get(fetchKeys);
@@ -2037,10 +1808,10 @@ function remoteKeysFor(issuer, fetchKeys) {
2037
1808
  return set;
2038
1809
  }
2039
1810
  function keysOf2(issuer) {
2040
- let set = published2.get(issuer);
1811
+ let set = published.get(issuer);
2041
1812
  if (set === void 0) {
2042
1813
  set = remoteKeys(issuer);
2043
- published2.set(issuer, set);
1814
+ published.set(issuer, set);
2044
1815
  }
2045
1816
  return set;
2046
1817
  }
@@ -2082,7 +1853,7 @@ function createTokenVerifier(options = {}) {
2082
1853
  if (audience === "") throw new CauthyConfigError("audience (CAUTHY_AUDIENCE) is not set: without it a token issued for another application would be accepted");
2083
1854
  const tenant = options.tenantId ?? env2("CAUTHY_TENANT_ID") ?? "";
2084
1855
  if (tenant === "") throw new CauthyConfigError("tenantId (CAUTHY_TENANT_ID) is not set: without it a token of another workspace would be accepted");
2085
- if (!UUID2.test(tenant) || tenant === NIL_UUID2) throw new CauthyConfigError("tenantId must be the workspace's id, a lowercase UUID that is not the nil UUID");
1856
+ if (!UUID.test(tenant) || tenant === NIL_UUID) throw new CauthyConfigError("tenantId must be the workspace's id, a lowercase UUID that is not the nil UUID");
2086
1857
  const leeway = options.leeway ?? 30;
2087
1858
  if (!Number.isFinite(leeway) || leeway < 0 || leeway > 60) throw new CauthyConfigError("leeway must be 0 to 60 seconds");
2088
1859
  return async (from) => {
@@ -2131,8 +1902,8 @@ function createTokenVerifier(options = {}) {
2131
1902
  throw new CauthyTokenError("invalid_claim", name);
2132
1903
  };
2133
1904
  if (typeof claims.iss !== "string") bad("iss");
2134
- if (typeof claims.sub !== "string" || !UUID2.test(claims.sub)) bad("sub");
2135
- if (typeof claims.tid !== "string" || !UUID2.test(claims.tid)) bad("tid");
1905
+ if (typeof claims.sub !== "string" || !UUID.test(claims.sub)) bad("sub");
1906
+ if (typeof claims.tid !== "string" || !UUID.test(claims.tid)) bad("tid");
2136
1907
  const aud = claims.aud;
2137
1908
  if (!(typeof aud === "string" || Array.isArray(aud) && aud.length > 0 && aud.every((a) => typeof a === "string"))) bad("aud");
2138
1909
  for (const name of ["exp", "iat", "nbf"]) if (claims[name] !== void 0 && !Number.isInteger(claims[name])) bad(name);
@@ -2170,13 +1941,13 @@ async function verifyToken(from, options = {}) {
2170
1941
  return createTokenVerifier(options)(from);
2171
1942
  }
2172
1943
  async function createVerifierFromEnv(options = {}) {
2173
- const text2 = options.key ?? env2("CAUTHY_KEY");
2174
- if (text2 === void 0 || text2 === "") return createTokenVerifier(options);
1944
+ const text = options.key ?? env2("CAUTHY_KEY");
1945
+ if (text === void 0 || text === "") return createTokenVerifier(options);
2175
1946
  refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
2176
1947
  for (const name of ["issuer", "audience", "tenantId"]) {
2177
1948
  if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
2178
1949
  }
2179
- const key = parseKey(text2);
1950
+ const key = parseKey(text);
2180
1951
  const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
2181
1952
  const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
2182
1953
  const { key: _, ...rest } = options;
@@ -2274,8 +2045,8 @@ function conditionalOf(c) {
2274
2045
  return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
2275
2046
  }
2276
2047
  function byKey(given) {
2277
- const text2 = given.key ?? env3("CAUTHY_KEY");
2278
- if (text2 === void 0 || text2 === "") return void 0;
2048
+ const text = given.key ?? env3("CAUTHY_KEY");
2049
+ if (text === void 0 || text === "") return void 0;
2279
2050
  refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
2280
2051
  for (const [name, value] of [
2281
2052
  ["issuer", given.issuer],
@@ -2286,12 +2057,11 @@ function byKey(given) {
2286
2057
  ["tenant", given.tenant],
2287
2058
  ["audience", given.audience],
2288
2059
  ["tenantId", given.tenantId],
2289
- ["systemKey", given.systemKey],
2290
- ["rootKey", given.rootKey]
2060
+ ["systemKey", given.systemKey]
2291
2061
  ]) {
2292
2062
  if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
2293
2063
  }
2294
- const key = parseKey(text2);
2064
+ const key = parseKey(text);
2295
2065
  const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
2296
2066
  let previous;
2297
2067
  if (prevText) {
@@ -2306,14 +2076,8 @@ function byKey(given) {
2306
2076
  throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
2307
2077
  }
2308
2078
  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
2079
  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 };
2080
+ return { key, previous, legacySecret };
2317
2081
  }
2318
2082
  var UnknownOriginError = class extends Error {
2319
2083
  constructor(origin, registered) {
@@ -2324,78 +2088,40 @@ var UnknownOriginError = class extends Error {
2324
2088
  origin;
2325
2089
  registered;
2326
2090
  };
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
- }
2334
- function byContract(given) {
2335
- failClosed(typeof process !== "undefined" ? process.env : void 0);
2336
- for (const [name, value] of [
2337
- ["clientId", given.clientId],
2338
- ["clientSecret", given.clientSecret],
2339
- ["secret", given.secret],
2340
- ["system", given.system],
2341
- ["tenant", given.tenant],
2342
- ["systemKey", given.systemKey]
2343
- ]) {
2344
- if (value !== void 0) throw new CauthyConfigError(`with a contract, ${name} comes from the contract: do not pass it`);
2345
- }
2346
- const contract = readContract(given.contract);
2347
- const root = given.rootKey ?? env3("PACT_ROOT_KEY") ?? "";
2348
- if (root === "") throw new CauthyConfigError("rootKey (PACT_ROOT_KEY) is required with a contract: 32 random bytes, e.g. openssl rand -base64 32");
2349
- if (contract.serviceClientId && !given.keys) {
2350
- throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
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.");
2353
- return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
2354
- }
2355
2091
  var toldOfHeader = false;
2356
2092
  var toldOfUnopened = false;
2357
2093
  function toldUnopened() {
2358
2094
  if (toldOfUnopened) return;
2359
2095
  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.");
2096
+ 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) beside it for the rotation window; rotating the key, set CAUTHY_KEY_PREVIOUS.");
2361
2097
  }
2362
2098
  function createCauthy(given = {}) {
2363
2099
  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") ?? "";
2100
+ const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? env3("CAUTHY_ISSUER") ?? "");
2101
+ const clientId = keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
2102
+ const clientSecret = keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
2103
+ const secret = keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
2104
+ const system = keyed ? "" : given.system ?? env3("CAUTHY_SYSTEM") ?? "";
2105
+ const tenant = keyed ? "" : given.tenant ?? env3("CAUTHY_TENANT") ?? "";
2373
2106
  if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
2374
2107
  if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
2375
- if (!contract && !keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
2108
+ if (!keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
2376
2109
  const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
2377
2110
  let signingKid;
2378
2111
  fromKey?.signer.then((s) => {
2379
2112
  signingKid = s.kid;
2380
2113
  }, () => void 0);
2381
- const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
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
2114
  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) {
2115
+ const withLegacy = async (own) => [...await own, ...legacySingle ? [await legacySingle] : []];
2116
+ const single = fromKey ? Promise.resolve([]) : keyOf(secret).then((k) => [k]);
2117
+ const cookieKeys = fromKey ? withLegacy(fromKey.cookie) : single;
2118
+ const flowKeys = fromKey ? withLegacy(fromKey.flow) : single;
2119
+ if (!keyed) {
2393
2120
  deprecated(
2394
2121
  "Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
2395
2122
  "Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
2396
2123
  );
2397
2124
  }
2398
- const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
2399
2125
  const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
2400
2126
  if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
2401
2127
  throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
@@ -2432,21 +2158,10 @@ function createCauthy(given = {}) {
2432
2158
  acceptInviteUrl,
2433
2159
  fetch: given.fetch,
2434
2160
  timeoutMs: systemTimeout,
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
- } : {}
2441
- });
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}`)
2161
+ // On CAUTHY_KEY the key proves the key's own client.
2162
+ ...fromKey ? { signer: { clientId, signer: fromKey.signer }, currentSystem: () => identity?.current()?.system ?? "" } : {}
2448
2163
  });
2449
- const identity = askSelf ? createIdentity({
2164
+ const identity = keyed ? createIdentity({
2450
2165
  issuer,
2451
2166
  token: () => ownSystem.token(),
2452
2167
  fetch: deadline(given.fetch, requestTimeout),
@@ -2456,6 +2171,8 @@ function createCauthy(given = {}) {
2456
2171
  identity?.ready().catch(() => {
2457
2172
  });
2458
2173
  const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
2174
+ const signInClientNow = () => identity?.current()?.sign_in_client_id || clientId;
2175
+ const signInClient = async () => identity ? (await identity.ready()).sign_in_client_id || clientId : clientId;
2459
2176
  const systemNow = () => identity?.current()?.system ?? system;
2460
2177
  const requestOrigin = (request) => {
2461
2178
  const url = new URL(request.url);
@@ -2480,7 +2197,7 @@ function createCauthy(given = {}) {
2480
2197
  };
2481
2198
  const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
2482
2199
  async function clientAuth(form) {
2483
- if (fromKey && !contract) {
2200
+ if (fromKey && form.get("client_id") === clientId) {
2484
2201
  const s = await fromKey.signer;
2485
2202
  form.set("client_assertion_type", ASSERTION_TYPE2);
2486
2203
  form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
@@ -2490,8 +2207,8 @@ function createCauthy(given = {}) {
2490
2207
  return form;
2491
2208
  }
2492
2209
  async function token(form, proof = {}) {
2493
- const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
2494
2210
  try {
2211
+ const body2 = await clientAuth(new URLSearchParams({ client_id: await signInClient(), ...form }));
2495
2212
  const res = await doFetch(`${issuer}/v1/oauth/token`, {
2496
2213
  method: "POST",
2497
2214
  headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json", ...proof },
@@ -2529,18 +2246,16 @@ function createCauthy(given = {}) {
2529
2246
  }
2530
2247
  const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
2531
2248
  async function revoke(refreshToken) {
2532
- const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: refreshToken }));
2533
- await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
2249
+ try {
2250
+ const form = await clientAuth(new URLSearchParams({ client_id: await signInClient(), token: refreshToken }));
2251
+ await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
2252
+ } catch {
2253
+ }
2534
2254
  }
2535
- const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
2536
2255
  async function stored(body2, was) {
2537
2256
  if (typeof body2?.access_token !== "string" || typeof body2?.refresh_token !== "string") return null;
2538
2257
  const who = await userinfo(body2.access_token);
2539
2258
  if (who === null || who.u.id === "") return null;
2540
- if (unverified(who.u)) {
2541
- await revoke(body2.refresh_token);
2542
- throw new CauthyApiError(403, "email_not_confirmed", "Confirm your email address first.");
2543
- }
2544
2259
  const lifetime = typeof body2.expires_in === "number" && body2.expires_in > 0 ? body2.expires_in : 300;
2545
2260
  return { at: body2.access_token, rt: body2.refresh_token, exp: Math.floor(Date.now() / 1e3) + lifetime, ...who, ...kindOf(body2, was) };
2546
2261
  }
@@ -2568,13 +2283,7 @@ function createCauthy(given = {}) {
2568
2283
  running = (async () => {
2569
2284
  const res = await token({ grant_type: "refresh_token", refresh_token: rt });
2570
2285
  if (!res.ok) return res.ended ? "ended" : "unreachable";
2571
- let next;
2572
- try {
2573
- next = await stored(res.body, was);
2574
- } catch (err) {
2575
- if (err instanceof CauthyApiError && err.code === "email_not_confirmed") return "ended";
2576
- throw err;
2577
- }
2286
+ const next = await stored(res.body, was);
2578
2287
  if (next !== null) return next;
2579
2288
  if (typeof res.body?.access_token !== "string" || typeof res.body?.refresh_token !== "string") return "unreachable";
2580
2289
  const lifetime = typeof res.body.expires_in === "number" && res.body.expires_in > 0 ? res.body.expires_in : 300;
@@ -2596,7 +2305,6 @@ function createCauthy(given = {}) {
2596
2305
  if (present) toldUnopened();
2597
2306
  return { session: null, status: "unauthenticated", cookies: present ? clearParts(cookieName, attrs(request, 0), had) : [] };
2598
2307
  }
2599
- if (unverified(value.u)) return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
2600
2308
  if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: stale ? await write(request, value, had) : [] };
2601
2309
  const renewed = await renew(value);
2602
2310
  if (renewed === "ended") {
@@ -2608,7 +2316,7 @@ function createCauthy(given = {}) {
2608
2316
  async function getSession(request, responseHeaders) {
2609
2317
  if (responseHeaders === void 0) {
2610
2318
  const { value } = await read(request);
2611
- return value !== null && fresh(value) && !unverified(value.u) ? toSession(value) : null;
2319
+ return value !== null && fresh(value) ? toSession(value) : null;
2612
2320
  }
2613
2321
  const r = await resolve2(request);
2614
2322
  for (const c of r.cookies) responseHeaders.append("set-cookie", c);
@@ -2616,7 +2324,7 @@ function createCauthy(given = {}) {
2616
2324
  }
2617
2325
  async function status(request) {
2618
2326
  const { value } = await read(request);
2619
- if (value === null || unverified(value.u)) return "unauthenticated";
2327
+ if (value === null) return "unauthenticated";
2620
2328
  return fresh(value) ? "authenticated" : "expired";
2621
2329
  }
2622
2330
  const orgCache = /* @__PURE__ */ new Map();
@@ -2703,7 +2411,7 @@ function createCauthy(given = {}) {
2703
2411
  const flow = { state: random(), verifier: random(48), returnTo: localPath(url.searchParams.get("return_to"), afterSignIn) };
2704
2412
  const challenge = b64url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(flow.verifier))));
2705
2413
  const q = new URLSearchParams({
2706
- client_id: clientId,
2414
+ client_id: await signInClient(),
2707
2415
  redirect_uri: `${originOf(request)}${basePath}/callback`,
2708
2416
  response_type: "code",
2709
2417
  state: flow.state,
@@ -2765,28 +2473,17 @@ function createCauthy(given = {}) {
2765
2473
  );
2766
2474
  };
2767
2475
  if (!res.ok && res.code === "email_not_confirmed") return unconfirmed();
2768
- let s;
2769
- try {
2770
- s = res.ok ? await stored(res.body) : null;
2771
- } catch (err) {
2772
- if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
2773
- return unconfirmed();
2774
- }
2476
+ const s = res.ok ? await stored(res.body) : null;
2775
2477
  if (s === null) return failed(request, "exchange_failed");
2776
2478
  return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0)), writeCookie(againCookie, "", attrs(request, 0))]);
2777
2479
  }
2778
2480
  async function logout(request) {
2779
2481
  const { had, value } = await read(request);
2780
2482
  if (value !== null) {
2781
- const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
2782
- await doFetch(`${issuer}/v1/oauth/revoke`, {
2783
- method: "POST",
2784
- headers: { "content-type": "application/x-www-form-urlencoded" },
2785
- body: body2
2786
- }).catch(() => void 0);
2483
+ await revoke(value.rt);
2787
2484
  orgCache.delete(value.at);
2788
2485
  }
2789
- const q = new URLSearchParams({ client_id: clientId, post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2486
+ const q = new URLSearchParams({ client_id: signInClientNow(), post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2790
2487
  return redirect(`${issuer}/v1/oauth/logout?${q}`, clearParts(cookieName, attrs(request, 0), had));
2791
2488
  }
2792
2489
  const STEP_SECONDS = 600;
@@ -2804,7 +2501,7 @@ function createCauthy(given = {}) {
2804
2501
  createdAt: String(d?.created_at ?? ""),
2805
2502
  lastUsedAt: d?.last_used_at ? String(d.last_used_at) : null
2806
2503
  });
2807
- const UUID3 = /^[0-9a-f-]{36}$/i;
2504
+ const UUID2 = /^[0-9a-f-]{36}$/i;
2808
2505
  const seatOf = (d) => ({
2809
2506
  id: String(d?.id ?? ""),
2810
2507
  system: String(d?.system ?? ""),
@@ -2909,10 +2606,10 @@ function createCauthy(given = {}) {
2909
2606
  ...page ? { "x-cauthy-link-to": `${originOf(request)}${page}` } : { "x-cauthy-links": "hosted" },
2910
2607
  // Whose request it is: what Cauthy mails goes in this application's
2911
2608
  // name, under its logo.
2912
- ...clientId ? { "x-cauthy-client-id": clientId } : {},
2609
+ ...signInClientNow() ? { "x-cauthy-client-id": signInClientNow() } : {},
2913
2610
  ...await passedOn(request),
2914
2611
  ...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
2915
- // The person asked to stay signed in: Cauthy keeps the session long if the client allows it.
2612
+ // The person asked to stay signed in: Cauthy keeps the session long if the workspace allows it.
2916
2613
  ...remember ? { "x-cauthy-remember": "1" } : {},
2917
2614
  // The answer to the workspace's Turnstile challenge, from the
2918
2615
  // person's browser: Cauthy checks it at the doors that ask for it.
@@ -3053,7 +2750,7 @@ function createCauthy(given = {}) {
3053
2750
  }
3054
2751
  if (route === "/look" && !post) {
3055
2752
  if (lookCache === null || lookCache.until < Date.now()) {
3056
- const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(clientId)}`);
2753
+ const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(signInClientNow())}`);
3057
2754
  lookCache = { until: Date.now() + LOOK_TTL_MS, look: res.error ? {} : res.data ?? {} };
3058
2755
  }
3059
2756
  return json(lookCache.look);
@@ -3078,7 +2775,7 @@ function createCauthy(given = {}) {
3078
2775
  signUp: settings.data?.allow_signup !== false && settings.data?.signup?.mode !== "invite_only",
3079
2776
  // The application's system makes an organization per sign-up: the form asks the company's name.
3080
2777
  ...settings.data?.signup?.organization?.ask === true ? { organization: { ask: true, required: settings.data.signup.organization.required === true } } : {},
3081
- // Whether the person may ask to stay signed in: the client's choice in Cauthy.
2778
+ // Whether the person may ask to stay signed in: the workspace's setting in Cauthy (persistent_sessions).
3082
2779
  ...typeof settings.data?.persistent_sessions === "boolean" ? { persistentSessions: settings.data.persistent_sessions } : {},
3083
2780
  // A sign-in link needs a page of the application to open.
3084
2781
  magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
@@ -3242,7 +2939,7 @@ function createCauthy(given = {}) {
3242
2939
  let path = "/v1/auth/me/organizations";
3243
2940
  if (route !== "/me/organizations") {
3244
2941
  const id = url.searchParams.get("id") ?? "";
3245
- if (!UUID3.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
2942
+ if (!UUID2.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3246
2943
  path += `/${id}/${route.endsWith("/roles") ? "roles" : "members"}`;
3247
2944
  } else if (url.searchParams.get("system")) {
3248
2945
  path += `?system=${encodeURIComponent(url.searchParams.get("system"))}`;
@@ -3303,8 +3000,7 @@ function createCauthy(given = {}) {
3303
3000
  if (route === "/sign-out") {
3304
3001
  const { had, value } = await read(request);
3305
3002
  if (value !== null) {
3306
- const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
3307
- await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
3003
+ await revoke(value.rt);
3308
3004
  orgCache.delete(value.at);
3309
3005
  }
3310
3006
  const h = new Headers();
@@ -3531,7 +3227,7 @@ function createCauthy(given = {}) {
3531
3227
  }
3532
3228
  if (route === "/me/organizations/rename" || route === "/me/organizations/archive" || route === "/me/organizations/delete") {
3533
3229
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3534
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3230
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3535
3231
  const path = `/v1/auth/me/organizations/${org}`;
3536
3232
  if (route === "/me/organizations/rename") {
3537
3233
  const bad = checkOrganizationName(b.name);
@@ -3548,7 +3244,7 @@ function createCauthy(given = {}) {
3548
3244
  }
3549
3245
  if (route.startsWith("/me/organizations/members/")) {
3550
3246
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3551
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3247
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3552
3248
  const base = `/v1/auth/me/organizations/${org}/members`;
3553
3249
  if (route === "/me/organizations/members/add") {
3554
3250
  const bad = checkEmail(b.email);
@@ -3559,7 +3255,7 @@ function createCauthy(given = {}) {
3559
3255
  email: String(b.email).trim(),
3560
3256
  role,
3561
3257
  name: typeof b.name === "string" ? b.name.trim() : "",
3562
- client_id: clientId,
3258
+ client_id: signInClientNow(),
3563
3259
  only_new: true,
3564
3260
  ...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {}
3565
3261
  }, pages.acceptInvite, mine.token);
@@ -3568,7 +3264,7 @@ function createCauthy(given = {}) {
3568
3264
  return json({ status: "added", member: orgMemberOf(res.data) }, 200, mine.headers);
3569
3265
  }
3570
3266
  const user = typeof b.userId === "string" ? b.userId : "";
3571
- if (!UUID3.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
3267
+ if (!UUID2.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
3572
3268
  if (route === "/me/organizations/members/role") {
3573
3269
  const role = typeof b.role === "string" ? b.role : "";
3574
3270
  if (role === "") return problem(400, "invalid_role", "Choose a role.", "role", mine.headers);
@@ -3583,7 +3279,7 @@ function createCauthy(given = {}) {
3583
3279
  return json({ status: "removed", userId: user }, 200, mine.headers);
3584
3280
  }
3585
3281
  if (route === "/me/organizations/members/invite") {
3586
- const res = await ask(request, "POST", `${base}/${user}/invite`, { client_id: clientId, ...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {} }, pages.acceptInvite, mine.token);
3282
+ const res = await ask(request, "POST", `${base}/${user}/invite`, { client_id: signInClientNow(), ...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {} }, pages.acceptInvite, mine.token);
3587
3283
  if (res.error) return refused(res);
3588
3284
  return json({ status: "sent", userId: user }, 200, mine.headers);
3589
3285
  }
@@ -3701,18 +3397,15 @@ function createCauthy(given = {}) {
3701
3397
  const url = new URL(request.url);
3702
3398
  const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
3703
3399
  const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
3704
- const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
3705
3400
  if (identity && route !== null) {
3706
3401
  const unready = await identityGate();
3707
3402
  if (unready) return finish(unready, route);
3708
3403
  }
3709
3404
  try {
3710
- return finish(await serve(request), route, published3);
3405
+ return finish(await serve(request), route);
3711
3406
  } catch (err) {
3712
3407
  if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
3713
- if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
3714
- const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
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);
3408
+ throw err;
3716
3409
  }
3717
3410
  }
3718
3411
  async function identityGate() {
@@ -3736,7 +3429,6 @@ function createCauthy(given = {}) {
3736
3429
  }
3737
3430
  async function serve(request) {
3738
3431
  const url = new URL(request.url);
3739
- if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
3740
3432
  if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return new Response("Not found", { status: 404 });
3741
3433
  const route = withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/";
3742
3434
  const get = request.method === "GET" || request.method === "HEAD";
@@ -3760,7 +3452,7 @@ function createCauthy(given = {}) {
3760
3452
  return redirect(back, r.cookies);
3761
3453
  }
3762
3454
  if (route === "/account" && get) {
3763
- const q = new URLSearchParams({ client_id: clientId });
3455
+ const q = new URLSearchParams({ client_id: signInClientNow() });
3764
3456
  const back = localPath(url.searchParams.get("return_to"), "");
3765
3457
  if (back !== "") q.set("return_to", `${originOf(request)}${back}`);
3766
3458
  const locale = url.searchParams.get("locale");
@@ -3779,7 +3471,7 @@ function createCauthy(given = {}) {
3779
3471
  const asker = await signedInAs(request);
3780
3472
  if ("response" in asker) return asker.response;
3781
3473
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3782
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
3474
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
3783
3475
  const named = (v) => typeof v === "string" && v !== "" && v.length <= 200;
3784
3476
  if (!named(b.resource) || !named(b.action)) return problem(400, "bad_request", "Say which resource and action.", void 0, asker.headers);
3785
3477
  const attributes2 = b.attributes;
@@ -3802,8 +3494,7 @@ function createCauthy(given = {}) {
3802
3494
  }
3803
3495
  }
3804
3496
  async function protect(request) {
3805
- const read1 = await read(request);
3806
- const value = read1.value !== null && unverified(read1.value.u) ? null : read1.value;
3497
+ const { value } = await read(request);
3807
3498
  if (value !== null && fresh(value)) return { session: toSession(value) };
3808
3499
  const url = new URL(request.url);
3809
3500
  const state = value === null ? "none" : "expired";
@@ -3816,85 +3507,36 @@ function createCauthy(given = {}) {
3816
3507
  return { response: redirect(to) };
3817
3508
  }
3818
3509
  const replay = given.webhookReplay ?? memoryReplayStore();
3819
- let lateSelf;
3820
3510
  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
- }
3511
+ if (identity) {
3825
3512
  let built;
3826
3513
  return async (from) => {
3827
- const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
3514
+ const id = await identity.ready();
3828
3515
  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
- }) };
3516
+ built = { from: id, verify: createTokenVerifier({ issuer, tenantId: id.tenant.id, audience: id.audience, fetch: given.fetch, ...o }) };
3837
3517
  }
3838
3518
  return built.verify(from);
3839
3519
  };
3840
3520
  }
3841
- if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
3842
- throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
3843
- }
3844
3521
  return createTokenVerifier({
3845
3522
  issuer,
3846
- tenantId: contract?.tenantId ?? given.tenantId,
3523
+ tenantId: given.tenantId,
3847
3524
  audience: given.audience,
3848
3525
  fetch: given.fetch,
3849
- ...contract ? { clientId: contract.clientId } : {},
3850
3526
  ...o
3851
3527
  });
3852
3528
  };
3853
3529
  const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now, replay });
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 ? {
3878
- contract,
3879
- keys: ownKeys,
3880
- async jwks() {
3881
- return new Response(JSON.stringify(await ownKeys.published()), {
3882
- headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
3883
- });
3884
- },
3885
- status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
3886
- } : void 0;
3887
3530
  const configured = () => ({
3888
3531
  issuer,
3889
3532
  audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
3890
- tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3533
+ tenant: { id: given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3891
3534
  system,
3892
3535
  client_id: clientId,
3893
3536
  sign_in_client_id: clientId,
3894
3537
  app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
3895
3538
  callback_path: `${basePath}/callback`,
3896
- key: null,
3897
- contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
3539
+ key: null
3898
3540
  });
3899
3541
  return {
3900
3542
  handler,
@@ -3916,7 +3558,6 @@ function createCauthy(given = {}) {
3916
3558
  check,
3917
3559
  toPublic,
3918
3560
  urls,
3919
- ...pact ? { pact } : {},
3920
3561
  options: { issuer, clientId, basePath, afterSignIn, afterSignOut, cookieName }
3921
3562
  };
3922
3563
  }
@@ -4000,7 +3641,6 @@ export {
4000
3641
  KEY_REPLACES,
4001
3642
  MIN_PASSWORD,
4002
3643
  PROTOCOL_VERSION,
4003
- STANDARD_VARIABLES,
4004
3644
  can,
4005
3645
  checkCode,
4006
3646
  checkEmail,
@@ -4016,7 +3656,6 @@ export {
4016
3656
  definePolicy,
4017
3657
  describePasswordPolicy,
4018
3658
  explainPasswordProblems,
4019
- failClosed,
4020
3659
  memoryReplayStore,
4021
3660
  parseKey,
4022
3661
  passwordPolicyOf,
@@ -4027,7 +3666,6 @@ export {
4027
3666
  pushCatalog,
4028
3667
  pushPolicy,
4029
3668
  pushTheme,
4030
- readContract,
4031
3669
  remoteKeys,
4032
3670
  themeFromStackmyth,
4033
3671
  tokensFromStackmyth,