@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.js CHANGED
@@ -30,7 +30,6 @@ __export(src_exports, {
30
30
  KEY_REPLACES: () => KEY_REPLACES,
31
31
  MIN_PASSWORD: () => MIN_PASSWORD,
32
32
  PROTOCOL_VERSION: () => PROTOCOL_VERSION,
33
- STANDARD_VARIABLES: () => STANDARD_VARIABLES,
34
33
  can: () => can,
35
34
  checkCode: () => checkCode,
36
35
  checkEmail: () => checkEmail,
@@ -46,7 +45,6 @@ __export(src_exports, {
46
45
  definePolicy: () => definePolicy,
47
46
  describePasswordPolicy: () => describePasswordPolicy,
48
47
  explainPasswordProblems: () => explainPasswordProblems,
49
- failClosed: () => failClosed,
50
48
  memoryReplayStore: () => memoryReplayStore,
51
49
  parseKey: () => parseKey,
52
50
  passwordPolicyOf: () => passwordPolicyOf,
@@ -57,7 +55,6 @@ __export(src_exports, {
57
55
  pushCatalog: () => pushCatalog,
58
56
  pushPolicy: () => pushPolicy,
59
57
  pushTheme: () => pushTheme,
60
- readContract: () => readContract,
61
58
  remoteKeys: () => remoteKeys,
62
59
  themeFromStackmyth: () => themeFromStackmyth,
63
60
  tokensFromStackmyth: () => tokensFromStackmyth,
@@ -132,261 +129,6 @@ async function unsealIndexed(sealed, ring) {
132
129
  return null;
133
130
  }
134
131
 
135
- // src/types.ts
136
- var CauthyConfigError = class extends Error {
137
- constructor(message) {
138
- super(`@cauthyhq/node: ${message}`);
139
- this.name = "CauthyConfigError";
140
- }
141
- };
142
- var CauthyKeyError = class extends CauthyConfigError {
143
- constructor(reason, message) {
144
- super(`CAUTHY_KEY: ${message} (${reason})`);
145
- this.reason = reason;
146
- this.name = "CauthyKeyError";
147
- }
148
- reason;
149
- };
150
-
151
- // src/url.ts
152
- function withoutTrailingSlashes(s) {
153
- let end = s.length;
154
- while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
155
- return s.slice(0, end);
156
- }
157
-
158
- // src/contract.ts
159
- var STANDARD_VARIABLES = [
160
- "CAUTHY_ISSUER",
161
- "CAUTHY_CLIENT_ID",
162
- "CAUTHY_CLIENT_SECRET",
163
- "CAUTHY_SECRET",
164
- "CAUTHY_SYSTEM",
165
- "CAUTHY_TENANT",
166
- "CAUTHY_SYSTEM_KEY",
167
- "CAUTHY_SYSTEM_KEY_ID"
168
- ];
169
- function failClosed(environment) {
170
- const set = STANDARD_VARIABLES.filter((name) => (environment?.[name] ?? "") !== "");
171
- if (set.length > 0) {
172
- 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.");
173
- }
174
- }
175
- var SLUG = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/;
176
- var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
177
- var NIL_UUID = "00000000-0000-0000-0000-000000000000";
178
- var text = (v) => typeof v === "string" ? v : "";
179
- function httpsAddress(raw, what) {
180
- let u;
181
- try {
182
- u = new URL(raw);
183
- } catch {
184
- throw new CauthyConfigError(`the contract's ${what} is not an address: ${JSON.stringify(raw)}`);
185
- }
186
- const local = u.hostname === "localhost" || u.hostname === "127.0.0.1";
187
- if (u.protocol !== "https:" && !(u.protocol === "http:" && local)) {
188
- throw new CauthyConfigError(`the contract's ${what} must be https: ${raw}`);
189
- }
190
- return u;
191
- }
192
- function readContract(options) {
193
- const { system } = options;
194
- const provider = options.provider ?? "cauthy";
195
- if (!SLUG.test(system ?? "")) throw new CauthyConfigError(`contract.system must be the system's id in the graph, e.g. "acme-crm"`);
196
- const graph = options.graph && typeof options.graph === "object" && "default" in options.graph && !("edges" in options.graph) ? options.graph.default : options.graph;
197
- if (!graph || !Array.isArray(graph.edges) || !Array.isArray(graph.nodes)) {
198
- throw new CauthyConfigError("contract.graph is not a context graph: pass the graph.json that `cauthy contracts pull` writes");
199
- }
200
- const node = (id) => graph.nodes?.find((n) => n.type === "system" && n.id === id);
201
- const own = node(system);
202
- if (!own) throw new CauthyConfigError(`the graph has no system "${system}"`);
203
- const cauthy = node(provider);
204
- if (!cauthy) throw new CauthyConfigError(`the graph has no system "${provider}"`);
205
- 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);
206
- if (links.length === 0) {
207
- 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`);
208
- }
209
- if (links.length > 1) {
210
- throw new CauthyConfigError(`${system} has ${links.length} links to ${provider}: name the one to read (contract.link): ${links.map((l) => text(l.link)).join(", ")}`);
211
- }
212
- const e = links[0];
213
- const link = text(e.link);
214
- const named = text(e.identity?.tenant);
215
- const pinned = text(e.identity?.tenant_id);
216
- 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`);
217
- let tenantId;
218
- let tenant;
219
- if (UUID.test(named)) {
220
- if (pinned !== "" && pinned.toLowerCase() !== named.toLowerCase()) {
221
- throw new CauthyConfigError(`the link ${link} names the workspace ${named} and its tenant_id ${pinned} does not match: pull the graph again`);
222
- }
223
- tenantId = named;
224
- tenant = text((cauthy.tenants ?? []).find((t) => t.id === named)?.slug);
225
- if (tenant === "") throw new CauthyConfigError(`the graph does not name the slug of the workspace ${named} that the link ${link} uses`);
226
- } else {
227
- if (!SLUG.test(named)) throw new CauthyConfigError(`the link ${link} names no workspace: identity.tenant is a UUID or a slug`);
228
- if (!UUID.test(pinned)) {
229
- 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`);
230
- }
231
- tenantId = pinned;
232
- tenant = named;
233
- }
234
- const issuer = withoutTrailingSlashes(httpsAddress(text(cauthy.url), `address of ${provider}`).href);
235
- const signIn = e.sign_in ?? {};
236
- const clientId = text(signIn.client_id) || `pact.${link}`;
237
- const redirectUris = Array.isArray(signIn.redirect_uris) ? signIn.redirect_uris.map(text).filter(Boolean) : [];
238
- if (redirectUris.length === 0) throw new CauthyConfigError(`the link ${link} names no redirect URI`);
239
- const proof = text(signIn.system_proof);
240
- if (signIn.system_proof !== void 0 && proof !== "off" && proof !== "auto" && proof !== "required") {
241
- 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`);
242
- }
243
- const systemProof = proof === "off" || proof === "auto" || proof === "required" ? proof : signIn.require_system_proof === true ? "required" : "off";
244
- if (systemProof !== "off" && !e.service) {
245
- 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`);
246
- }
247
- const out2 = {
248
- system,
249
- link,
250
- issuer,
251
- tenantId,
252
- tenant,
253
- clientId,
254
- redirectUris,
255
- requireSystemProof: systemProof === "required",
256
- systemProof,
257
- requireVerifiedEmail: signIn.require_verified_email === true,
258
- persistentSessions: signIn.persistent_sessions === true
259
- };
260
- if (e.service) {
261
- out2.serviceClientId = text(e.service.client_id) || `pact.${link}.service`;
262
- const jwks = text(e.service.jwks_url) || text(own.jwks_url);
263
- if (jwks === "") throw new CauthyConfigError(`the link ${link} gives ${system} its own identity, and ${system} names no jwks_url`);
264
- out2.jwksUrl = httpsAddress(jwks, "jwks_url").href;
265
- out2.jwksPath = new URL(out2.jwksUrl).pathname;
266
- }
267
- if (text(own.events_url) !== "") out2.eventsUrl = httpsAddress(text(own.events_url), "events_url").href;
268
- return out2;
269
- }
270
-
271
- // src/keys.ts
272
- var import_jose2 = require("jose");
273
- var RETIRE_AFTER_MS = 24 * 60 * 60 * 1e3;
274
- var FORGET_AFTER_MS = 7 * 24 * 60 * 60 * 1e3;
275
- var CURRENT_TTL_MS = 5 * 60 * 1e3;
276
- var JWKS_MAX_AGE_S = 300;
277
- var SIGN_AFTER_MS = (JWKS_MAX_AGE_S + 60) * 1e3;
278
- var SETTLING_TTL_MS = 5 * 1e3;
279
- var newestFirst = (keys2) => [...keys2].sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.kid.localeCompare(a.kid));
280
- function published(key) {
281
- const { kty, n, e } = key.publicJwk;
282
- return { kty, n, e, kid: key.kid, alg: "RS256", use: "sig" };
283
- }
284
- function pactKeys(store, sealKeys, now = Date.now) {
285
- let remembered;
286
- let making;
287
- async function make(after) {
288
- const { publicKey, privateKey } = await (0, import_jose2.generateKeyPair)("RS256", { modulusLength: 2048, extractable: true });
289
- const publicJwk = await (0, import_jose2.exportJWK)(publicKey);
290
- const kid = await (0, import_jose2.calculateJwkThumbprint)(publicJwk);
291
- const at = Math.max(now(), after ? Date.parse(after.createdAt) + 1 : 0);
292
- const key = { kid, publicJwk, sealed: await seal(await (0, import_jose2.exportJWK)(privateKey), (await sealKeys)[0]), createdAt: new Date(at).toISOString() };
293
- if (!after && store.addFirst) await store.addFirst(key);
294
- else await store.add(key);
295
- return key;
296
- }
297
- const age = (key) => now() - Date.parse(key.createdAt);
298
- function signer(kept) {
299
- const ripe = kept.find((k) => age(k) >= SIGN_AFTER_MS);
300
- if (!ripe) return { key: kept[kept.length - 1], until: now() + SETTLING_TTL_MS };
301
- const until = ripe === kept[0] ? now() + CURRENT_TTL_MS : Math.min(now() + CURRENT_TTL_MS, Date.parse(kept[0].createdAt) + SIGN_AFTER_MS);
302
- return { key: ripe, until };
303
- }
304
- async function open(key) {
305
- const ring = await sealKeys;
306
- for (let i = 0; i < ring.length; i++) {
307
- const jwk = await unseal(key.sealed, ring[i]);
308
- if (jwk !== null) return { jwk, fresh: i === 0 };
309
- }
310
- return null;
311
- }
312
- async function keptKeys() {
313
- const kept = newestFirst(await store.list());
314
- if (kept.length > 0) return kept;
315
- making ??= make().finally(() => {
316
- making = void 0;
317
- });
318
- await making;
319
- const again = newestFirst(await store.list());
320
- if (again.length === 0) throw new CauthyConfigError("the key store kept no key: check that it writes");
321
- return again;
322
- }
323
- return {
324
- async current() {
325
- if (remembered && remembered.until > now()) return remembered;
326
- const { key, until } = signer(await keptKeys());
327
- if (remembered?.kid === key.kid) {
328
- remembered.until = until;
329
- return remembered;
330
- }
331
- const opened = await open(key);
332
- if (opened === null) {
333
- 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`);
334
- }
335
- remembered = { kid: key.kid, privateKey: await (0, import_jose2.importJWK)(opened.jwk, "RS256"), until };
336
- return remembered;
337
- },
338
- async published() {
339
- const kept = await keptKeys();
340
- const out2 = [];
341
- for (let i = 0; i < kept.length; i++) {
342
- if (i === 0 || now() - Date.parse(kept[i - 1].createdAt) < RETIRE_AFTER_MS) out2.push(published(kept[i]));
343
- else if (now() - Date.parse(kept[i - 1].createdAt) > FORGET_AFTER_MS) await store.remove(kept[i].kid);
344
- }
345
- return { keys: out2 };
346
- },
347
- async rotate() {
348
- const key = await make(newestFirst(await store.list())[0]);
349
- remembered = void 0;
350
- return key.kid;
351
- },
352
- async reseal() {
353
- let changed = 0;
354
- const ring = await sealKeys;
355
- for (const key of await store.list()) {
356
- const opened = await open(key);
357
- if (opened === null || opened.fresh) continue;
358
- await store.update({ ...key, sealed: await seal(opened.jwk, ring[0]) });
359
- changed++;
360
- }
361
- return changed;
362
- }
363
- };
364
- }
365
-
366
- // src/rootkey.ts
367
- var encoder2 = new TextEncoder();
368
- var MIN_ROOT_KEY = 32;
369
- async function derive(root, purpose) {
370
- const ikm = await crypto.subtle.importKey("raw", encoder2.encode(root), "HKDF", false, ["deriveBits"]);
371
- const bits = await crypto.subtle.deriveBits(
372
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder2.encode(`pact/${purpose}/v1`) },
373
- ikm,
374
- 256
375
- );
376
- return new Uint8Array(bits);
377
- }
378
- function deriveKeys(root, previous) {
379
- if (root.length < MIN_ROOT_KEY) {
380
- throw new CauthyConfigError(`rootKey (PACT_ROOT_KEY) must be at least ${MIN_ROOT_KEY} characters: 32 random bytes, e.g. openssl rand -base64 32`);
381
- }
382
- if (previous !== void 0 && previous !== "" && previous.length < MIN_ROOT_KEY) {
383
- throw new CauthyConfigError(`the previous root key (PACT_ROOT_KEY_PREVIOUS) must be at least ${MIN_ROOT_KEY} characters`);
384
- }
385
- const roots = previous && previous !== root ? [root, previous] : [root];
386
- const ring = (purpose) => Promise.all(roots.map((r) => derive(r, purpose)));
387
- return { seal: ring("seal"), cookie: ring("cookie"), flow: ring("flow") };
388
- }
389
-
390
132
  // src/deadline.ts
391
133
  var DEFAULT_DEADLINE_MS = 5e3;
392
134
  var isTimeout = (err) => err?.name === "TimeoutError";
@@ -420,6 +162,13 @@ function anySignal(signals) {
420
162
  return controller.signal;
421
163
  }
422
164
 
165
+ // src/url.ts
166
+ function withoutTrailingSlashes(s) {
167
+ let end = s.length;
168
+ while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
169
+ return s.slice(0, end);
170
+ }
171
+
423
172
  // src/permissions.ts
424
173
  function matches(entry, resource, action) {
425
174
  const i = entry.lastIndexOf(":");
@@ -489,19 +238,19 @@ var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
489
238
  function policySource(system, read) {
490
239
  const roles = Array.isArray(read) ? read : read.roles;
491
240
  const catalog = Array.isArray(read) ? [] : read.catalog ?? [];
492
- const text2 = (v) => JSON.stringify(v);
493
- const key = (k) => IDENT.test(k) ? k : text2(k);
241
+ const text = (v) => JSON.stringify(v);
242
+ const key = (k) => IDENT.test(k) ? k : text(k);
494
243
  const one = (r, pad) => {
495
- const parts = [`role: ${text2(r.role)}`];
496
- if (r.name) parts.push(`name: ${text2(r.name)}`);
497
- if (r.description) parts.push(`description: ${text2(r.description)}`);
498
- if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text2).join(", ")}]`);
244
+ const parts = [`role: ${text(r.role)}`];
245
+ if (r.name) parts.push(`name: ${text(r.name)}`);
246
+ if (r.description) parts.push(`description: ${text(r.description)}`);
247
+ if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text).join(", ")}]`);
499
248
  if (r.permissions?.length) {
500
249
  const list = r.permissions.map((p) => `${pad} { ${[
501
- ...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text2(p.kind)}`] : [],
502
- `resource: ${text2(p.resource)}`,
503
- `action: ${text2(p.action)}`,
504
- ...p.condition ? [`condition: ${text2(p.condition)}`] : []
250
+ ...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text(p.kind)}`] : [],
251
+ `resource: ${text(p.resource)}`,
252
+ `action: ${text(p.action)}`,
253
+ ...p.condition ? [`condition: ${text(p.condition)}`] : []
505
254
  ].join(", ")} },`);
506
255
  parts.push(`permissions: [
507
256
  ${list.join("\n")}
@@ -513,14 +262,14 @@ ${pad} `)},
513
262
  ${pad} },`;
514
263
  };
515
264
  const resource = (c) => {
516
- const parts = [`resource: ${text2(c.resource)}`];
517
- if (c.description) parts.push(`description: ${text2(c.description)}`);
518
- parts.push(`actions: [${c.actions.map(text2).join(", ")}]`);
265
+ const parts = [`resource: ${text(c.resource)}`];
266
+ if (c.description) parts.push(`description: ${text(c.description)}`);
267
+ parts.push(`actions: [${c.actions.map(text).join(", ")}]`);
519
268
  const attrs = Object.entries(c.attributes ?? {});
520
- if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text2(v)}`).join(", ")} }`);
269
+ if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text(v)}`).join(", ")} }`);
521
270
  return ` { ${parts.join(", ")} },`;
522
271
  };
523
- const head = `// The catalog of roles of ${text2(system)}, as Cauthy has it.
272
+ const head = `// The catalog of roles of ${text(system)}, as Cauthy has it.
524
273
  // Written by \`pullPolicy\`: run it again after the catalog
525
274
  // changes, and do not edit this file by hand.
526
275
  `;
@@ -630,7 +379,7 @@ function organizationIdOf(o) {
630
379
  }
631
380
 
632
381
  // src/system.ts
633
- var import_jose3 = require("jose");
382
+ var import_jose2 = require("jose");
634
383
 
635
384
  // src/theme.ts
636
385
  var MAX_RADIUS = 40;
@@ -647,7 +396,7 @@ function withoutComments(css) {
647
396
  }
648
397
  }
649
398
  function declarations(css) {
650
- const text2 = withoutComments(css);
399
+ const text = withoutComments(css);
651
400
  const out2 = [];
652
401
  const within = [];
653
402
  let chunk = "";
@@ -658,7 +407,7 @@ function declarations(css) {
658
407
  if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
659
408
  chunk = "";
660
409
  };
661
- for (const ch of text2) {
410
+ for (const ch of text) {
662
411
  if (quote) {
663
412
  chunk += ch;
664
413
  if (ch === quote) quote = "";
@@ -994,6 +743,22 @@ async function pushTheme(o) {
994
743
  }
995
744
  }
996
745
 
746
+ // src/types.ts
747
+ var CauthyConfigError = class extends Error {
748
+ constructor(message) {
749
+ super(`@cauthyhq/node: ${message}`);
750
+ this.name = "CauthyConfigError";
751
+ }
752
+ };
753
+ var CauthyKeyError = class extends CauthyConfigError {
754
+ constructor(reason, message) {
755
+ super(`CAUTHY_KEY: ${message} (${reason})`);
756
+ this.reason = reason;
757
+ this.name = "CauthyKeyError";
758
+ }
759
+ reason;
760
+ };
761
+
997
762
  // src/system.ts
998
763
  var ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
999
764
  var SKEW_MS = 3e4;
@@ -1033,7 +798,7 @@ function asJwk(k) {
1033
798
  }
1034
799
  async function loadKey(k) {
1035
800
  const jwk = asJwk(k);
1036
- return jwk ? (0, import_jose3.importJWK)(jwk, "RS256") : (0, import_jose3.importPKCS8)(k.privateKey, "RS256", { extractable: true });
801
+ return jwk ? (0, import_jose2.importJWK)(jwk, "RS256") : (0, import_jose2.importPKCS8)(k.privateKey, "RS256", { extractable: true });
1037
802
  }
1038
803
  function createSystem(given = {}) {
1039
804
  const issuer = withoutTrailingSlashes(given.issuer ?? env("CAUTHY_ISSUER") ?? "");
@@ -1060,10 +825,6 @@ function createSystem(given = {}) {
1060
825
  if (given.signer) return;
1061
826
  if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
1062
827
  if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
1063
- if (given.keys) {
1064
- if (!given.keysClientId) throw new CauthyConfigError("the contract gives the system no identity of its own: its link needs a service block");
1065
- return;
1066
- }
1067
828
  if (key ? key.keyId === "" : clientSecret === "") {
1068
829
  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)");
1069
830
  }
@@ -1081,15 +842,15 @@ function createSystem(given = {}) {
1081
842
  let asking;
1082
843
  async function ask() {
1083
844
  configured();
1084
- const self = given.signer ? given.signer.clientId : given.keys ? given.keysClientId : key?.clientId || clientId;
845
+ const self = given.signer ? given.signer.clientId : key?.clientId || clientId;
1085
846
  const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
1086
847
  if (given.signer) {
1087
848
  const s = await given.signer.signer;
1088
849
  form.set("client_assertion_type", ASSERTION_TYPE);
1089
850
  form.set("client_assertion", await s.assertion(self, tokenEndpoint));
1090
- } else if (given.keys || key) {
1091
- const signer = given.keys ? await given.keys.current() : { kid: key.keyId, privateKey: await signingKey() };
1092
- 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);
851
+ } else if (key) {
852
+ const signer = { kid: key.keyId, privateKey: await signingKey() };
853
+ const assertion = await new import_jose2.SignJWT({}).setProtectedHeader({ alg: "RS256", kid: signer.kid }).setIssuer(self).setSubject(self).setAudience(tokenEndpoint).setJti(crypto.randomUUID()).setIssuedAt().setExpirationTime("2m").sign(signer.privateKey);
1093
854
  form.set("client_assertion_type", ASSERTION_TYPE);
1094
855
  form.set("client_assertion", assertion);
1095
856
  } else {
@@ -1197,9 +958,8 @@ function createSystem(given = {}) {
1197
958
  return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
1198
959
  },
1199
960
  async publicJwks() {
1200
- if (given.keys) return given.keys.published();
1201
961
  if (!key) return { keys: [] };
1202
- const { kty, n, e } = asJwk(key) ?? await (0, import_jose3.exportJWK)(await signingKey());
962
+ const { kty, n, e } = asJwk(key) ?? await (0, import_jose2.exportJWK)(await signingKey());
1203
963
  return { keys: [{ kty, n, e, kid: key.keyId, alg: "RS256", use: "sig" }] };
1204
964
  }
1205
965
  };
@@ -1361,7 +1121,7 @@ function firstProblem(...checks) {
1361
1121
  }
1362
1122
 
1363
1123
  // src/webhook.ts
1364
- var import_jose4 = require("jose");
1124
+ var import_jose3 = require("jose");
1365
1125
 
1366
1126
  // src/sha256.ts
1367
1127
  var K = new Uint32Array([
@@ -1533,8 +1293,8 @@ var fail = (reason, message) => {
1533
1293
  throw new CauthyKeyError(reason, message);
1534
1294
  };
1535
1295
  var NOT_A_KEY = "not a Cauthy key (copy it again from the dashboard)";
1536
- function parseKey(text2) {
1537
- const s = text2.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
1296
+ function parseKey(text) {
1297
+ const s = text.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
1538
1298
  if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
1539
1299
  const head = s.split(".", 1)[0];
1540
1300
  if (head !== KEY_PREFIX) {
@@ -1602,7 +1362,7 @@ var keys = /* @__PURE__ */ new Map();
1602
1362
  function keysOf(issuer) {
1603
1363
  let set = keys.get(issuer);
1604
1364
  if (set === void 0) {
1605
- set = (0, import_jose4.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
1365
+ set = (0, import_jose3.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
1606
1366
  keys.set(issuer, set);
1607
1367
  }
1608
1368
  return set;
@@ -1619,7 +1379,7 @@ async function verifyWebhook(request, options = {}) {
1619
1379
  const audience = options.url ?? request.url.split("?")[0];
1620
1380
  let claims;
1621
1381
  try {
1622
- const verified = await (0, import_jose4.jwtVerify)(signature, options.jwks ?? keysOf(issuer), {
1382
+ const verified = await (0, import_jose3.jwtVerify)(signature, options.jwks ?? keysOf(issuer), {
1623
1383
  algorithms: ["RS256"],
1624
1384
  audience,
1625
1385
  typ: "cauthy-webhook+jwt",
@@ -1717,10 +1477,10 @@ async function finish(res, route, cacheable = false) {
1717
1477
  if (!cacheable && !(headers.get("cache-control") ?? "").includes("no-store")) headers.set("cache-control", "no-store");
1718
1478
  let body = res.body;
1719
1479
  if (route !== null && SIGN_IN_ROUTES.has(route) && res.ok && (headers.get("content-type") ?? "").startsWith("application/json")) {
1720
- const text2 = await res.text();
1721
- body = text2;
1480
+ const text = await res.text();
1481
+ body = text;
1722
1482
  try {
1723
- const parsed = JSON.parse(text2);
1483
+ const parsed = JSON.parse(text);
1724
1484
  const next = nextOf(route, parsed?.status);
1725
1485
  if (parsed !== null && next !== void 0) body = JSON.stringify({ ...parsed, next });
1726
1486
  } catch {
@@ -1731,29 +1491,29 @@ async function finish(res, route, cacheable = false) {
1731
1491
  }
1732
1492
 
1733
1493
  // src/verify.ts
1734
- var import_jose5 = require("jose");
1494
+ var import_jose4 = require("jose");
1735
1495
 
1736
1496
  // src/json.ts
1737
1497
  var SPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r"]);
1738
1498
  var LIMIT = 2n ** 53n;
1739
- function parseStrict(text2, integerMembers = []) {
1499
+ function parseStrict(text, integerMembers = []) {
1740
1500
  let i = 0;
1741
1501
  let depth = 0;
1742
1502
  const fail2 = (what) => {
1743
1503
  throw new SyntaxError(`${what} at ${i}`);
1744
1504
  };
1745
1505
  const skip = () => {
1746
- while (i < text2.length && SPACE.has(text2[i])) i++;
1506
+ while (i < text.length && SPACE.has(text[i])) i++;
1747
1507
  };
1748
1508
  const value = (integer = false) => {
1749
1509
  skip();
1750
- const c = text2[i];
1510
+ const c = text[i];
1751
1511
  if (c === "{") return object();
1752
1512
  if (c === "[") return array();
1753
1513
  if (c === '"') return string();
1754
1514
  if (c === "-" || c >= "0" && c <= "9") return number(integer);
1755
1515
  for (const [word, v] of [["true", true], ["false", false], ["null", null]]) {
1756
- if (text2.startsWith(word, i)) {
1516
+ if (text.startsWith(word, i)) {
1757
1517
  i += word.length;
1758
1518
  return v;
1759
1519
  }
@@ -1763,13 +1523,13 @@ function parseStrict(text2, integerMembers = []) {
1763
1523
  const string = () => {
1764
1524
  const start = i;
1765
1525
  i++;
1766
- while (i < text2.length && text2[i] !== '"') i += text2[i] === "\\" ? 2 : 1;
1767
- if (text2[i] !== '"') fail2("unterminated string");
1526
+ while (i < text.length && text[i] !== '"') i += text[i] === "\\" ? 2 : 1;
1527
+ if (text[i] !== '"') fail2("unterminated string");
1768
1528
  i++;
1769
- return JSON.parse(text2.slice(start, i));
1529
+ return JSON.parse(text.slice(start, i));
1770
1530
  };
1771
1531
  const number = (integer) => {
1772
- const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text2.slice(i));
1532
+ const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i));
1773
1533
  if (!m) return fail2("bad number");
1774
1534
  i += m[0].length;
1775
1535
  if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
@@ -1784,7 +1544,7 @@ function parseStrict(text2, integerMembers = []) {
1784
1544
  depth++;
1785
1545
  const out2 = [];
1786
1546
  skip();
1787
- if (text2[i] === "]") {
1547
+ if (text[i] === "]") {
1788
1548
  i++;
1789
1549
  depth--;
1790
1550
  return out2;
@@ -1792,8 +1552,8 @@ function parseStrict(text2, integerMembers = []) {
1792
1552
  for (; ; ) {
1793
1553
  out2.push(value());
1794
1554
  skip();
1795
- if (text2[i] === ",") i++;
1796
- else if (text2[i] === "]") {
1555
+ if (text[i] === ",") i++;
1556
+ else if (text[i] === "]") {
1797
1557
  i++;
1798
1558
  depth--;
1799
1559
  return out2;
@@ -1806,24 +1566,24 @@ function parseStrict(text2, integerMembers = []) {
1806
1566
  const out2 = {};
1807
1567
  const seen = /* @__PURE__ */ new Set();
1808
1568
  skip();
1809
- if (text2[i] === "}") {
1569
+ if (text[i] === "}") {
1810
1570
  i++;
1811
1571
  depth--;
1812
1572
  return out2;
1813
1573
  }
1814
1574
  for (; ; ) {
1815
1575
  skip();
1816
- if (text2[i] !== '"') fail2("expected a name");
1576
+ if (text[i] !== '"') fail2("expected a name");
1817
1577
  const name = string();
1818
1578
  if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
1819
1579
  seen.add(name);
1820
1580
  skip();
1821
- if (text2[i] !== ":") fail2("expected :");
1581
+ if (text[i] !== ":") fail2("expected :");
1822
1582
  i++;
1823
1583
  Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
1824
1584
  skip();
1825
- if (text2[i] === ",") i++;
1826
- else if (text2[i] === "}") {
1585
+ if (text[i] === ",") i++;
1586
+ else if (text[i] === "}") {
1827
1587
  i++;
1828
1588
  depth--;
1829
1589
  return out2;
@@ -1832,7 +1592,7 @@ function parseStrict(text2, integerMembers = []) {
1832
1592
  };
1833
1593
  const result = value();
1834
1594
  skip();
1835
- if (i < text2.length) fail2("trailing characters");
1595
+ if (i < text.length) fail2("trailing characters");
1836
1596
  return result;
1837
1597
  }
1838
1598
 
@@ -1853,6 +1613,15 @@ function deprecated(what, instead) {
1853
1613
  var enc2 = new TextEncoder();
1854
1614
  var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
1855
1615
  var ASSERTION_TYPE2 = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
1616
+ async function ringKey(secret, purpose) {
1617
+ const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
1618
+ return new Uint8Array(await crypto.subtle.deriveBits(
1619
+ { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: enc2.encode(`pact/${purpose}/v1`) },
1620
+ ikm,
1621
+ 256
1622
+ ));
1623
+ }
1624
+ var ringOf = (secrets, purpose) => Promise.all(secrets.map((s) => ringKey(s, purpose)));
1856
1625
  async function seedOf(secret) {
1857
1626
  const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
1858
1627
  return new Uint8Array(await crypto.subtle.deriveBits(
@@ -1890,12 +1659,11 @@ async function signerOf(secret) {
1890
1659
  }
1891
1660
  function deriveFromKey(key, previous) {
1892
1661
  const other = previous && previous.secret !== key.secret ? previous : void 0;
1893
- const rings = deriveKeys(key.secret, other?.secret);
1662
+ const secrets = other ? [key.secret, other.secret] : [key.secret];
1894
1663
  return {
1895
- cookie: rings.cookie,
1896
- flow: rings.flow,
1897
- signer: signerOf(key.secret),
1898
- ...other ? { previousJwk: signerOf(other.secret).then((s) => ({ ...s.publicJwk, kid: s.kid })) } : {}
1664
+ cookie: ringOf(secrets, "cookie"),
1665
+ flow: ringOf(secrets, "flow"),
1666
+ signer: signerOf(key.secret)
1899
1667
  };
1900
1668
  }
1901
1669
 
@@ -2014,8 +1782,8 @@ var CauthyTokenError = class extends Error {
2014
1782
  detail;
2015
1783
  };
2016
1784
  var MAX_TOKEN_BYTES = 8192;
2017
- var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
2018
- var NIL_UUID2 = "00000000-0000-0000-0000-000000000000";
1785
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
1786
+ var NIL_UUID = "00000000-0000-0000-0000-000000000000";
2019
1787
  var SEGMENT = /^[A-Za-z0-9_-]+$/;
2020
1788
  var MAX_KEYS_BYTES = 64 * 1024;
2021
1789
  var KEYS_TIMEOUT_MS = 5e3;
@@ -2043,7 +1811,7 @@ function remoteKeys(issuer, fetchKeys = fetch) {
2043
1811
  if (seen.has(k.kid)) throw new Error(`two usable keys share the kid ${JSON.stringify(k.kid)}`);
2044
1812
  seen.add(k.kid);
2045
1813
  }
2046
- set = (0, import_jose5.createLocalJWKSet)(parsed);
1814
+ set = (0, import_jose4.createLocalJWKSet)(parsed);
2047
1815
  const maxAge = /max-age=(\d+)/.exec(res.headers.get("cache-control") ?? "");
2048
1816
  const ttl = Math.max(KEYS_MIN_TTL_S, Math.min(maxAge ? Number(maxAge[1]) : KEYS_DEFAULT_TTL_S, KEYS_MAX_TTL_S));
2049
1817
  expiresAt = Date.now() + ttl * 1e3;
@@ -2095,7 +1863,7 @@ async function readCapped(res, max) {
2095
1863
  }
2096
1864
  return out2;
2097
1865
  }
2098
- var published2 = /* @__PURE__ */ new Map();
1866
+ var published = /* @__PURE__ */ new Map();
2099
1867
  var withFetch = /* @__PURE__ */ new WeakMap();
2100
1868
  function remoteKeysFor(issuer, fetchKeys) {
2101
1869
  let byIssuer = withFetch.get(fetchKeys);
@@ -2105,10 +1873,10 @@ function remoteKeysFor(issuer, fetchKeys) {
2105
1873
  return set;
2106
1874
  }
2107
1875
  function keysOf2(issuer) {
2108
- let set = published2.get(issuer);
1876
+ let set = published.get(issuer);
2109
1877
  if (set === void 0) {
2110
1878
  set = remoteKeys(issuer);
2111
- published2.set(issuer, set);
1879
+ published.set(issuer, set);
2112
1880
  }
2113
1881
  return set;
2114
1882
  }
@@ -2150,7 +1918,7 @@ function createTokenVerifier(options = {}) {
2150
1918
  if (audience === "") throw new CauthyConfigError("audience (CAUTHY_AUDIENCE) is not set: without it a token issued for another application would be accepted");
2151
1919
  const tenant = options.tenantId ?? env2("CAUTHY_TENANT_ID") ?? "";
2152
1920
  if (tenant === "") throw new CauthyConfigError("tenantId (CAUTHY_TENANT_ID) is not set: without it a token of another workspace would be accepted");
2153
- 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");
1921
+ 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");
2154
1922
  const leeway = options.leeway ?? 30;
2155
1923
  if (!Number.isFinite(leeway) || leeway < 0 || leeway > 60) throw new CauthyConfigError("leeway must be 0 to 60 seconds");
2156
1924
  return async (from) => {
@@ -2174,7 +1942,7 @@ function createTokenVerifier(options = {}) {
2174
1942
  if (typeof header.kid !== "string" || header.kid === "") throw new CauthyTokenError("missing_kid");
2175
1943
  let payloadBytes;
2176
1944
  try {
2177
- const verified = await (0, import_jose5.compactVerify)(token, options.jwks ?? (options.fetch ? remoteKeysFor(issuer, options.fetch) : keysOf2(issuer)), { algorithms: ["RS256"] });
1945
+ const verified = await (0, import_jose4.compactVerify)(token, options.jwks ?? (options.fetch ? remoteKeysFor(issuer, options.fetch) : keysOf2(issuer)), { algorithms: ["RS256"] });
2178
1946
  payloadBytes = verified.payload;
2179
1947
  } catch (err) {
2180
1948
  const code = err?.code;
@@ -2199,8 +1967,8 @@ function createTokenVerifier(options = {}) {
2199
1967
  throw new CauthyTokenError("invalid_claim", name);
2200
1968
  };
2201
1969
  if (typeof claims.iss !== "string") bad("iss");
2202
- if (typeof claims.sub !== "string" || !UUID2.test(claims.sub)) bad("sub");
2203
- if (typeof claims.tid !== "string" || !UUID2.test(claims.tid)) bad("tid");
1970
+ if (typeof claims.sub !== "string" || !UUID.test(claims.sub)) bad("sub");
1971
+ if (typeof claims.tid !== "string" || !UUID.test(claims.tid)) bad("tid");
2204
1972
  const aud = claims.aud;
2205
1973
  if (!(typeof aud === "string" || Array.isArray(aud) && aud.length > 0 && aud.every((a) => typeof a === "string"))) bad("aud");
2206
1974
  for (const name of ["exp", "iat", "nbf"]) if (claims[name] !== void 0 && !Number.isInteger(claims[name])) bad(name);
@@ -2238,13 +2006,13 @@ async function verifyToken(from, options = {}) {
2238
2006
  return createTokenVerifier(options)(from);
2239
2007
  }
2240
2008
  async function createVerifierFromEnv(options = {}) {
2241
- const text2 = options.key ?? env2("CAUTHY_KEY");
2242
- if (text2 === void 0 || text2 === "") return createTokenVerifier(options);
2009
+ const text = options.key ?? env2("CAUTHY_KEY");
2010
+ if (text === void 0 || text === "") return createTokenVerifier(options);
2243
2011
  refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
2244
2012
  for (const name of ["issuer", "audience", "tenantId"]) {
2245
2013
  if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
2246
2014
  }
2247
- const key = parseKey(text2);
2015
+ const key = parseKey(text);
2248
2016
  const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
2249
2017
  const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
2250
2018
  const { key: _, ...rest } = options;
@@ -2342,8 +2110,8 @@ function conditionalOf(c) {
2342
2110
  return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
2343
2111
  }
2344
2112
  function byKey(given) {
2345
- const text2 = given.key ?? env3("CAUTHY_KEY");
2346
- if (text2 === void 0 || text2 === "") return void 0;
2113
+ const text = given.key ?? env3("CAUTHY_KEY");
2114
+ if (text === void 0 || text === "") return void 0;
2347
2115
  refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
2348
2116
  for (const [name, value] of [
2349
2117
  ["issuer", given.issuer],
@@ -2354,12 +2122,11 @@ function byKey(given) {
2354
2122
  ["tenant", given.tenant],
2355
2123
  ["audience", given.audience],
2356
2124
  ["tenantId", given.tenantId],
2357
- ["systemKey", given.systemKey],
2358
- ["rootKey", given.rootKey]
2125
+ ["systemKey", given.systemKey]
2359
2126
  ]) {
2360
2127
  if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
2361
2128
  }
2362
- const key = parseKey(text2);
2129
+ const key = parseKey(text);
2363
2130
  const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
2364
2131
  let previous;
2365
2132
  if (prevText) {
@@ -2374,14 +2141,8 @@ function byKey(given) {
2374
2141
  throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
2375
2142
  }
2376
2143
  const legacySecret = env3("CAUTHY_SECRET") || void 0;
2377
- const legacyRoot = env3("PACT_ROOT_KEY") || void 0;
2378
- const legacyRootPrevious = legacyRoot ? env3("PACT_ROOT_KEY_PREVIOUS") || void 0 : void 0;
2379
2144
  if (legacySecret) deprecated("CAUTHY_SECRET beside CAUTHY_KEY", "It is read only for sessions made before the move: remove it after the rotation window.");
2380
- 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.");
2381
- if (given.keys && !(legacyRoot && given.contract)) {
2382
- 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)");
2383
- }
2384
- return { key, previous, legacySecret, legacyRoot, legacyRootPrevious };
2145
+ return { key, previous, legacySecret };
2385
2146
  }
2386
2147
  var UnknownOriginError = class extends Error {
2387
2148
  constructor(origin, registered) {
@@ -2392,78 +2153,40 @@ var UnknownOriginError = class extends Error {
2392
2153
  origin;
2393
2154
  registered;
2394
2155
  };
2395
- function contractOnKey(given, keyed) {
2396
- const contract = readContract(given.contract);
2397
- if (contract.issuer !== keyed.key.issuer) {
2398
- 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`);
2399
- }
2400
- return contract;
2401
- }
2402
- function byContract(given) {
2403
- failClosed(typeof process !== "undefined" ? process.env : void 0);
2404
- for (const [name, value] of [
2405
- ["clientId", given.clientId],
2406
- ["clientSecret", given.clientSecret],
2407
- ["secret", given.secret],
2408
- ["system", given.system],
2409
- ["tenant", given.tenant],
2410
- ["systemKey", given.systemKey]
2411
- ]) {
2412
- if (value !== void 0) throw new CauthyConfigError(`with a contract, ${name} comes from the contract: do not pass it`);
2413
- }
2414
- const contract = readContract(given.contract);
2415
- const root = given.rootKey ?? env3("PACT_ROOT_KEY") ?? "";
2416
- if (root === "") throw new CauthyConfigError("rootKey (PACT_ROOT_KEY) is required with a contract: 32 random bytes, e.g. openssl rand -base64 32");
2417
- if (contract.serviceClientId && !given.keys) {
2418
- throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
2419
- }
2420
- 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.");
2421
- return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
2422
- }
2423
2156
  var toldOfHeader = false;
2424
2157
  var toldOfUnopened = false;
2425
2158
  function toldUnopened() {
2426
2159
  if (toldOfUnopened) return;
2427
2160
  toldOfUnopened = true;
2428
- 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.");
2161
+ 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.");
2429
2162
  }
2430
2163
  function createCauthy(given = {}) {
2431
2164
  const keyed = byKey(given);
2432
- const pacted = given.contract && !keyed ? byContract(given) : void 0;
2433
- const contract = keyed && given.contract ? contractOnKey(given, keyed) : pacted?.contract;
2434
- const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? contract?.issuer ?? env3("CAUTHY_ISSUER") ?? "");
2435
- const clientId = contract?.clientId ?? keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
2436
- const clientSecret = contract || keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
2437
- const secret = contract || keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
2438
- const askSelf = keyed !== void 0 && contract === void 0;
2439
- const system = askSelf ? "" : contract?.system ?? given.system ?? env3("CAUTHY_SYSTEM") ?? "";
2440
- const tenant = askSelf ? "" : contract?.tenant ?? given.tenant ?? env3("CAUTHY_TENANT") ?? "";
2165
+ const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? env3("CAUTHY_ISSUER") ?? "");
2166
+ const clientId = keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
2167
+ const clientSecret = keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
2168
+ const secret = keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
2169
+ const system = keyed ? "" : given.system ?? env3("CAUTHY_SYSTEM") ?? "";
2170
+ const tenant = keyed ? "" : given.tenant ?? env3("CAUTHY_TENANT") ?? "";
2441
2171
  if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
2442
2172
  if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
2443
- if (!contract && !keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
2173
+ if (!keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
2444
2174
  const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
2445
2175
  let signingKid;
2446
2176
  fromKey?.signer.then((s) => {
2447
2177
  signingKid = s.kid;
2448
2178
  }, () => void 0);
2449
- const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
2450
- const single = derived || fromKey ? void 0 : keyOf(secret).then((k) => [k]);
2451
- const legacyRings = keyed?.legacyRoot ? deriveKeys(keyed.legacyRoot, keyed.legacyRootPrevious) : void 0;
2452
2179
  const legacySingle = keyed?.legacySecret ? keyOf(keyed.legacySecret) : void 0;
2453
- const withLegacy = async (own, purpose) => [
2454
- ...await own,
2455
- ...legacyRings ? await legacyRings[purpose] : [],
2456
- ...legacySingle ? [await legacySingle] : []
2457
- ];
2458
- const cookieKeys = fromKey ? withLegacy(fromKey.cookie, "cookie") : derived?.cookie ?? single;
2459
- const flowKeys = fromKey ? withLegacy(fromKey.flow, "flow") : derived?.flow ?? single;
2460
- if (!keyed && !pacted) {
2180
+ const withLegacy = async (own) => [...await own, ...legacySingle ? [await legacySingle] : []];
2181
+ const single = fromKey ? Promise.resolve([]) : keyOf(secret).then((k) => [k]);
2182
+ const cookieKeys = fromKey ? withLegacy(fromKey.cookie) : single;
2183
+ const flowKeys = fromKey ? withLegacy(fromKey.flow) : single;
2184
+ if (!keyed) {
2461
2185
  deprecated(
2462
2186
  "Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
2463
2187
  "Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
2464
2188
  );
2465
2189
  }
2466
- const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
2467
2190
  const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
2468
2191
  if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
2469
2192
  throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
@@ -2500,21 +2223,10 @@ function createCauthy(given = {}) {
2500
2223
  acceptInviteUrl,
2501
2224
  fetch: given.fetch,
2502
2225
  timeoutMs: systemTimeout,
2503
- ...ownKeys ? { keys: ownKeys, keysClientId: contract?.serviceClientId } : {},
2504
- // With a contract the key proves the link's service client; without, the key's own client.
2505
- ...fromKey ? {
2506
- signer: { clientId: contract ? contract.serviceClientId ?? keyed.key.clientId : clientId, signer: fromKey.signer },
2507
- currentSystem: () => identity?.current()?.system ?? ""
2508
- } : {}
2509
- });
2510
- const selfOptions = () => ({
2511
- issuer,
2512
- token: () => ownSystem.token(),
2513
- fetch: deadline(given.fetch, requestTimeout),
2514
- kid: () => signingKid,
2515
- log: (m) => console.error(`@cauthyhq/node: ${m}`)
2226
+ // On CAUTHY_KEY the key proves the key's own client.
2227
+ ...fromKey ? { signer: { clientId, signer: fromKey.signer }, currentSystem: () => identity?.current()?.system ?? "" } : {}
2516
2228
  });
2517
- const identity = askSelf ? createIdentity({
2229
+ const identity = keyed ? createIdentity({
2518
2230
  issuer,
2519
2231
  token: () => ownSystem.token(),
2520
2232
  fetch: deadline(given.fetch, requestTimeout),
@@ -2524,6 +2236,8 @@ function createCauthy(given = {}) {
2524
2236
  identity?.ready().catch(() => {
2525
2237
  });
2526
2238
  const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
2239
+ const signInClientNow = () => identity?.current()?.sign_in_client_id || clientId;
2240
+ const signInClient = async () => identity ? (await identity.ready()).sign_in_client_id || clientId : clientId;
2527
2241
  const systemNow = () => identity?.current()?.system ?? system;
2528
2242
  const requestOrigin = (request) => {
2529
2243
  const url = new URL(request.url);
@@ -2548,7 +2262,7 @@ function createCauthy(given = {}) {
2548
2262
  };
2549
2263
  const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
2550
2264
  async function clientAuth(form) {
2551
- if (fromKey && !contract) {
2265
+ if (fromKey && form.get("client_id") === clientId) {
2552
2266
  const s = await fromKey.signer;
2553
2267
  form.set("client_assertion_type", ASSERTION_TYPE2);
2554
2268
  form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
@@ -2558,8 +2272,8 @@ function createCauthy(given = {}) {
2558
2272
  return form;
2559
2273
  }
2560
2274
  async function token(form, proof = {}) {
2561
- const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
2562
2275
  try {
2276
+ const body2 = await clientAuth(new URLSearchParams({ client_id: await signInClient(), ...form }));
2563
2277
  const res = await doFetch(`${issuer}/v1/oauth/token`, {
2564
2278
  method: "POST",
2565
2279
  headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json", ...proof },
@@ -2597,18 +2311,16 @@ function createCauthy(given = {}) {
2597
2311
  }
2598
2312
  const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
2599
2313
  async function revoke(refreshToken) {
2600
- const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: refreshToken }));
2601
- await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
2314
+ try {
2315
+ const form = await clientAuth(new URLSearchParams({ client_id: await signInClient(), token: refreshToken }));
2316
+ await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
2317
+ } catch {
2318
+ }
2602
2319
  }
2603
- const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
2604
2320
  async function stored(body2, was) {
2605
2321
  if (typeof body2?.access_token !== "string" || typeof body2?.refresh_token !== "string") return null;
2606
2322
  const who = await userinfo(body2.access_token);
2607
2323
  if (who === null || who.u.id === "") return null;
2608
- if (unverified(who.u)) {
2609
- await revoke(body2.refresh_token);
2610
- throw new CauthyApiError(403, "email_not_confirmed", "Confirm your email address first.");
2611
- }
2612
2324
  const lifetime = typeof body2.expires_in === "number" && body2.expires_in > 0 ? body2.expires_in : 300;
2613
2325
  return { at: body2.access_token, rt: body2.refresh_token, exp: Math.floor(Date.now() / 1e3) + lifetime, ...who, ...kindOf(body2, was) };
2614
2326
  }
@@ -2636,13 +2348,7 @@ function createCauthy(given = {}) {
2636
2348
  running = (async () => {
2637
2349
  const res = await token({ grant_type: "refresh_token", refresh_token: rt });
2638
2350
  if (!res.ok) return res.ended ? "ended" : "unreachable";
2639
- let next;
2640
- try {
2641
- next = await stored(res.body, was);
2642
- } catch (err) {
2643
- if (err instanceof CauthyApiError && err.code === "email_not_confirmed") return "ended";
2644
- throw err;
2645
- }
2351
+ const next = await stored(res.body, was);
2646
2352
  if (next !== null) return next;
2647
2353
  if (typeof res.body?.access_token !== "string" || typeof res.body?.refresh_token !== "string") return "unreachable";
2648
2354
  const lifetime = typeof res.body.expires_in === "number" && res.body.expires_in > 0 ? res.body.expires_in : 300;
@@ -2664,7 +2370,6 @@ function createCauthy(given = {}) {
2664
2370
  if (present) toldUnopened();
2665
2371
  return { session: null, status: "unauthenticated", cookies: present ? clearParts(cookieName, attrs(request, 0), had) : [] };
2666
2372
  }
2667
- if (unverified(value.u)) return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
2668
2373
  if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: stale ? await write(request, value, had) : [] };
2669
2374
  const renewed = await renew(value);
2670
2375
  if (renewed === "ended") {
@@ -2676,7 +2381,7 @@ function createCauthy(given = {}) {
2676
2381
  async function getSession(request, responseHeaders) {
2677
2382
  if (responseHeaders === void 0) {
2678
2383
  const { value } = await read(request);
2679
- return value !== null && fresh(value) && !unverified(value.u) ? toSession(value) : null;
2384
+ return value !== null && fresh(value) ? toSession(value) : null;
2680
2385
  }
2681
2386
  const r = await resolve2(request);
2682
2387
  for (const c of r.cookies) responseHeaders.append("set-cookie", c);
@@ -2684,7 +2389,7 @@ function createCauthy(given = {}) {
2684
2389
  }
2685
2390
  async function status(request) {
2686
2391
  const { value } = await read(request);
2687
- if (value === null || unverified(value.u)) return "unauthenticated";
2392
+ if (value === null) return "unauthenticated";
2688
2393
  return fresh(value) ? "authenticated" : "expired";
2689
2394
  }
2690
2395
  const orgCache = /* @__PURE__ */ new Map();
@@ -2771,7 +2476,7 @@ function createCauthy(given = {}) {
2771
2476
  const flow = { state: random(), verifier: random(48), returnTo: localPath(url.searchParams.get("return_to"), afterSignIn) };
2772
2477
  const challenge = b64url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(flow.verifier))));
2773
2478
  const q = new URLSearchParams({
2774
- client_id: clientId,
2479
+ client_id: await signInClient(),
2775
2480
  redirect_uri: `${originOf(request)}${basePath}/callback`,
2776
2481
  response_type: "code",
2777
2482
  state: flow.state,
@@ -2833,28 +2538,17 @@ function createCauthy(given = {}) {
2833
2538
  );
2834
2539
  };
2835
2540
  if (!res.ok && res.code === "email_not_confirmed") return unconfirmed();
2836
- let s;
2837
- try {
2838
- s = res.ok ? await stored(res.body) : null;
2839
- } catch (err) {
2840
- if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
2841
- return unconfirmed();
2842
- }
2541
+ const s = res.ok ? await stored(res.body) : null;
2843
2542
  if (s === null) return failed(request, "exchange_failed");
2844
2543
  return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0)), writeCookie(againCookie, "", attrs(request, 0))]);
2845
2544
  }
2846
2545
  async function logout(request) {
2847
2546
  const { had, value } = await read(request);
2848
2547
  if (value !== null) {
2849
- const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
2850
- await doFetch(`${issuer}/v1/oauth/revoke`, {
2851
- method: "POST",
2852
- headers: { "content-type": "application/x-www-form-urlencoded" },
2853
- body: body2
2854
- }).catch(() => void 0);
2548
+ await revoke(value.rt);
2855
2549
  orgCache.delete(value.at);
2856
2550
  }
2857
- const q = new URLSearchParams({ client_id: clientId, post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2551
+ const q = new URLSearchParams({ client_id: signInClientNow(), post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2858
2552
  return redirect(`${issuer}/v1/oauth/logout?${q}`, clearParts(cookieName, attrs(request, 0), had));
2859
2553
  }
2860
2554
  const STEP_SECONDS = 600;
@@ -2872,7 +2566,7 @@ function createCauthy(given = {}) {
2872
2566
  createdAt: String(d?.created_at ?? ""),
2873
2567
  lastUsedAt: d?.last_used_at ? String(d.last_used_at) : null
2874
2568
  });
2875
- const UUID3 = /^[0-9a-f-]{36}$/i;
2569
+ const UUID2 = /^[0-9a-f-]{36}$/i;
2876
2570
  const seatOf = (d) => ({
2877
2571
  id: String(d?.id ?? ""),
2878
2572
  system: String(d?.system ?? ""),
@@ -2977,10 +2671,10 @@ function createCauthy(given = {}) {
2977
2671
  ...page ? { "x-cauthy-link-to": `${originOf(request)}${page}` } : { "x-cauthy-links": "hosted" },
2978
2672
  // Whose request it is: what Cauthy mails goes in this application's
2979
2673
  // name, under its logo.
2980
- ...clientId ? { "x-cauthy-client-id": clientId } : {},
2674
+ ...signInClientNow() ? { "x-cauthy-client-id": signInClientNow() } : {},
2981
2675
  ...await passedOn(request),
2982
2676
  ...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
2983
- // The person asked to stay signed in: Cauthy keeps the session long if the client allows it.
2677
+ // The person asked to stay signed in: Cauthy keeps the session long if the workspace allows it.
2984
2678
  ...remember ? { "x-cauthy-remember": "1" } : {},
2985
2679
  // The answer to the workspace's Turnstile challenge, from the
2986
2680
  // person's browser: Cauthy checks it at the doors that ask for it.
@@ -3121,7 +2815,7 @@ function createCauthy(given = {}) {
3121
2815
  }
3122
2816
  if (route === "/look" && !post) {
3123
2817
  if (lookCache === null || lookCache.until < Date.now()) {
3124
- const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(clientId)}`);
2818
+ const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(signInClientNow())}`);
3125
2819
  lookCache = { until: Date.now() + LOOK_TTL_MS, look: res.error ? {} : res.data ?? {} };
3126
2820
  }
3127
2821
  return json(lookCache.look);
@@ -3146,7 +2840,7 @@ function createCauthy(given = {}) {
3146
2840
  signUp: settings.data?.allow_signup !== false && settings.data?.signup?.mode !== "invite_only",
3147
2841
  // The application's system makes an organization per sign-up: the form asks the company's name.
3148
2842
  ...settings.data?.signup?.organization?.ask === true ? { organization: { ask: true, required: settings.data.signup.organization.required === true } } : {},
3149
- // Whether the person may ask to stay signed in: the client's choice in Cauthy.
2843
+ // Whether the person may ask to stay signed in: the workspace's setting in Cauthy (persistent_sessions).
3150
2844
  ...typeof settings.data?.persistent_sessions === "boolean" ? { persistentSessions: settings.data.persistent_sessions } : {},
3151
2845
  // A sign-in link needs a page of the application to open.
3152
2846
  magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
@@ -3310,7 +3004,7 @@ function createCauthy(given = {}) {
3310
3004
  let path = "/v1/auth/me/organizations";
3311
3005
  if (route !== "/me/organizations") {
3312
3006
  const id = url.searchParams.get("id") ?? "";
3313
- if (!UUID3.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3007
+ if (!UUID2.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3314
3008
  path += `/${id}/${route.endsWith("/roles") ? "roles" : "members"}`;
3315
3009
  } else if (url.searchParams.get("system")) {
3316
3010
  path += `?system=${encodeURIComponent(url.searchParams.get("system"))}`;
@@ -3371,8 +3065,7 @@ function createCauthy(given = {}) {
3371
3065
  if (route === "/sign-out") {
3372
3066
  const { had, value } = await read(request);
3373
3067
  if (value !== null) {
3374
- const form = await clientAuth(new URLSearchParams({ client_id: clientId, token: value.rt }));
3375
- await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
3068
+ await revoke(value.rt);
3376
3069
  orgCache.delete(value.at);
3377
3070
  }
3378
3071
  const h = new Headers();
@@ -3599,7 +3292,7 @@ function createCauthy(given = {}) {
3599
3292
  }
3600
3293
  if (route === "/me/organizations/rename" || route === "/me/organizations/archive" || route === "/me/organizations/delete") {
3601
3294
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3602
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3295
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3603
3296
  const path = `/v1/auth/me/organizations/${org}`;
3604
3297
  if (route === "/me/organizations/rename") {
3605
3298
  const bad = checkOrganizationName(b.name);
@@ -3616,7 +3309,7 @@ function createCauthy(given = {}) {
3616
3309
  }
3617
3310
  if (route.startsWith("/me/organizations/members/")) {
3618
3311
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3619
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3312
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3620
3313
  const base = `/v1/auth/me/organizations/${org}/members`;
3621
3314
  if (route === "/me/organizations/members/add") {
3622
3315
  const bad = checkEmail(b.email);
@@ -3627,7 +3320,7 @@ function createCauthy(given = {}) {
3627
3320
  email: String(b.email).trim(),
3628
3321
  role,
3629
3322
  name: typeof b.name === "string" ? b.name.trim() : "",
3630
- client_id: clientId,
3323
+ client_id: signInClientNow(),
3631
3324
  only_new: true,
3632
3325
  ...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {}
3633
3326
  }, pages.acceptInvite, mine.token);
@@ -3636,7 +3329,7 @@ function createCauthy(given = {}) {
3636
3329
  return json({ status: "added", member: orgMemberOf(res.data) }, 200, mine.headers);
3637
3330
  }
3638
3331
  const user = typeof b.userId === "string" ? b.userId : "";
3639
- if (!UUID3.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
3332
+ if (!UUID2.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
3640
3333
  if (route === "/me/organizations/members/role") {
3641
3334
  const role = typeof b.role === "string" ? b.role : "";
3642
3335
  if (role === "") return problem(400, "invalid_role", "Choose a role.", "role", mine.headers);
@@ -3651,7 +3344,7 @@ function createCauthy(given = {}) {
3651
3344
  return json({ status: "removed", userId: user }, 200, mine.headers);
3652
3345
  }
3653
3346
  if (route === "/me/organizations/members/invite") {
3654
- 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);
3347
+ 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);
3655
3348
  if (res.error) return refused(res);
3656
3349
  return json({ status: "sent", userId: user }, 200, mine.headers);
3657
3350
  }
@@ -3769,18 +3462,15 @@ function createCauthy(given = {}) {
3769
3462
  const url = new URL(request.url);
3770
3463
  const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
3771
3464
  const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
3772
- const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
3773
3465
  if (identity && route !== null) {
3774
3466
  const unready = await identityGate();
3775
3467
  if (unready) return finish(unready, route);
3776
3468
  }
3777
3469
  try {
3778
- return finish(await serve(request), route, published3);
3470
+ return finish(await serve(request), route);
3779
3471
  } catch (err) {
3780
3472
  if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
3781
- if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
3782
- const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
3783
- 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);
3473
+ throw err;
3784
3474
  }
3785
3475
  }
3786
3476
  async function identityGate() {
@@ -3804,7 +3494,6 @@ function createCauthy(given = {}) {
3804
3494
  }
3805
3495
  async function serve(request) {
3806
3496
  const url = new URL(request.url);
3807
- if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
3808
3497
  if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return new Response("Not found", { status: 404 });
3809
3498
  const route = withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/";
3810
3499
  const get = request.method === "GET" || request.method === "HEAD";
@@ -3828,7 +3517,7 @@ function createCauthy(given = {}) {
3828
3517
  return redirect(back, r.cookies);
3829
3518
  }
3830
3519
  if (route === "/account" && get) {
3831
- const q = new URLSearchParams({ client_id: clientId });
3520
+ const q = new URLSearchParams({ client_id: signInClientNow() });
3832
3521
  const back = localPath(url.searchParams.get("return_to"), "");
3833
3522
  if (back !== "") q.set("return_to", `${originOf(request)}${back}`);
3834
3523
  const locale = url.searchParams.get("locale");
@@ -3847,7 +3536,7 @@ function createCauthy(given = {}) {
3847
3536
  const asker = await signedInAs(request);
3848
3537
  if ("response" in asker) return asker.response;
3849
3538
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
3850
- if (!UUID3.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
3539
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
3851
3540
  const named = (v) => typeof v === "string" && v !== "" && v.length <= 200;
3852
3541
  if (!named(b.resource) || !named(b.action)) return problem(400, "bad_request", "Say which resource and action.", void 0, asker.headers);
3853
3542
  const attributes2 = b.attributes;
@@ -3870,8 +3559,7 @@ function createCauthy(given = {}) {
3870
3559
  }
3871
3560
  }
3872
3561
  async function protect(request) {
3873
- const read1 = await read(request);
3874
- const value = read1.value !== null && unverified(read1.value.u) ? null : read1.value;
3562
+ const { value } = await read(request);
3875
3563
  if (value !== null && fresh(value)) return { session: toSession(value) };
3876
3564
  const url = new URL(request.url);
3877
3565
  const state = value === null ? "none" : "expired";
@@ -3884,85 +3572,36 @@ function createCauthy(given = {}) {
3884
3572
  return { response: redirect(to) };
3885
3573
  }
3886
3574
  const replay = given.webhookReplay ?? memoryReplayStore();
3887
- let lateSelf;
3888
3575
  const tokenVerifier = (o = {}) => {
3889
- if (keyed) {
3890
- if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
3891
- throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
3892
- }
3576
+ if (identity) {
3893
3577
  let built;
3894
3578
  return async (from) => {
3895
- const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
3579
+ const id = await identity.ready();
3896
3580
  if (built?.from !== id) {
3897
- built = { from: id, verify: createTokenVerifier({
3898
- issuer,
3899
- tenantId: contract?.tenantId ?? id.tenant.id,
3900
- audience: id.audience,
3901
- fetch: given.fetch,
3902
- ...contract ? { clientId: contract.clientId } : {},
3903
- ...o
3904
- }) };
3581
+ built = { from: id, verify: createTokenVerifier({ issuer, tenantId: id.tenant.id, audience: id.audience, fetch: given.fetch, ...o }) };
3905
3582
  }
3906
3583
  return built.verify(from);
3907
3584
  };
3908
3585
  }
3909
- if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
3910
- throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
3911
- }
3912
3586
  return createTokenVerifier({
3913
3587
  issuer,
3914
- tenantId: contract?.tenantId ?? given.tenantId,
3588
+ tenantId: given.tenantId,
3915
3589
  audience: given.audience,
3916
3590
  fetch: given.fetch,
3917
- ...contract ? { clientId: contract.clientId } : {},
3918
3591
  ...o
3919
3592
  });
3920
3593
  };
3921
3594
  const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now, replay });
3922
- async function keyJwks() {
3923
- const s = await fromKey.signer;
3924
- const keys2 = [{ ...s.publicJwk, kid: s.kid, alg: "EdDSA", use: "sig" }];
3925
- if (fromKey.previousJwk) keys2.push({ ...await fromKey.previousJwk, alg: "EdDSA", use: "sig" });
3926
- if (ownKeys) keys2.push(...(await ownKeys.published()).keys);
3927
- return new Response(JSON.stringify({ keys: keys2 }), {
3928
- headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
3929
- });
3930
- }
3931
- const noStore = () => {
3932
- 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");
3933
- };
3934
- const keyOnly = {
3935
- current: async () => noStore(),
3936
- published: async () => ({ keys: [] }),
3937
- rotate: async () => noStore(),
3938
- reseal: async () => 0
3939
- };
3940
- const pact = contract && fromKey ? {
3941
- contract,
3942
- keys: ownKeys ?? keyOnly,
3943
- jwks: keyJwks,
3944
- status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
3945
- } : contract && ownKeys ? {
3946
- contract,
3947
- keys: ownKeys,
3948
- async jwks() {
3949
- return new Response(JSON.stringify(await ownKeys.published()), {
3950
- headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
3951
- });
3952
- },
3953
- status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
3954
- } : void 0;
3955
3595
  const configured = () => ({
3956
3596
  issuer,
3957
3597
  audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
3958
- tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3598
+ tenant: { id: given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3959
3599
  system,
3960
3600
  client_id: clientId,
3961
3601
  sign_in_client_id: clientId,
3962
3602
  app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
3963
3603
  callback_path: `${basePath}/callback`,
3964
- key: null,
3965
- contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
3604
+ key: null
3966
3605
  });
3967
3606
  return {
3968
3607
  handler,
@@ -3984,7 +3623,6 @@ function createCauthy(given = {}) {
3984
3623
  check,
3985
3624
  toPublic,
3986
3625
  urls,
3987
- ...pact ? { pact } : {},
3988
3626
  options: { issuer, clientId, basePath, afterSignIn, afterSignOut, cookieName }
3989
3627
  };
3990
3628
  }
@@ -4069,7 +3707,6 @@ async function connectSystem(o) {
4069
3707
  KEY_REPLACES,
4070
3708
  MIN_PASSWORD,
4071
3709
  PROTOCOL_VERSION,
4072
- STANDARD_VARIABLES,
4073
3710
  can,
4074
3711
  checkCode,
4075
3712
  checkEmail,
@@ -4085,7 +3722,6 @@ async function connectSystem(o) {
4085
3722
  definePolicy,
4086
3723
  describePasswordPolicy,
4087
3724
  explainPasswordProblems,
4088
- failClosed,
4089
3725
  memoryReplayStore,
4090
3726
  parseKey,
4091
3727
  passwordPolicyOf,
@@ -4096,7 +3732,6 @@ async function connectSystem(o) {
4096
3732
  pushCatalog,
4097
3733
  pushPolicy,
4098
3734
  pushTheme,
4099
- readContract,
4100
3735
  remoteKeys,
4101
3736
  themeFromStackmyth,
4102
3737
  tokensFromStackmyth,