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