@cauthyhq/node 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +13 -27
  3. package/dist/{cauthy-CCSSj3Lg.d.mts → cauthy-CsWUgyv_.d.mts} +42 -145
  4. package/dist/{cauthy-C7gZ-pbv.d.ts → cauthy-DHLBEuqP.d.ts} +42 -145
  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 +169 -524
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +162 -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 +52 -50
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.mjs +52 -50
  38. package/dist/testing.mjs.map +1 -1
  39. package/package.json +4 -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;
@@ -2341,9 +2109,18 @@ function redirect(location, cookies = []) {
2341
2109
  function conditionalOf(c) {
2342
2110
  return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
2343
2111
  }
2112
+ function refuseContracts(given) {
2113
+ const options = given;
2114
+ const set = [
2115
+ ...["contract", "keys", "rootKey", "rootKeyPrevious"].filter((name) => options[name] !== void 0).map((name) => `the ${name} option`),
2116
+ ...["PACT_ROOT_KEY", "PACT_ROOT_KEY_PREVIOUS"].filter((name) => (env3(name) ?? "") !== "")
2117
+ ];
2118
+ if (set.length === 0) return;
2119
+ throw new CauthyConfigError(`contracts are retired: run on CAUTHY_KEY alone, and remove ${set.join(", ")}. An application without a key makes it on its card in Cauthy's dashboard; the same key keeps its people signed in. Protect sign-in with the "Protect sign-in" switches on that card. Sessions sealed with PACT_ROOT_KEY are no longer read.`);
2120
+ }
2344
2121
  function byKey(given) {
2345
- const text2 = given.key ?? env3("CAUTHY_KEY");
2346
- if (text2 === void 0 || text2 === "") return void 0;
2122
+ const text = given.key ?? env3("CAUTHY_KEY");
2123
+ if (text === void 0 || text === "") return void 0;
2347
2124
  refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
2348
2125
  for (const [name, value] of [
2349
2126
  ["issuer", given.issuer],
@@ -2354,12 +2131,11 @@ function byKey(given) {
2354
2131
  ["tenant", given.tenant],
2355
2132
  ["audience", given.audience],
2356
2133
  ["tenantId", given.tenantId],
2357
- ["systemKey", given.systemKey],
2358
- ["rootKey", given.rootKey]
2134
+ ["systemKey", given.systemKey]
2359
2135
  ]) {
2360
2136
  if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
2361
2137
  }
2362
- const key = parseKey(text2);
2138
+ const key = parseKey(text);
2363
2139
  const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
2364
2140
  let previous;
2365
2141
  if (prevText) {
@@ -2374,14 +2150,8 @@ function byKey(given) {
2374
2150
  throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
2375
2151
  }
2376
2152
  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
2153
  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 };
2154
+ return { key, previous, legacySecret };
2385
2155
  }
2386
2156
  var UnknownOriginError = class extends Error {
2387
2157
  constructor(origin, registered) {
@@ -2392,78 +2162,41 @@ var UnknownOriginError = class extends Error {
2392
2162
  origin;
2393
2163
  registered;
2394
2164
  };
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
2165
  var toldOfHeader = false;
2424
2166
  var toldOfUnopened = false;
2425
2167
  function toldUnopened() {
2426
2168
  if (toldOfUnopened) return;
2427
2169
  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.");
2170
+ 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
2171
  }
2430
2172
  function createCauthy(given = {}) {
2173
+ refuseContracts(given);
2431
2174
  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") ?? "";
2175
+ const issuer = withoutTrailingSlashes(keyed?.key.issuer ?? given.issuer ?? env3("CAUTHY_ISSUER") ?? "");
2176
+ const clientId = keyed?.key.clientId ?? given.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
2177
+ const clientSecret = keyed ? void 0 : given.clientSecret ?? env3("CAUTHY_CLIENT_SECRET");
2178
+ const secret = keyed ? "" : given.secret ?? env3("CAUTHY_SECRET") ?? "";
2179
+ const system = keyed ? "" : given.system ?? env3("CAUTHY_SYSTEM") ?? "";
2180
+ const tenant = keyed ? "" : given.tenant ?? env3("CAUTHY_TENANT") ?? "";
2441
2181
  if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
2442
2182
  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");
2183
+ if (!keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
2444
2184
  const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
2445
2185
  let signingKid;
2446
2186
  fromKey?.signer.then((s) => {
2447
2187
  signingKid = s.kid;
2448
2188
  }, () => 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
2189
  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) {
2190
+ const withLegacy = async (own) => [...await own, ...legacySingle ? [await legacySingle] : []];
2191
+ const single = fromKey ? Promise.resolve([]) : keyOf(secret).then((k) => [k]);
2192
+ const cookieKeys = fromKey ? withLegacy(fromKey.cookie) : single;
2193
+ const flowKeys = fromKey ? withLegacy(fromKey.flow) : single;
2194
+ if (!keyed) {
2461
2195
  deprecated(
2462
2196
  "Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
2463
2197
  "Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
2464
2198
  );
2465
2199
  }
2466
- const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
2467
2200
  const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
2468
2201
  if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
2469
2202
  throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
@@ -2500,21 +2233,10 @@ function createCauthy(given = {}) {
2500
2233
  acceptInviteUrl,
2501
2234
  fetch: given.fetch,
2502
2235
  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}`)
2236
+ // On CAUTHY_KEY the key proves the key's own client.
2237
+ ...fromKey ? { signer: { clientId, signer: fromKey.signer }, currentSystem: () => identity?.current()?.system ?? "" } : {}
2516
2238
  });
2517
- const identity = askSelf ? createIdentity({
2239
+ const identity = keyed ? createIdentity({
2518
2240
  issuer,
2519
2241
  token: () => ownSystem.token(),
2520
2242
  fetch: deadline(given.fetch, requestTimeout),
@@ -2524,6 +2246,8 @@ function createCauthy(given = {}) {
2524
2246
  identity?.ready().catch(() => {
2525
2247
  });
2526
2248
  const tenantNow = () => identity?.current()?.tenant.slug ?? tenant;
2249
+ const signInClientNow = () => identity?.current()?.sign_in_client_id || clientId;
2250
+ const signInClient = async () => identity ? (await identity.ready()).sign_in_client_id || clientId : clientId;
2527
2251
  const systemNow = () => identity?.current()?.system ?? system;
2528
2252
  const requestOrigin = (request) => {
2529
2253
  const url = new URL(request.url);
@@ -2548,7 +2272,7 @@ function createCauthy(given = {}) {
2548
2272
  };
2549
2273
  const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
2550
2274
  async function clientAuth(form) {
2551
- if (fromKey && !contract) {
2275
+ if (fromKey && form.get("client_id") === clientId) {
2552
2276
  const s = await fromKey.signer;
2553
2277
  form.set("client_assertion_type", ASSERTION_TYPE2);
2554
2278
  form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
@@ -2558,8 +2282,8 @@ function createCauthy(given = {}) {
2558
2282
  return form;
2559
2283
  }
2560
2284
  async function token(form, proof = {}) {
2561
- const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
2562
2285
  try {
2286
+ const body2 = await clientAuth(new URLSearchParams({ client_id: await signInClient(), ...form }));
2563
2287
  const res = await doFetch(`${issuer}/v1/oauth/token`, {
2564
2288
  method: "POST",
2565
2289
  headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json", ...proof },
@@ -2597,18 +2321,16 @@ function createCauthy(given = {}) {
2597
2321
  }
2598
2322
  const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
2599
2323
  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);
2324
+ try {
2325
+ const form = await clientAuth(new URLSearchParams({ client_id: await signInClient(), token: refreshToken }));
2326
+ await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
2327
+ } catch {
2328
+ }
2602
2329
  }
2603
- const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
2604
2330
  async function stored(body2, was) {
2605
2331
  if (typeof body2?.access_token !== "string" || typeof body2?.refresh_token !== "string") return null;
2606
2332
  const who = await userinfo(body2.access_token);
2607
2333
  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
2334
  const lifetime = typeof body2.expires_in === "number" && body2.expires_in > 0 ? body2.expires_in : 300;
2613
2335
  return { at: body2.access_token, rt: body2.refresh_token, exp: Math.floor(Date.now() / 1e3) + lifetime, ...who, ...kindOf(body2, was) };
2614
2336
  }
@@ -2636,13 +2358,7 @@ function createCauthy(given = {}) {
2636
2358
  running = (async () => {
2637
2359
  const res = await token({ grant_type: "refresh_token", refresh_token: rt });
2638
2360
  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
- }
2361
+ const next = await stored(res.body, was);
2646
2362
  if (next !== null) return next;
2647
2363
  if (typeof res.body?.access_token !== "string" || typeof res.body?.refresh_token !== "string") return "unreachable";
2648
2364
  const lifetime = typeof res.body.expires_in === "number" && res.body.expires_in > 0 ? res.body.expires_in : 300;
@@ -2664,7 +2380,6 @@ function createCauthy(given = {}) {
2664
2380
  if (present) toldUnopened();
2665
2381
  return { session: null, status: "unauthenticated", cookies: present ? clearParts(cookieName, attrs(request, 0), had) : [] };
2666
2382
  }
2667
- if (unverified(value.u)) return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
2668
2383
  if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: stale ? await write(request, value, had) : [] };
2669
2384
  const renewed = await renew(value);
2670
2385
  if (renewed === "ended") {
@@ -2676,7 +2391,7 @@ function createCauthy(given = {}) {
2676
2391
  async function getSession(request, responseHeaders) {
2677
2392
  if (responseHeaders === void 0) {
2678
2393
  const { value } = await read(request);
2679
- return value !== null && fresh(value) && !unverified(value.u) ? toSession(value) : null;
2394
+ return value !== null && fresh(value) ? toSession(value) : null;
2680
2395
  }
2681
2396
  const r = await resolve2(request);
2682
2397
  for (const c of r.cookies) responseHeaders.append("set-cookie", c);
@@ -2684,7 +2399,7 @@ function createCauthy(given = {}) {
2684
2399
  }
2685
2400
  async function status(request) {
2686
2401
  const { value } = await read(request);
2687
- if (value === null || unverified(value.u)) return "unauthenticated";
2402
+ if (value === null) return "unauthenticated";
2688
2403
  return fresh(value) ? "authenticated" : "expired";
2689
2404
  }
2690
2405
  const orgCache = /* @__PURE__ */ new Map();
@@ -2771,7 +2486,7 @@ function createCauthy(given = {}) {
2771
2486
  const flow = { state: random(), verifier: random(48), returnTo: localPath(url.searchParams.get("return_to"), afterSignIn) };
2772
2487
  const challenge = b64url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(flow.verifier))));
2773
2488
  const q = new URLSearchParams({
2774
- client_id: clientId,
2489
+ client_id: await signInClient(),
2775
2490
  redirect_uri: `${originOf(request)}${basePath}/callback`,
2776
2491
  response_type: "code",
2777
2492
  state: flow.state,
@@ -2833,28 +2548,17 @@ function createCauthy(given = {}) {
2833
2548
  );
2834
2549
  };
2835
2550
  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
- }
2551
+ const s = res.ok ? await stored(res.body) : null;
2843
2552
  if (s === null) return failed(request, "exchange_failed");
2844
2553
  return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0)), writeCookie(againCookie, "", attrs(request, 0))]);
2845
2554
  }
2846
2555
  async function logout(request) {
2847
2556
  const { had, value } = await read(request);
2848
2557
  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);
2558
+ await revoke(value.rt);
2855
2559
  orgCache.delete(value.at);
2856
2560
  }
2857
- const q = new URLSearchParams({ client_id: clientId, post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2561
+ const q = new URLSearchParams({ client_id: signInClientNow(), post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
2858
2562
  return redirect(`${issuer}/v1/oauth/logout?${q}`, clearParts(cookieName, attrs(request, 0), had));
2859
2563
  }
2860
2564
  const STEP_SECONDS = 600;
@@ -2872,7 +2576,7 @@ function createCauthy(given = {}) {
2872
2576
  createdAt: String(d?.created_at ?? ""),
2873
2577
  lastUsedAt: d?.last_used_at ? String(d.last_used_at) : null
2874
2578
  });
2875
- const UUID3 = /^[0-9a-f-]{36}$/i;
2579
+ const UUID2 = /^[0-9a-f-]{36}$/i;
2876
2580
  const seatOf = (d) => ({
2877
2581
  id: String(d?.id ?? ""),
2878
2582
  system: String(d?.system ?? ""),
@@ -2977,10 +2681,10 @@ function createCauthy(given = {}) {
2977
2681
  ...page ? { "x-cauthy-link-to": `${originOf(request)}${page}` } : { "x-cauthy-links": "hosted" },
2978
2682
  // Whose request it is: what Cauthy mails goes in this application's
2979
2683
  // name, under its logo.
2980
- ...clientId ? { "x-cauthy-client-id": clientId } : {},
2684
+ ...signInClientNow() ? { "x-cauthy-client-id": signInClientNow() } : {},
2981
2685
  ...await passedOn(request),
2982
2686
  ...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
2983
- // The person asked to stay signed in: Cauthy keeps the session long if the client allows it.
2687
+ // The person asked to stay signed in: Cauthy keeps the session long if the workspace allows it.
2984
2688
  ...remember ? { "x-cauthy-remember": "1" } : {},
2985
2689
  // The answer to the workspace's Turnstile challenge, from the
2986
2690
  // person's browser: Cauthy checks it at the doors that ask for it.
@@ -3121,7 +2825,7 @@ function createCauthy(given = {}) {
3121
2825
  }
3122
2826
  if (route === "/look" && !post) {
3123
2827
  if (lookCache === null || lookCache.until < Date.now()) {
3124
- const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(clientId)}`);
2828
+ const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(signInClientNow())}`);
3125
2829
  lookCache = { until: Date.now() + LOOK_TTL_MS, look: res.error ? {} : res.data ?? {} };
3126
2830
  }
3127
2831
  return json(lookCache.look);
@@ -3146,7 +2850,7 @@ function createCauthy(given = {}) {
3146
2850
  signUp: settings.data?.allow_signup !== false && settings.data?.signup?.mode !== "invite_only",
3147
2851
  // The application's system makes an organization per sign-up: the form asks the company's name.
3148
2852
  ...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.
2853
+ // Whether the person may ask to stay signed in: the workspace's setting in Cauthy (persistent_sessions).
3150
2854
  ...typeof settings.data?.persistent_sessions === "boolean" ? { persistentSessions: settings.data.persistent_sessions } : {},
3151
2855
  // A sign-in link needs a page of the application to open.
3152
2856
  magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
@@ -3310,7 +3014,7 @@ function createCauthy(given = {}) {
3310
3014
  let path = "/v1/auth/me/organizations";
3311
3015
  if (route !== "/me/organizations") {
3312
3016
  const id = url.searchParams.get("id") ?? "";
3313
- if (!UUID3.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3017
+ if (!UUID2.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3314
3018
  path += `/${id}/${route.endsWith("/roles") ? "roles" : "members"}`;
3315
3019
  } else if (url.searchParams.get("system")) {
3316
3020
  path += `?system=${encodeURIComponent(url.searchParams.get("system"))}`;
@@ -3371,8 +3075,7 @@ function createCauthy(given = {}) {
3371
3075
  if (route === "/sign-out") {
3372
3076
  const { had, value } = await read(request);
3373
3077
  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);
3078
+ await revoke(value.rt);
3376
3079
  orgCache.delete(value.at);
3377
3080
  }
3378
3081
  const h = new Headers();
@@ -3599,7 +3302,7 @@ function createCauthy(given = {}) {
3599
3302
  }
3600
3303
  if (route === "/me/organizations/rename" || route === "/me/organizations/archive" || route === "/me/organizations/delete") {
3601
3304
  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);
3305
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3603
3306
  const path = `/v1/auth/me/organizations/${org}`;
3604
3307
  if (route === "/me/organizations/rename") {
3605
3308
  const bad = checkOrganizationName(b.name);
@@ -3616,7 +3319,7 @@ function createCauthy(given = {}) {
3616
3319
  }
3617
3320
  if (route.startsWith("/me/organizations/members/")) {
3618
3321
  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);
3322
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
3620
3323
  const base = `/v1/auth/me/organizations/${org}/members`;
3621
3324
  if (route === "/me/organizations/members/add") {
3622
3325
  const bad = checkEmail(b.email);
@@ -3627,7 +3330,7 @@ function createCauthy(given = {}) {
3627
3330
  email: String(b.email).trim(),
3628
3331
  role,
3629
3332
  name: typeof b.name === "string" ? b.name.trim() : "",
3630
- client_id: clientId,
3333
+ client_id: signInClientNow(),
3631
3334
  only_new: true,
3632
3335
  ...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {}
3633
3336
  }, pages.acceptInvite, mine.token);
@@ -3636,7 +3339,7 @@ function createCauthy(given = {}) {
3636
3339
  return json({ status: "added", member: orgMemberOf(res.data) }, 200, mine.headers);
3637
3340
  }
3638
3341
  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);
3342
+ if (!UUID2.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
3640
3343
  if (route === "/me/organizations/members/role") {
3641
3344
  const role = typeof b.role === "string" ? b.role : "";
3642
3345
  if (role === "") return problem(400, "invalid_role", "Choose a role.", "role", mine.headers);
@@ -3651,7 +3354,7 @@ function createCauthy(given = {}) {
3651
3354
  return json({ status: "removed", userId: user }, 200, mine.headers);
3652
3355
  }
3653
3356
  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);
3357
+ 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
3358
  if (res.error) return refused(res);
3656
3359
  return json({ status: "sent", userId: user }, 200, mine.headers);
3657
3360
  }
@@ -3769,18 +3472,15 @@ function createCauthy(given = {}) {
3769
3472
  const url = new URL(request.url);
3770
3473
  const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
3771
3474
  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
3475
  if (identity && route !== null) {
3774
3476
  const unready = await identityGate();
3775
3477
  if (unready) return finish(unready, route);
3776
3478
  }
3777
3479
  try {
3778
- return finish(await serve(request), route, published3);
3480
+ return finish(await serve(request), route);
3779
3481
  } catch (err) {
3780
3482
  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);
3483
+ throw err;
3784
3484
  }
3785
3485
  }
3786
3486
  async function identityGate() {
@@ -3804,7 +3504,6 @@ function createCauthy(given = {}) {
3804
3504
  }
3805
3505
  async function serve(request) {
3806
3506
  const url = new URL(request.url);
3807
- if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
3808
3507
  if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return new Response("Not found", { status: 404 });
3809
3508
  const route = withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/";
3810
3509
  const get = request.method === "GET" || request.method === "HEAD";
@@ -3828,7 +3527,7 @@ function createCauthy(given = {}) {
3828
3527
  return redirect(back, r.cookies);
3829
3528
  }
3830
3529
  if (route === "/account" && get) {
3831
- const q = new URLSearchParams({ client_id: clientId });
3530
+ const q = new URLSearchParams({ client_id: signInClientNow() });
3832
3531
  const back = localPath(url.searchParams.get("return_to"), "");
3833
3532
  if (back !== "") q.set("return_to", `${originOf(request)}${back}`);
3834
3533
  const locale = url.searchParams.get("locale");
@@ -3847,7 +3546,7 @@ function createCauthy(given = {}) {
3847
3546
  const asker = await signedInAs(request);
3848
3547
  if ("response" in asker) return asker.response;
3849
3548
  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);
3549
+ if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
3851
3550
  const named = (v) => typeof v === "string" && v !== "" && v.length <= 200;
3852
3551
  if (!named(b.resource) || !named(b.action)) return problem(400, "bad_request", "Say which resource and action.", void 0, asker.headers);
3853
3552
  const attributes2 = b.attributes;
@@ -3870,8 +3569,7 @@ function createCauthy(given = {}) {
3870
3569
  }
3871
3570
  }
3872
3571
  async function protect(request) {
3873
- const read1 = await read(request);
3874
- const value = read1.value !== null && unverified(read1.value.u) ? null : read1.value;
3572
+ const { value } = await read(request);
3875
3573
  if (value !== null && fresh(value)) return { session: toSession(value) };
3876
3574
  const url = new URL(request.url);
3877
3575
  const state = value === null ? "none" : "expired";
@@ -3884,85 +3582,36 @@ function createCauthy(given = {}) {
3884
3582
  return { response: redirect(to) };
3885
3583
  }
3886
3584
  const replay = given.webhookReplay ?? memoryReplayStore();
3887
- let lateSelf;
3888
3585
  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
- }
3586
+ if (identity) {
3893
3587
  let built;
3894
3588
  return async (from) => {
3895
- const id = await (identity ?? (lateSelf ??= createIdentity(selfOptions()))).ready();
3589
+ const id = await identity.ready();
3896
3590
  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
- }) };
3591
+ built = { from: id, verify: createTokenVerifier({ issuer, tenantId: id.tenant.id, audience: id.audience, fetch: given.fetch, ...o }) };
3905
3592
  }
3906
3593
  return built.verify(from);
3907
3594
  };
3908
3595
  }
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
3596
  return createTokenVerifier({
3913
3597
  issuer,
3914
- tenantId: contract?.tenantId ?? given.tenantId,
3598
+ tenantId: given.tenantId,
3915
3599
  audience: given.audience,
3916
3600
  fetch: given.fetch,
3917
- ...contract ? { clientId: contract.clientId } : {},
3918
3601
  ...o
3919
3602
  });
3920
3603
  };
3921
3604
  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
3605
  const configured = () => ({
3956
3606
  issuer,
3957
3607
  audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
3958
- tenant: { id: contract?.tenantId ?? given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3608
+ tenant: { id: given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
3959
3609
  system,
3960
3610
  client_id: clientId,
3961
3611
  sign_in_client_id: clientId,
3962
3612
  app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
3963
3613
  callback_path: `${basePath}/callback`,
3964
- key: null,
3965
- contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
3614
+ key: null
3966
3615
  });
3967
3616
  return {
3968
3617
  handler,
@@ -3984,7 +3633,6 @@ function createCauthy(given = {}) {
3984
3633
  check,
3985
3634
  toPublic,
3986
3635
  urls,
3987
- ...pact ? { pact } : {},
3988
3636
  options: { issuer, clientId, basePath, afterSignIn, afterSignOut, cookieName }
3989
3637
  };
3990
3638
  }
@@ -4069,7 +3717,6 @@ async function connectSystem(o) {
4069
3717
  KEY_REPLACES,
4070
3718
  MIN_PASSWORD,
4071
3719
  PROTOCOL_VERSION,
4072
- STANDARD_VARIABLES,
4073
3720
  can,
4074
3721
  checkCode,
4075
3722
  checkEmail,
@@ -4085,7 +3732,6 @@ async function connectSystem(o) {
4085
3732
  definePolicy,
4086
3733
  describePasswordPolicy,
4087
3734
  explainPasswordProblems,
4088
- failClosed,
4089
3735
  memoryReplayStore,
4090
3736
  parseKey,
4091
3737
  passwordPolicyOf,
@@ -4096,7 +3742,6 @@ async function connectSystem(o) {
4096
3742
  pushCatalog,
4097
3743
  pushPolicy,
4098
3744
  pushTheme,
4099
- readContract,
4100
3745
  remoteKeys,
4101
3746
  themeFromStackmyth,
4102
3747
  tokensFromStackmyth,