@cauthyhq/node 1.2.0 → 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.
- package/CHANGELOG.md +30 -0
- package/README.md +16 -28
- package/dist/{cauthy-CCSSj3Lg.d.mts → cauthy-CsWUgyv_.d.mts} +42 -145
- package/dist/{cauthy-C7gZ-pbv.d.ts → cauthy-DHLBEuqP.d.ts} +42 -145
- package/dist/express.d.mts +2 -5
- package/dist/express.d.ts +2 -5
- package/dist/express.js +1 -2
- package/dist/express.js.map +1 -1
- package/dist/express.mjs +1 -2
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +1 -3
- package/dist/fastify.d.ts +1 -3
- package/dist/fastify.js +0 -2
- package/dist/fastify.js.map +1 -1
- package/dist/fastify.mjs +0 -2
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +2 -5
- package/dist/hono.d.ts +2 -5
- package/dist/hono.js +1 -2
- package/dist/hono.js.map +1 -1
- package/dist/hono.mjs +1 -2
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +187 -530
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +180 -520
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/mcp.mjs.map +1 -1
- package/dist/next.d.mts +1 -3
- package/dist/next.d.ts +1 -3
- package/dist/testing.d.mts +11 -5
- package/dist/testing.d.ts +11 -5
- package/dist/testing.js +52 -50
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +52 -50
- package/dist/testing.mjs.map +1 -1
- package/package.json +5 -14
- package/dist/identity-BTJ8NB_w.d.mts +0 -27
- package/dist/identity-BTJ8NB_w.d.ts +0 -27
- package/dist/keys-D5k0omTb.d.mts +0 -64
- package/dist/keys-D5k0omTb.d.ts +0 -64
- package/dist/keys.d.mts +0 -8
- package/dist/keys.d.ts +0 -8
- package/dist/keys.js +0 -157
- package/dist/keys.js.map +0 -1
- package/dist/keys.mjs +0 -129
- 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,
|
|
@@ -122,266 +119,14 @@ async function unseal(sealed, key) {
|
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
async function unsealAny(sealed, ring) {
|
|
125
|
-
|
|
126
|
-
const value = await unseal(sealed, key);
|
|
127
|
-
if (value !== null) return value;
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
122
|
+
return (await unsealIndexed(sealed, ring))?.value ?? null;
|
|
130
123
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
super(`@cauthyhq/node: ${message}`);
|
|
136
|
-
this.name = "CauthyConfigError";
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
var CauthyKeyError = class extends CauthyConfigError {
|
|
140
|
-
constructor(reason, message) {
|
|
141
|
-
super(`CAUTHY_KEY: ${message} (${reason})`);
|
|
142
|
-
this.reason = reason;
|
|
143
|
-
this.name = "CauthyKeyError";
|
|
124
|
+
async function unsealIndexed(sealed, ring) {
|
|
125
|
+
for (let index = 0; index < ring.length; index++) {
|
|
126
|
+
const value = await unseal(sealed, ring[index]);
|
|
127
|
+
if (value !== null) return { value, index };
|
|
144
128
|
}
|
|
145
|
-
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// src/url.ts
|
|
149
|
-
function withoutTrailingSlashes(s) {
|
|
150
|
-
let end = s.length;
|
|
151
|
-
while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
|
|
152
|
-
return s.slice(0, end);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// src/contract.ts
|
|
156
|
-
var STANDARD_VARIABLES = [
|
|
157
|
-
"CAUTHY_ISSUER",
|
|
158
|
-
"CAUTHY_CLIENT_ID",
|
|
159
|
-
"CAUTHY_CLIENT_SECRET",
|
|
160
|
-
"CAUTHY_SECRET",
|
|
161
|
-
"CAUTHY_SYSTEM",
|
|
162
|
-
"CAUTHY_TENANT",
|
|
163
|
-
"CAUTHY_SYSTEM_KEY",
|
|
164
|
-
"CAUTHY_SYSTEM_KEY_ID"
|
|
165
|
-
];
|
|
166
|
-
function failClosed(environment) {
|
|
167
|
-
const set = STANDARD_VARIABLES.filter((name) => (environment?.[name] ?? "") !== "");
|
|
168
|
-
if (set.length > 0) {
|
|
169
|
-
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.");
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
var SLUG = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/;
|
|
173
|
-
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
174
|
-
var NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
175
|
-
var text = (v) => typeof v === "string" ? v : "";
|
|
176
|
-
function httpsAddress(raw, what) {
|
|
177
|
-
let u;
|
|
178
|
-
try {
|
|
179
|
-
u = new URL(raw);
|
|
180
|
-
} catch {
|
|
181
|
-
throw new CauthyConfigError(`the contract's ${what} is not an address: ${JSON.stringify(raw)}`);
|
|
182
|
-
}
|
|
183
|
-
const local = u.hostname === "localhost" || u.hostname === "127.0.0.1";
|
|
184
|
-
if (u.protocol !== "https:" && !(u.protocol === "http:" && local)) {
|
|
185
|
-
throw new CauthyConfigError(`the contract's ${what} must be https: ${raw}`);
|
|
186
|
-
}
|
|
187
|
-
return u;
|
|
188
|
-
}
|
|
189
|
-
function readContract(options) {
|
|
190
|
-
const { system } = options;
|
|
191
|
-
const provider = options.provider ?? "cauthy";
|
|
192
|
-
if (!SLUG.test(system ?? "")) throw new CauthyConfigError(`contract.system must be the system's id in the graph, e.g. "acme-crm"`);
|
|
193
|
-
const graph = options.graph && typeof options.graph === "object" && "default" in options.graph && !("edges" in options.graph) ? options.graph.default : options.graph;
|
|
194
|
-
if (!graph || !Array.isArray(graph.edges) || !Array.isArray(graph.nodes)) {
|
|
195
|
-
throw new CauthyConfigError("contract.graph is not a context graph: pass the graph.json that `cauthy contracts pull` writes");
|
|
196
|
-
}
|
|
197
|
-
const node = (id) => graph.nodes?.find((n) => n.type === "system" && n.id === id);
|
|
198
|
-
const own = node(system);
|
|
199
|
-
if (!own) throw new CauthyConfigError(`the graph has no system "${system}"`);
|
|
200
|
-
const cauthy = node(provider);
|
|
201
|
-
if (!cauthy) throw new CauthyConfigError(`the graph has no system "${provider}"`);
|
|
202
|
-
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);
|
|
203
|
-
if (links.length === 0) {
|
|
204
|
-
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`);
|
|
205
|
-
}
|
|
206
|
-
if (links.length > 1) {
|
|
207
|
-
throw new CauthyConfigError(`${system} has ${links.length} links to ${provider}: name the one to read (contract.link): ${links.map((l) => text(l.link)).join(", ")}`);
|
|
208
|
-
}
|
|
209
|
-
const e = links[0];
|
|
210
|
-
const link = text(e.link);
|
|
211
|
-
const named = text(e.identity?.tenant);
|
|
212
|
-
const pinned = text(e.identity?.tenant_id);
|
|
213
|
-
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`);
|
|
214
|
-
let tenantId;
|
|
215
|
-
let tenant;
|
|
216
|
-
if (UUID.test(named)) {
|
|
217
|
-
if (pinned !== "" && pinned.toLowerCase() !== named.toLowerCase()) {
|
|
218
|
-
throw new CauthyConfigError(`the link ${link} names the workspace ${named} and its tenant_id ${pinned} does not match: pull the graph again`);
|
|
219
|
-
}
|
|
220
|
-
tenantId = named;
|
|
221
|
-
tenant = text((cauthy.tenants ?? []).find((t) => t.id === named)?.slug);
|
|
222
|
-
if (tenant === "") throw new CauthyConfigError(`the graph does not name the slug of the workspace ${named} that the link ${link} uses`);
|
|
223
|
-
} else {
|
|
224
|
-
if (!SLUG.test(named)) throw new CauthyConfigError(`the link ${link} names no workspace: identity.tenant is a UUID or a slug`);
|
|
225
|
-
if (!UUID.test(pinned)) {
|
|
226
|
-
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`);
|
|
227
|
-
}
|
|
228
|
-
tenantId = pinned;
|
|
229
|
-
tenant = named;
|
|
230
|
-
}
|
|
231
|
-
const issuer = withoutTrailingSlashes(httpsAddress(text(cauthy.url), `address of ${provider}`).href);
|
|
232
|
-
const signIn = e.sign_in ?? {};
|
|
233
|
-
const clientId = text(signIn.client_id) || `pact.${link}`;
|
|
234
|
-
const redirectUris = Array.isArray(signIn.redirect_uris) ? signIn.redirect_uris.map(text).filter(Boolean) : [];
|
|
235
|
-
if (redirectUris.length === 0) throw new CauthyConfigError(`the link ${link} names no redirect URI`);
|
|
236
|
-
const proof = text(signIn.system_proof);
|
|
237
|
-
if (signIn.system_proof !== void 0 && proof !== "off" && proof !== "auto" && proof !== "required") {
|
|
238
|
-
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`);
|
|
239
|
-
}
|
|
240
|
-
const systemProof = proof === "off" || proof === "auto" || proof === "required" ? proof : signIn.require_system_proof === true ? "required" : "off";
|
|
241
|
-
if (systemProof !== "off" && !e.service) {
|
|
242
|
-
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`);
|
|
243
|
-
}
|
|
244
|
-
const out2 = {
|
|
245
|
-
system,
|
|
246
|
-
link,
|
|
247
|
-
issuer,
|
|
248
|
-
tenantId,
|
|
249
|
-
tenant,
|
|
250
|
-
clientId,
|
|
251
|
-
redirectUris,
|
|
252
|
-
requireSystemProof: systemProof === "required",
|
|
253
|
-
systemProof,
|
|
254
|
-
requireVerifiedEmail: signIn.require_verified_email === true,
|
|
255
|
-
persistentSessions: signIn.persistent_sessions === true
|
|
256
|
-
};
|
|
257
|
-
if (e.service) {
|
|
258
|
-
out2.serviceClientId = text(e.service.client_id) || `pact.${link}.service`;
|
|
259
|
-
const jwks = text(e.service.jwks_url) || text(own.jwks_url);
|
|
260
|
-
if (jwks === "") throw new CauthyConfigError(`the link ${link} gives ${system} its own identity, and ${system} names no jwks_url`);
|
|
261
|
-
out2.jwksUrl = httpsAddress(jwks, "jwks_url").href;
|
|
262
|
-
out2.jwksPath = new URL(out2.jwksUrl).pathname;
|
|
263
|
-
}
|
|
264
|
-
if (text(own.events_url) !== "") out2.eventsUrl = httpsAddress(text(own.events_url), "events_url").href;
|
|
265
|
-
return out2;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/keys.ts
|
|
269
|
-
var import_jose2 = require("jose");
|
|
270
|
-
var RETIRE_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
271
|
-
var FORGET_AFTER_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
272
|
-
var CURRENT_TTL_MS = 5 * 60 * 1e3;
|
|
273
|
-
var JWKS_MAX_AGE_S = 300;
|
|
274
|
-
var SIGN_AFTER_MS = (JWKS_MAX_AGE_S + 60) * 1e3;
|
|
275
|
-
var SETTLING_TTL_MS = 5 * 1e3;
|
|
276
|
-
var newestFirst = (keys2) => [...keys2].sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.kid.localeCompare(a.kid));
|
|
277
|
-
function published(key) {
|
|
278
|
-
const { kty, n, e } = key.publicJwk;
|
|
279
|
-
return { kty, n, e, kid: key.kid, alg: "RS256", use: "sig" };
|
|
280
|
-
}
|
|
281
|
-
function pactKeys(store, sealKeys, now = Date.now) {
|
|
282
|
-
let remembered;
|
|
283
|
-
let making;
|
|
284
|
-
async function make(after) {
|
|
285
|
-
const { publicKey, privateKey } = await (0, import_jose2.generateKeyPair)("RS256", { modulusLength: 2048, extractable: true });
|
|
286
|
-
const publicJwk = await (0, import_jose2.exportJWK)(publicKey);
|
|
287
|
-
const kid = await (0, import_jose2.calculateJwkThumbprint)(publicJwk);
|
|
288
|
-
const at = Math.max(now(), after ? Date.parse(after.createdAt) + 1 : 0);
|
|
289
|
-
const key = { kid, publicJwk, sealed: await seal(await (0, import_jose2.exportJWK)(privateKey), (await sealKeys)[0]), createdAt: new Date(at).toISOString() };
|
|
290
|
-
if (!after && store.addFirst) await store.addFirst(key);
|
|
291
|
-
else await store.add(key);
|
|
292
|
-
return key;
|
|
293
|
-
}
|
|
294
|
-
const age = (key) => now() - Date.parse(key.createdAt);
|
|
295
|
-
function signer(kept) {
|
|
296
|
-
const ripe = kept.find((k) => age(k) >= SIGN_AFTER_MS);
|
|
297
|
-
if (!ripe) return { key: kept[kept.length - 1], until: now() + SETTLING_TTL_MS };
|
|
298
|
-
const until = ripe === kept[0] ? now() + CURRENT_TTL_MS : Math.min(now() + CURRENT_TTL_MS, Date.parse(kept[0].createdAt) + SIGN_AFTER_MS);
|
|
299
|
-
return { key: ripe, until };
|
|
300
|
-
}
|
|
301
|
-
async function open(key) {
|
|
302
|
-
const ring = await sealKeys;
|
|
303
|
-
for (let i = 0; i < ring.length; i++) {
|
|
304
|
-
const jwk = await unseal(key.sealed, ring[i]);
|
|
305
|
-
if (jwk !== null) return { jwk, fresh: i === 0 };
|
|
306
|
-
}
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
async function keptKeys() {
|
|
310
|
-
const kept = newestFirst(await store.list());
|
|
311
|
-
if (kept.length > 0) return kept;
|
|
312
|
-
making ??= make().finally(() => {
|
|
313
|
-
making = void 0;
|
|
314
|
-
});
|
|
315
|
-
await making;
|
|
316
|
-
const again = newestFirst(await store.list());
|
|
317
|
-
if (again.length === 0) throw new CauthyConfigError("the key store kept no key: check that it writes");
|
|
318
|
-
return again;
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
async current() {
|
|
322
|
-
if (remembered && remembered.until > now()) return remembered;
|
|
323
|
-
const { key, until } = signer(await keptKeys());
|
|
324
|
-
if (remembered?.kid === key.kid) {
|
|
325
|
-
remembered.until = until;
|
|
326
|
-
return remembered;
|
|
327
|
-
}
|
|
328
|
-
const opened = await open(key);
|
|
329
|
-
if (opened === null) {
|
|
330
|
-
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`);
|
|
331
|
-
}
|
|
332
|
-
remembered = { kid: key.kid, privateKey: await (0, import_jose2.importJWK)(opened.jwk, "RS256"), until };
|
|
333
|
-
return remembered;
|
|
334
|
-
},
|
|
335
|
-
async published() {
|
|
336
|
-
const kept = await keptKeys();
|
|
337
|
-
const out2 = [];
|
|
338
|
-
for (let i = 0; i < kept.length; i++) {
|
|
339
|
-
if (i === 0 || now() - Date.parse(kept[i - 1].createdAt) < RETIRE_AFTER_MS) out2.push(published(kept[i]));
|
|
340
|
-
else if (now() - Date.parse(kept[i - 1].createdAt) > FORGET_AFTER_MS) await store.remove(kept[i].kid);
|
|
341
|
-
}
|
|
342
|
-
return { keys: out2 };
|
|
343
|
-
},
|
|
344
|
-
async rotate() {
|
|
345
|
-
const key = await make(newestFirst(await store.list())[0]);
|
|
346
|
-
remembered = void 0;
|
|
347
|
-
return key.kid;
|
|
348
|
-
},
|
|
349
|
-
async reseal() {
|
|
350
|
-
let changed = 0;
|
|
351
|
-
const ring = await sealKeys;
|
|
352
|
-
for (const key of await store.list()) {
|
|
353
|
-
const opened = await open(key);
|
|
354
|
-
if (opened === null || opened.fresh) continue;
|
|
355
|
-
await store.update({ ...key, sealed: await seal(opened.jwk, ring[0]) });
|
|
356
|
-
changed++;
|
|
357
|
-
}
|
|
358
|
-
return changed;
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// src/rootkey.ts
|
|
364
|
-
var encoder2 = new TextEncoder();
|
|
365
|
-
var MIN_ROOT_KEY = 32;
|
|
366
|
-
async function derive(root, purpose) {
|
|
367
|
-
const ikm = await crypto.subtle.importKey("raw", encoder2.encode(root), "HKDF", false, ["deriveBits"]);
|
|
368
|
-
const bits = await crypto.subtle.deriveBits(
|
|
369
|
-
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder2.encode(`pact/${purpose}/v1`) },
|
|
370
|
-
ikm,
|
|
371
|
-
256
|
|
372
|
-
);
|
|
373
|
-
return new Uint8Array(bits);
|
|
374
|
-
}
|
|
375
|
-
function deriveKeys(root, previous) {
|
|
376
|
-
if (root.length < MIN_ROOT_KEY) {
|
|
377
|
-
throw new CauthyConfigError(`rootKey (PACT_ROOT_KEY) must be at least ${MIN_ROOT_KEY} characters: 32 random bytes, e.g. openssl rand -base64 32`);
|
|
378
|
-
}
|
|
379
|
-
if (previous !== void 0 && previous !== "" && previous.length < MIN_ROOT_KEY) {
|
|
380
|
-
throw new CauthyConfigError(`the previous root key (PACT_ROOT_KEY_PREVIOUS) must be at least ${MIN_ROOT_KEY} characters`);
|
|
381
|
-
}
|
|
382
|
-
const roots = previous && previous !== root ? [root, previous] : [root];
|
|
383
|
-
const ring = (purpose) => Promise.all(roots.map((r) => derive(r, purpose)));
|
|
384
|
-
return { seal: ring("seal"), cookie: ring("cookie"), flow: ring("flow") };
|
|
129
|
+
return null;
|
|
385
130
|
}
|
|
386
131
|
|
|
387
132
|
// src/deadline.ts
|
|
@@ -417,6 +162,13 @@ function anySignal(signals) {
|
|
|
417
162
|
return controller.signal;
|
|
418
163
|
}
|
|
419
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
|
+
|
|
420
172
|
// src/permissions.ts
|
|
421
173
|
function matches(entry, resource, action) {
|
|
422
174
|
const i = entry.lastIndexOf(":");
|
|
@@ -486,19 +238,19 @@ var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
|
486
238
|
function policySource(system, read) {
|
|
487
239
|
const roles = Array.isArray(read) ? read : read.roles;
|
|
488
240
|
const catalog = Array.isArray(read) ? [] : read.catalog ?? [];
|
|
489
|
-
const
|
|
490
|
-
const key = (k) => IDENT.test(k) ? k :
|
|
241
|
+
const text = (v) => JSON.stringify(v);
|
|
242
|
+
const key = (k) => IDENT.test(k) ? k : text(k);
|
|
491
243
|
const one = (r, pad) => {
|
|
492
|
-
const parts = [`role: ${
|
|
493
|
-
if (r.name) parts.push(`name: ${
|
|
494
|
-
if (r.description) parts.push(`description: ${
|
|
495
|
-
if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(
|
|
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(", ")}]`);
|
|
496
248
|
if (r.permissions?.length) {
|
|
497
249
|
const list = r.permissions.map((p) => `${pad} { ${[
|
|
498
|
-
...p.kind && p.kind !== "*" ? [`${key("kind")}: ${
|
|
499
|
-
`resource: ${
|
|
500
|
-
`action: ${
|
|
501
|
-
...p.condition ? [`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)}`] : []
|
|
502
254
|
].join(", ")} },`);
|
|
503
255
|
parts.push(`permissions: [
|
|
504
256
|
${list.join("\n")}
|
|
@@ -510,14 +262,14 @@ ${pad} `)},
|
|
|
510
262
|
${pad} },`;
|
|
511
263
|
};
|
|
512
264
|
const resource = (c) => {
|
|
513
|
-
const parts = [`resource: ${
|
|
514
|
-
if (c.description) parts.push(`description: ${
|
|
515
|
-
parts.push(`actions: [${c.actions.map(
|
|
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(", ")}]`);
|
|
516
268
|
const attrs = Object.entries(c.attributes ?? {});
|
|
517
|
-
if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${
|
|
269
|
+
if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text(v)}`).join(", ")} }`);
|
|
518
270
|
return ` { ${parts.join(", ")} },`;
|
|
519
271
|
};
|
|
520
|
-
const head = `// The catalog of roles of ${
|
|
272
|
+
const head = `// The catalog of roles of ${text(system)}, as Cauthy has it.
|
|
521
273
|
// Written by \`pullPolicy\`: run it again after the catalog
|
|
522
274
|
// changes, and do not edit this file by hand.
|
|
523
275
|
`;
|
|
@@ -627,7 +379,7 @@ function organizationIdOf(o) {
|
|
|
627
379
|
}
|
|
628
380
|
|
|
629
381
|
// src/system.ts
|
|
630
|
-
var
|
|
382
|
+
var import_jose2 = require("jose");
|
|
631
383
|
|
|
632
384
|
// src/theme.ts
|
|
633
385
|
var MAX_RADIUS = 40;
|
|
@@ -644,7 +396,7 @@ function withoutComments(css) {
|
|
|
644
396
|
}
|
|
645
397
|
}
|
|
646
398
|
function declarations(css) {
|
|
647
|
-
const
|
|
399
|
+
const text = withoutComments(css);
|
|
648
400
|
const out2 = [];
|
|
649
401
|
const within = [];
|
|
650
402
|
let chunk = "";
|
|
@@ -655,7 +407,7 @@ function declarations(css) {
|
|
|
655
407
|
if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
|
|
656
408
|
chunk = "";
|
|
657
409
|
};
|
|
658
|
-
for (const ch of
|
|
410
|
+
for (const ch of text) {
|
|
659
411
|
if (quote) {
|
|
660
412
|
chunk += ch;
|
|
661
413
|
if (ch === quote) quote = "";
|
|
@@ -991,6 +743,22 @@ async function pushTheme(o) {
|
|
|
991
743
|
}
|
|
992
744
|
}
|
|
993
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
|
+
|
|
994
762
|
// src/system.ts
|
|
995
763
|
var ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
996
764
|
var SKEW_MS = 3e4;
|
|
@@ -1030,7 +798,7 @@ function asJwk(k) {
|
|
|
1030
798
|
}
|
|
1031
799
|
async function loadKey(k) {
|
|
1032
800
|
const jwk = asJwk(k);
|
|
1033
|
-
return jwk ? (0,
|
|
801
|
+
return jwk ? (0, import_jose2.importJWK)(jwk, "RS256") : (0, import_jose2.importPKCS8)(k.privateKey, "RS256", { extractable: true });
|
|
1034
802
|
}
|
|
1035
803
|
function createSystem(given = {}) {
|
|
1036
804
|
const issuer = withoutTrailingSlashes(given.issuer ?? env("CAUTHY_ISSUER") ?? "");
|
|
@@ -1057,10 +825,6 @@ function createSystem(given = {}) {
|
|
|
1057
825
|
if (given.signer) return;
|
|
1058
826
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
1059
827
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
1060
|
-
if (given.keys) {
|
|
1061
|
-
if (!given.keysClientId) throw new CauthyConfigError("the contract gives the system no identity of its own: its link needs a service block");
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
828
|
if (key ? key.keyId === "" : clientSecret === "") {
|
|
1065
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)");
|
|
1066
830
|
}
|
|
@@ -1078,15 +842,15 @@ function createSystem(given = {}) {
|
|
|
1078
842
|
let asking;
|
|
1079
843
|
async function ask() {
|
|
1080
844
|
configured();
|
|
1081
|
-
const self = given.signer ? given.signer.clientId :
|
|
845
|
+
const self = given.signer ? given.signer.clientId : key?.clientId || clientId;
|
|
1082
846
|
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: self });
|
|
1083
847
|
if (given.signer) {
|
|
1084
848
|
const s = await given.signer.signer;
|
|
1085
849
|
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
1086
850
|
form.set("client_assertion", await s.assertion(self, tokenEndpoint));
|
|
1087
|
-
} else if (
|
|
1088
|
-
const signer =
|
|
1089
|
-
const assertion = await new
|
|
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);
|
|
1090
854
|
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
1091
855
|
form.set("client_assertion", assertion);
|
|
1092
856
|
} else {
|
|
@@ -1194,9 +958,8 @@ function createSystem(given = {}) {
|
|
|
1194
958
|
return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
|
|
1195
959
|
},
|
|
1196
960
|
async publicJwks() {
|
|
1197
|
-
if (given.keys) return given.keys.published();
|
|
1198
961
|
if (!key) return { keys: [] };
|
|
1199
|
-
const { kty, n, e } = asJwk(key) ?? await (0,
|
|
962
|
+
const { kty, n, e } = asJwk(key) ?? await (0, import_jose2.exportJWK)(await signingKey());
|
|
1200
963
|
return { keys: [{ kty, n, e, kid: key.keyId, alg: "RS256", use: "sig" }] };
|
|
1201
964
|
}
|
|
1202
965
|
};
|
|
@@ -1358,7 +1121,7 @@ function firstProblem(...checks) {
|
|
|
1358
1121
|
}
|
|
1359
1122
|
|
|
1360
1123
|
// src/webhook.ts
|
|
1361
|
-
var
|
|
1124
|
+
var import_jose3 = require("jose");
|
|
1362
1125
|
|
|
1363
1126
|
// src/sha256.ts
|
|
1364
1127
|
var K = new Uint32Array([
|
|
@@ -1530,8 +1293,8 @@ var fail = (reason, message) => {
|
|
|
1530
1293
|
throw new CauthyKeyError(reason, message);
|
|
1531
1294
|
};
|
|
1532
1295
|
var NOT_A_KEY = "not a Cauthy key (copy it again from the dashboard)";
|
|
1533
|
-
function parseKey(
|
|
1534
|
-
const s =
|
|
1296
|
+
function parseKey(text) {
|
|
1297
|
+
const s = text.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, "");
|
|
1535
1298
|
if (s === "" || s.length > MAX_LEN || !printableAscii(s)) fail("key_format", NOT_A_KEY);
|
|
1536
1299
|
const head = s.split(".", 1)[0];
|
|
1537
1300
|
if (head !== KEY_PREFIX) {
|
|
@@ -1599,7 +1362,7 @@ var keys = /* @__PURE__ */ new Map();
|
|
|
1599
1362
|
function keysOf(issuer) {
|
|
1600
1363
|
let set = keys.get(issuer);
|
|
1601
1364
|
if (set === void 0) {
|
|
1602
|
-
set = (0,
|
|
1365
|
+
set = (0, import_jose3.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
|
|
1603
1366
|
keys.set(issuer, set);
|
|
1604
1367
|
}
|
|
1605
1368
|
return set;
|
|
@@ -1616,7 +1379,7 @@ async function verifyWebhook(request, options = {}) {
|
|
|
1616
1379
|
const audience = options.url ?? request.url.split("?")[0];
|
|
1617
1380
|
let claims;
|
|
1618
1381
|
try {
|
|
1619
|
-
const verified = await (0,
|
|
1382
|
+
const verified = await (0, import_jose3.jwtVerify)(signature, options.jwks ?? keysOf(issuer), {
|
|
1620
1383
|
algorithms: ["RS256"],
|
|
1621
1384
|
audience,
|
|
1622
1385
|
typ: "cauthy-webhook+jwt",
|
|
@@ -1714,10 +1477,10 @@ async function finish(res, route, cacheable = false) {
|
|
|
1714
1477
|
if (!cacheable && !(headers.get("cache-control") ?? "").includes("no-store")) headers.set("cache-control", "no-store");
|
|
1715
1478
|
let body = res.body;
|
|
1716
1479
|
if (route !== null && SIGN_IN_ROUTES.has(route) && res.ok && (headers.get("content-type") ?? "").startsWith("application/json")) {
|
|
1717
|
-
const
|
|
1718
|
-
body =
|
|
1480
|
+
const text = await res.text();
|
|
1481
|
+
body = text;
|
|
1719
1482
|
try {
|
|
1720
|
-
const parsed = JSON.parse(
|
|
1483
|
+
const parsed = JSON.parse(text);
|
|
1721
1484
|
const next = nextOf(route, parsed?.status);
|
|
1722
1485
|
if (parsed !== null && next !== void 0) body = JSON.stringify({ ...parsed, next });
|
|
1723
1486
|
} catch {
|
|
@@ -1728,29 +1491,29 @@ async function finish(res, route, cacheable = false) {
|
|
|
1728
1491
|
}
|
|
1729
1492
|
|
|
1730
1493
|
// src/verify.ts
|
|
1731
|
-
var
|
|
1494
|
+
var import_jose4 = require("jose");
|
|
1732
1495
|
|
|
1733
1496
|
// src/json.ts
|
|
1734
1497
|
var SPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r"]);
|
|
1735
1498
|
var LIMIT = 2n ** 53n;
|
|
1736
|
-
function parseStrict(
|
|
1499
|
+
function parseStrict(text, integerMembers = []) {
|
|
1737
1500
|
let i = 0;
|
|
1738
1501
|
let depth = 0;
|
|
1739
1502
|
const fail2 = (what) => {
|
|
1740
1503
|
throw new SyntaxError(`${what} at ${i}`);
|
|
1741
1504
|
};
|
|
1742
1505
|
const skip = () => {
|
|
1743
|
-
while (i <
|
|
1506
|
+
while (i < text.length && SPACE.has(text[i])) i++;
|
|
1744
1507
|
};
|
|
1745
1508
|
const value = (integer = false) => {
|
|
1746
1509
|
skip();
|
|
1747
|
-
const c =
|
|
1510
|
+
const c = text[i];
|
|
1748
1511
|
if (c === "{") return object();
|
|
1749
1512
|
if (c === "[") return array();
|
|
1750
1513
|
if (c === '"') return string();
|
|
1751
1514
|
if (c === "-" || c >= "0" && c <= "9") return number(integer);
|
|
1752
1515
|
for (const [word, v] of [["true", true], ["false", false], ["null", null]]) {
|
|
1753
|
-
if (
|
|
1516
|
+
if (text.startsWith(word, i)) {
|
|
1754
1517
|
i += word.length;
|
|
1755
1518
|
return v;
|
|
1756
1519
|
}
|
|
@@ -1760,13 +1523,13 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1760
1523
|
const string = () => {
|
|
1761
1524
|
const start = i;
|
|
1762
1525
|
i++;
|
|
1763
|
-
while (i <
|
|
1764
|
-
if (
|
|
1526
|
+
while (i < text.length && text[i] !== '"') i += text[i] === "\\" ? 2 : 1;
|
|
1527
|
+
if (text[i] !== '"') fail2("unterminated string");
|
|
1765
1528
|
i++;
|
|
1766
|
-
return JSON.parse(
|
|
1529
|
+
return JSON.parse(text.slice(start, i));
|
|
1767
1530
|
};
|
|
1768
1531
|
const number = (integer) => {
|
|
1769
|
-
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(
|
|
1532
|
+
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i));
|
|
1770
1533
|
if (!m) return fail2("bad number");
|
|
1771
1534
|
i += m[0].length;
|
|
1772
1535
|
if (integer && (m[2] !== void 0 || m[3] !== void 0)) return Number.NaN;
|
|
@@ -1781,7 +1544,7 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1781
1544
|
depth++;
|
|
1782
1545
|
const out2 = [];
|
|
1783
1546
|
skip();
|
|
1784
|
-
if (
|
|
1547
|
+
if (text[i] === "]") {
|
|
1785
1548
|
i++;
|
|
1786
1549
|
depth--;
|
|
1787
1550
|
return out2;
|
|
@@ -1789,8 +1552,8 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1789
1552
|
for (; ; ) {
|
|
1790
1553
|
out2.push(value());
|
|
1791
1554
|
skip();
|
|
1792
|
-
if (
|
|
1793
|
-
else if (
|
|
1555
|
+
if (text[i] === ",") i++;
|
|
1556
|
+
else if (text[i] === "]") {
|
|
1794
1557
|
i++;
|
|
1795
1558
|
depth--;
|
|
1796
1559
|
return out2;
|
|
@@ -1803,24 +1566,24 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1803
1566
|
const out2 = {};
|
|
1804
1567
|
const seen = /* @__PURE__ */ new Set();
|
|
1805
1568
|
skip();
|
|
1806
|
-
if (
|
|
1569
|
+
if (text[i] === "}") {
|
|
1807
1570
|
i++;
|
|
1808
1571
|
depth--;
|
|
1809
1572
|
return out2;
|
|
1810
1573
|
}
|
|
1811
1574
|
for (; ; ) {
|
|
1812
1575
|
skip();
|
|
1813
|
-
if (
|
|
1576
|
+
if (text[i] !== '"') fail2("expected a name");
|
|
1814
1577
|
const name = string();
|
|
1815
1578
|
if (seen.has(name)) fail2(`repeated name ${JSON.stringify(name)}`);
|
|
1816
1579
|
seen.add(name);
|
|
1817
1580
|
skip();
|
|
1818
|
-
if (
|
|
1581
|
+
if (text[i] !== ":") fail2("expected :");
|
|
1819
1582
|
i++;
|
|
1820
1583
|
Object.defineProperty(out2, name, { value: value(depth === 1 && integerMembers.includes(name)), enumerable: true, writable: true, configurable: true });
|
|
1821
1584
|
skip();
|
|
1822
|
-
if (
|
|
1823
|
-
else if (
|
|
1585
|
+
if (text[i] === ",") i++;
|
|
1586
|
+
else if (text[i] === "}") {
|
|
1824
1587
|
i++;
|
|
1825
1588
|
depth--;
|
|
1826
1589
|
return out2;
|
|
@@ -1829,7 +1592,7 @@ function parseStrict(text2, integerMembers = []) {
|
|
|
1829
1592
|
};
|
|
1830
1593
|
const result = value();
|
|
1831
1594
|
skip();
|
|
1832
|
-
if (i <
|
|
1595
|
+
if (i < text.length) fail2("trailing characters");
|
|
1833
1596
|
return result;
|
|
1834
1597
|
}
|
|
1835
1598
|
|
|
@@ -1850,6 +1613,15 @@ function deprecated(what, instead) {
|
|
|
1850
1613
|
var enc2 = new TextEncoder();
|
|
1851
1614
|
var PKCS8_ED25519 = Uint8Array.from([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
|
1852
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)));
|
|
1853
1625
|
async function seedOf(secret) {
|
|
1854
1626
|
const ikm = await crypto.subtle.importKey("raw", enc2.encode(secret), "HKDF", false, ["deriveBits"]);
|
|
1855
1627
|
return new Uint8Array(await crypto.subtle.deriveBits(
|
|
@@ -1887,12 +1659,11 @@ async function signerOf(secret) {
|
|
|
1887
1659
|
}
|
|
1888
1660
|
function deriveFromKey(key, previous) {
|
|
1889
1661
|
const other = previous && previous.secret !== key.secret ? previous : void 0;
|
|
1890
|
-
const
|
|
1662
|
+
const secrets = other ? [key.secret, other.secret] : [key.secret];
|
|
1891
1663
|
return {
|
|
1892
|
-
cookie:
|
|
1893
|
-
flow:
|
|
1894
|
-
signer: signerOf(key.secret)
|
|
1895
|
-
...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)
|
|
1896
1667
|
};
|
|
1897
1668
|
}
|
|
1898
1669
|
|
|
@@ -2011,8 +1782,8 @@ var CauthyTokenError = class extends Error {
|
|
|
2011
1782
|
detail;
|
|
2012
1783
|
};
|
|
2013
1784
|
var MAX_TOKEN_BYTES = 8192;
|
|
2014
|
-
var
|
|
2015
|
-
var
|
|
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";
|
|
2016
1787
|
var SEGMENT = /^[A-Za-z0-9_-]+$/;
|
|
2017
1788
|
var MAX_KEYS_BYTES = 64 * 1024;
|
|
2018
1789
|
var KEYS_TIMEOUT_MS = 5e3;
|
|
@@ -2040,7 +1811,7 @@ function remoteKeys(issuer, fetchKeys = fetch) {
|
|
|
2040
1811
|
if (seen.has(k.kid)) throw new Error(`two usable keys share the kid ${JSON.stringify(k.kid)}`);
|
|
2041
1812
|
seen.add(k.kid);
|
|
2042
1813
|
}
|
|
2043
|
-
set = (0,
|
|
1814
|
+
set = (0, import_jose4.createLocalJWKSet)(parsed);
|
|
2044
1815
|
const maxAge = /max-age=(\d+)/.exec(res.headers.get("cache-control") ?? "");
|
|
2045
1816
|
const ttl = Math.max(KEYS_MIN_TTL_S, Math.min(maxAge ? Number(maxAge[1]) : KEYS_DEFAULT_TTL_S, KEYS_MAX_TTL_S));
|
|
2046
1817
|
expiresAt = Date.now() + ttl * 1e3;
|
|
@@ -2092,7 +1863,7 @@ async function readCapped(res, max) {
|
|
|
2092
1863
|
}
|
|
2093
1864
|
return out2;
|
|
2094
1865
|
}
|
|
2095
|
-
var
|
|
1866
|
+
var published = /* @__PURE__ */ new Map();
|
|
2096
1867
|
var withFetch = /* @__PURE__ */ new WeakMap();
|
|
2097
1868
|
function remoteKeysFor(issuer, fetchKeys) {
|
|
2098
1869
|
let byIssuer = withFetch.get(fetchKeys);
|
|
@@ -2102,10 +1873,10 @@ function remoteKeysFor(issuer, fetchKeys) {
|
|
|
2102
1873
|
return set;
|
|
2103
1874
|
}
|
|
2104
1875
|
function keysOf2(issuer) {
|
|
2105
|
-
let set =
|
|
1876
|
+
let set = published.get(issuer);
|
|
2106
1877
|
if (set === void 0) {
|
|
2107
1878
|
set = remoteKeys(issuer);
|
|
2108
|
-
|
|
1879
|
+
published.set(issuer, set);
|
|
2109
1880
|
}
|
|
2110
1881
|
return set;
|
|
2111
1882
|
}
|
|
@@ -2147,7 +1918,7 @@ function createTokenVerifier(options = {}) {
|
|
|
2147
1918
|
if (audience === "") throw new CauthyConfigError("audience (CAUTHY_AUDIENCE) is not set: without it a token issued for another application would be accepted");
|
|
2148
1919
|
const tenant = options.tenantId ?? env2("CAUTHY_TENANT_ID") ?? "";
|
|
2149
1920
|
if (tenant === "") throw new CauthyConfigError("tenantId (CAUTHY_TENANT_ID) is not set: without it a token of another workspace would be accepted");
|
|
2150
|
-
if (!
|
|
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");
|
|
2151
1922
|
const leeway = options.leeway ?? 30;
|
|
2152
1923
|
if (!Number.isFinite(leeway) || leeway < 0 || leeway > 60) throw new CauthyConfigError("leeway must be 0 to 60 seconds");
|
|
2153
1924
|
return async (from) => {
|
|
@@ -2171,7 +1942,7 @@ function createTokenVerifier(options = {}) {
|
|
|
2171
1942
|
if (typeof header.kid !== "string" || header.kid === "") throw new CauthyTokenError("missing_kid");
|
|
2172
1943
|
let payloadBytes;
|
|
2173
1944
|
try {
|
|
2174
|
-
const verified = await (0,
|
|
1945
|
+
const verified = await (0, import_jose4.compactVerify)(token, options.jwks ?? (options.fetch ? remoteKeysFor(issuer, options.fetch) : keysOf2(issuer)), { algorithms: ["RS256"] });
|
|
2175
1946
|
payloadBytes = verified.payload;
|
|
2176
1947
|
} catch (err) {
|
|
2177
1948
|
const code = err?.code;
|
|
@@ -2196,8 +1967,8 @@ function createTokenVerifier(options = {}) {
|
|
|
2196
1967
|
throw new CauthyTokenError("invalid_claim", name);
|
|
2197
1968
|
};
|
|
2198
1969
|
if (typeof claims.iss !== "string") bad("iss");
|
|
2199
|
-
if (typeof claims.sub !== "string" || !
|
|
2200
|
-
if (typeof claims.tid !== "string" || !
|
|
1970
|
+
if (typeof claims.sub !== "string" || !UUID.test(claims.sub)) bad("sub");
|
|
1971
|
+
if (typeof claims.tid !== "string" || !UUID.test(claims.tid)) bad("tid");
|
|
2201
1972
|
const aud = claims.aud;
|
|
2202
1973
|
if (!(typeof aud === "string" || Array.isArray(aud) && aud.length > 0 && aud.every((a) => typeof a === "string"))) bad("aud");
|
|
2203
1974
|
for (const name of ["exp", "iat", "nbf"]) if (claims[name] !== void 0 && !Number.isInteger(claims[name])) bad(name);
|
|
@@ -2235,13 +2006,13 @@ async function verifyToken(from, options = {}) {
|
|
|
2235
2006
|
return createTokenVerifier(options)(from);
|
|
2236
2007
|
}
|
|
2237
2008
|
async function createVerifierFromEnv(options = {}) {
|
|
2238
|
-
const
|
|
2239
|
-
if (
|
|
2009
|
+
const text = options.key ?? env2("CAUTHY_KEY");
|
|
2010
|
+
if (text === void 0 || text === "") return createTokenVerifier(options);
|
|
2240
2011
|
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2241
2012
|
for (const name of ["issuer", "audience", "tenantId"]) {
|
|
2242
2013
|
if (options[name] !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
2243
2014
|
}
|
|
2244
|
-
const key = parseKey(
|
|
2015
|
+
const key = parseKey(text);
|
|
2245
2016
|
const system = createSystem({ issuer: key.issuer, clientId: key.clientId, signer: { clientId: key.clientId, signer: deriveFromKey(key).signer }, fetch: options.fetch });
|
|
2246
2017
|
const id = await createIdentity({ issuer: key.issuer, token: () => system.token(), fetch: deadline(options.fetch, KEYS_TIMEOUT_MS) }).ready();
|
|
2247
2018
|
const { key: _, ...rest } = options;
|
|
@@ -2338,9 +2109,18 @@ function redirect(location, cookies = []) {
|
|
|
2338
2109
|
function conditionalOf(c) {
|
|
2339
2110
|
return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
|
|
2340
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
|
+
}
|
|
2341
2121
|
function byKey(given) {
|
|
2342
|
-
const
|
|
2343
|
-
if (
|
|
2122
|
+
const text = given.key ?? env3("CAUTHY_KEY");
|
|
2123
|
+
if (text === void 0 || text === "") return void 0;
|
|
2344
2124
|
refuseBesideKey(typeof process !== "undefined" ? process.env : void 0);
|
|
2345
2125
|
for (const [name, value] of [
|
|
2346
2126
|
["issuer", given.issuer],
|
|
@@ -2351,12 +2131,11 @@ function byKey(given) {
|
|
|
2351
2131
|
["tenant", given.tenant],
|
|
2352
2132
|
["audience", given.audience],
|
|
2353
2133
|
["tenantId", given.tenantId],
|
|
2354
|
-
["systemKey", given.systemKey]
|
|
2355
|
-
["rootKey", given.rootKey]
|
|
2134
|
+
["systemKey", given.systemKey]
|
|
2356
2135
|
]) {
|
|
2357
2136
|
if (value !== void 0) throw new CauthyConfigError(`with CAUTHY_KEY, ${name} comes from CAUTHY_KEY: do not pass it`);
|
|
2358
2137
|
}
|
|
2359
|
-
const key = parseKey(
|
|
2138
|
+
const key = parseKey(text);
|
|
2360
2139
|
const prevText = given.keyPrevious ?? env3("CAUTHY_KEY_PREVIOUS");
|
|
2361
2140
|
let previous;
|
|
2362
2141
|
if (prevText) {
|
|
@@ -2371,13 +2150,8 @@ function byKey(given) {
|
|
|
2371
2150
|
throw new CauthyKeyError("key_previous_mismatch", "CAUTHY_KEY_PREVIOUS names another Cauthy or client than CAUTHY_KEY");
|
|
2372
2151
|
}
|
|
2373
2152
|
const legacySecret = env3("CAUTHY_SECRET") || void 0;
|
|
2374
|
-
const legacyRoot = env3("PACT_ROOT_KEY") || void 0;
|
|
2375
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.");
|
|
2376
|
-
|
|
2377
|
-
if (given.keys && !(legacyRoot && given.contract)) {
|
|
2378
|
-
throw new CauthyConfigError("with CAUTHY_KEY the application keeps no key store: remove keys (it is read only beside PACT_ROOT_KEY and a contract, while moving)");
|
|
2379
|
-
}
|
|
2380
|
-
return { key, previous, legacySecret, legacyRoot };
|
|
2154
|
+
return { key, previous, legacySecret };
|
|
2381
2155
|
}
|
|
2382
2156
|
var UnknownOriginError = class extends Error {
|
|
2383
2157
|
constructor(origin, registered) {
|
|
@@ -2388,72 +2162,41 @@ var UnknownOriginError = class extends Error {
|
|
|
2388
2162
|
origin;
|
|
2389
2163
|
registered;
|
|
2390
2164
|
};
|
|
2391
|
-
function contractOnKey(given, keyed) {
|
|
2392
|
-
const contract = readContract(given.contract);
|
|
2393
|
-
if (contract.issuer !== keyed.key.issuer) {
|
|
2394
|
-
throw new CauthyKeyError("key_contract_issuer", `the key names ${keyed.key.issuer}, and the contract's Cauthy is ${contract.issuer}: make the key in that Cauthy's dashboard`);
|
|
2395
|
-
}
|
|
2396
|
-
return contract;
|
|
2397
|
-
}
|
|
2398
|
-
function byContract(given) {
|
|
2399
|
-
failClosed(typeof process !== "undefined" ? process.env : void 0);
|
|
2400
|
-
for (const [name, value] of [
|
|
2401
|
-
["clientId", given.clientId],
|
|
2402
|
-
["clientSecret", given.clientSecret],
|
|
2403
|
-
["secret", given.secret],
|
|
2404
|
-
["system", given.system],
|
|
2405
|
-
["tenant", given.tenant],
|
|
2406
|
-
["systemKey", given.systemKey]
|
|
2407
|
-
]) {
|
|
2408
|
-
if (value !== void 0) throw new CauthyConfigError(`with a contract, ${name} comes from the contract: do not pass it`);
|
|
2409
|
-
}
|
|
2410
|
-
const contract = readContract(given.contract);
|
|
2411
|
-
const root = given.rootKey ?? env3("PACT_ROOT_KEY") ?? "";
|
|
2412
|
-
if (root === "") throw new CauthyConfigError("rootKey (PACT_ROOT_KEY) is required with a contract: 32 random bytes, e.g. openssl rand -base64 32");
|
|
2413
|
-
if (contract.serviceClientId && !given.keys) {
|
|
2414
|
-
throw new CauthyConfigError(`the link ${contract.link} gives ${contract.system} its own identity: pass keys, where its key is kept (@cauthyhq/node/keys)`);
|
|
2415
|
-
}
|
|
2416
|
-
deprecated("PACT_ROOT_KEY", "Make the application's CAUTHY_KEY in Cauthy's dashboard (the application's card), then remove PACT_ROOT_KEY, the keys option and its table.");
|
|
2417
|
-
return { contract, root, previous: given.rootKeyPrevious ?? env3("PACT_ROOT_KEY_PREVIOUS") };
|
|
2418
|
-
}
|
|
2419
2165
|
var toldOfHeader = false;
|
|
2166
|
+
var toldOfUnopened = false;
|
|
2167
|
+
function toldUnopened() {
|
|
2168
|
+
if (toldOfUnopened) return;
|
|
2169
|
+
toldOfUnopened = true;
|
|
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.");
|
|
2171
|
+
}
|
|
2420
2172
|
function createCauthy(given = {}) {
|
|
2173
|
+
refuseContracts(given);
|
|
2421
2174
|
const keyed = byKey(given);
|
|
2422
|
-
const
|
|
2423
|
-
const
|
|
2424
|
-
const
|
|
2425
|
-
const
|
|
2426
|
-
const
|
|
2427
|
-
const
|
|
2428
|
-
const askSelf = keyed !== void 0 && contract === void 0;
|
|
2429
|
-
const system = askSelf ? "" : contract?.system ?? given.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
2430
|
-
const tenant = askSelf ? "" : contract?.tenant ?? given.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
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") ?? "";
|
|
2431
2181
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
2432
2182
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
2433
|
-
if (!
|
|
2183
|
+
if (!keyed && secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
2434
2184
|
const fromKey = keyed ? deriveFromKey(keyed.key, keyed.previous) : void 0;
|
|
2435
2185
|
let signingKid;
|
|
2436
2186
|
fromKey?.signer.then((s) => {
|
|
2437
2187
|
signingKid = s.kid;
|
|
2438
2188
|
}, () => void 0);
|
|
2439
|
-
const derived = pacted ? deriveKeys(pacted.root, pacted.previous) : void 0;
|
|
2440
|
-
const single = derived || fromKey ? void 0 : keyOf(secret).then((k) => [k]);
|
|
2441
|
-
const legacyRings = keyed?.legacyRoot ? deriveKeys(keyed.legacyRoot) : void 0;
|
|
2442
2189
|
const legacySingle = keyed?.legacySecret ? keyOf(keyed.legacySecret) : void 0;
|
|
2443
|
-
const withLegacy = async (own
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
const cookieKeys = fromKey ? withLegacy(fromKey.cookie, "cookie") : derived?.cookie ?? single;
|
|
2449
|
-
const flowKeys = fromKey ? withLegacy(fromKey.flow, "flow") : derived?.flow ?? single;
|
|
2450
|
-
if (!keyed && !pacted) {
|
|
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) {
|
|
2451
2195
|
deprecated(
|
|
2452
2196
|
"Configuring with CAUTHY_ISSUER, CAUTHY_CLIENT_ID, CAUTHY_CLIENT_SECRET, CAUTHY_SECRET and the other CAUTHY_* variables (or their options)",
|
|
2453
2197
|
"Connect the application in Cauthy's dashboard and use the one variable CAUTHY_KEY."
|
|
2454
2198
|
);
|
|
2455
2199
|
}
|
|
2456
|
-
const ownKeys = derived && given.keys ? pactKeys(given.keys, derived.seal) : legacyRings && contract && given.keys ? pactKeys(given.keys, legacyRings.seal) : void 0;
|
|
2457
2200
|
const baseURL = given.baseURL ?? (keyed ? void 0 : env3("CAUTHY_BASE_URL"));
|
|
2458
2201
|
if (baseURL !== void 0 && baseURL !== "" && !/^https?:\/\/[^/]+\/*$/.test(baseURL)) {
|
|
2459
2202
|
throw new CauthyConfigError("baseURL (CAUTHY_BASE_URL) must be the application's public origin, e.g. https://app.example.com");
|
|
@@ -2490,21 +2233,10 @@ function createCauthy(given = {}) {
|
|
|
2490
2233
|
acceptInviteUrl,
|
|
2491
2234
|
fetch: given.fetch,
|
|
2492
2235
|
timeoutMs: systemTimeout,
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
...fromKey ? {
|
|
2496
|
-
signer: { clientId: contract ? contract.serviceClientId ?? keyed.key.clientId : clientId, signer: fromKey.signer },
|
|
2497
|
-
currentSystem: () => identity?.current()?.system ?? ""
|
|
2498
|
-
} : {}
|
|
2236
|
+
// On CAUTHY_KEY the key proves the key's own client.
|
|
2237
|
+
...fromKey ? { signer: { clientId, signer: fromKey.signer }, currentSystem: () => identity?.current()?.system ?? "" } : {}
|
|
2499
2238
|
});
|
|
2500
|
-
const
|
|
2501
|
-
issuer,
|
|
2502
|
-
token: () => ownSystem.token(),
|
|
2503
|
-
fetch: deadline(given.fetch, requestTimeout),
|
|
2504
|
-
kid: () => signingKid,
|
|
2505
|
-
log: (m) => console.error(`@cauthyhq/node: ${m}`)
|
|
2506
|
-
});
|
|
2507
|
-
const identity = askSelf ? createIdentity({
|
|
2239
|
+
const identity = keyed ? createIdentity({
|
|
2508
2240
|
issuer,
|
|
2509
2241
|
token: () => ownSystem.token(),
|
|
2510
2242
|
fetch: deadline(given.fetch, requestTimeout),
|
|
@@ -2514,6 +2246,8 @@ function createCauthy(given = {}) {
|
|
|
2514
2246
|
identity?.ready().catch(() => {
|
|
2515
2247
|
});
|
|
2516
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;
|
|
2517
2251
|
const systemNow = () => identity?.current()?.system ?? system;
|
|
2518
2252
|
const requestOrigin = (request) => {
|
|
2519
2253
|
const url = new URL(request.url);
|
|
@@ -2538,7 +2272,7 @@ function createCauthy(given = {}) {
|
|
|
2538
2272
|
};
|
|
2539
2273
|
const attrs = (request, maxAge) => ({ secure: safeOrigin(request).startsWith("https://"), maxAge });
|
|
2540
2274
|
async function clientAuth(form) {
|
|
2541
|
-
if (fromKey &&
|
|
2275
|
+
if (fromKey && form.get("client_id") === clientId) {
|
|
2542
2276
|
const s = await fromKey.signer;
|
|
2543
2277
|
form.set("client_assertion_type", ASSERTION_TYPE2);
|
|
2544
2278
|
form.set("client_assertion", await s.assertion(clientId, `${issuer}/v1/oauth/token`));
|
|
@@ -2548,8 +2282,8 @@ function createCauthy(given = {}) {
|
|
|
2548
2282
|
return form;
|
|
2549
2283
|
}
|
|
2550
2284
|
async function token(form, proof = {}) {
|
|
2551
|
-
const body2 = await clientAuth(new URLSearchParams({ client_id: clientId, ...form }));
|
|
2552
2285
|
try {
|
|
2286
|
+
const body2 = await clientAuth(new URLSearchParams({ client_id: await signInClient(), ...form }));
|
|
2553
2287
|
const res = await doFetch(`${issuer}/v1/oauth/token`, {
|
|
2554
2288
|
method: "POST",
|
|
2555
2289
|
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json", ...proof },
|
|
@@ -2587,18 +2321,16 @@ function createCauthy(given = {}) {
|
|
|
2587
2321
|
}
|
|
2588
2322
|
const kindOf = (body2, was) => (typeof body2?.persistent === "boolean" ? body2.persistent : was?.p === 1) ? { p: 1 } : {};
|
|
2589
2323
|
async function revoke(refreshToken) {
|
|
2590
|
-
|
|
2591
|
-
|
|
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
|
+
}
|
|
2592
2329
|
}
|
|
2593
|
-
const unverified = (u) => contract?.requireVerifiedEmail === true && !u.emailVerified;
|
|
2594
2330
|
async function stored(body2, was) {
|
|
2595
2331
|
if (typeof body2?.access_token !== "string" || typeof body2?.refresh_token !== "string") return null;
|
|
2596
2332
|
const who = await userinfo(body2.access_token);
|
|
2597
2333
|
if (who === null || who.u.id === "") return null;
|
|
2598
|
-
if (unverified(who.u)) {
|
|
2599
|
-
await revoke(body2.refresh_token);
|
|
2600
|
-
throw new CauthyApiError(403, "email_not_confirmed", "Confirm your email address first.");
|
|
2601
|
-
}
|
|
2602
2334
|
const lifetime = typeof body2.expires_in === "number" && body2.expires_in > 0 ? body2.expires_in : 300;
|
|
2603
2335
|
return { at: body2.access_token, rt: body2.refresh_token, exp: Math.floor(Date.now() / 1e3) + lifetime, ...who, ...kindOf(body2, was) };
|
|
2604
2336
|
}
|
|
@@ -2608,10 +2340,11 @@ function createCauthy(given = {}) {
|
|
|
2608
2340
|
async function read(request) {
|
|
2609
2341
|
const had = readCookies(request.headers.get("cookie"));
|
|
2610
2342
|
const raw = readParts(had, cookieName);
|
|
2611
|
-
if (raw === null) return { had, value: null, present: false };
|
|
2612
|
-
const
|
|
2343
|
+
if (raw === null) return { had, value: null, present: false, stale: false };
|
|
2344
|
+
const opened = await unsealIndexed(raw, await cookieKeys);
|
|
2345
|
+
const value = opened?.value ?? null;
|
|
2613
2346
|
const sound = value !== null && typeof value.at === "string" && typeof value.rt === "string" && typeof value.exp === "number";
|
|
2614
|
-
return { had, value: sound ? value : null, present: true };
|
|
2347
|
+
return { had, value: sound ? value : null, present: true, stale: sound && opened.index > 0 };
|
|
2615
2348
|
}
|
|
2616
2349
|
async function write(request, s, had) {
|
|
2617
2350
|
return writeParts(cookieName, await seal(s, (await cookieKeys)[0]), attrs(request, s.p === 1 ? PERSISTENT_SECONDS : void 0), had);
|
|
@@ -2625,13 +2358,7 @@ function createCauthy(given = {}) {
|
|
|
2625
2358
|
running = (async () => {
|
|
2626
2359
|
const res = await token({ grant_type: "refresh_token", refresh_token: rt });
|
|
2627
2360
|
if (!res.ok) return res.ended ? "ended" : "unreachable";
|
|
2628
|
-
|
|
2629
|
-
try {
|
|
2630
|
-
next = await stored(res.body, was);
|
|
2631
|
-
} catch (err) {
|
|
2632
|
-
if (err instanceof CauthyApiError && err.code === "email_not_confirmed") return "ended";
|
|
2633
|
-
throw err;
|
|
2634
|
-
}
|
|
2361
|
+
const next = await stored(res.body, was);
|
|
2635
2362
|
if (next !== null) return next;
|
|
2636
2363
|
if (typeof res.body?.access_token !== "string" || typeof res.body?.refresh_token !== "string") return "unreachable";
|
|
2637
2364
|
const lifetime = typeof res.body.expires_in === "number" && res.body.expires_in > 0 ? res.body.expires_in : 300;
|
|
@@ -2648,12 +2375,12 @@ function createCauthy(given = {}) {
|
|
|
2648
2375
|
return running;
|
|
2649
2376
|
}
|
|
2650
2377
|
async function resolve2(request) {
|
|
2651
|
-
const { had, value, present } = await read(request);
|
|
2378
|
+
const { had, value, present, stale } = await read(request);
|
|
2652
2379
|
if (value === null) {
|
|
2380
|
+
if (present) toldUnopened();
|
|
2653
2381
|
return { session: null, status: "unauthenticated", cookies: present ? clearParts(cookieName, attrs(request, 0), had) : [] };
|
|
2654
2382
|
}
|
|
2655
|
-
if (
|
|
2656
|
-
if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: [] };
|
|
2383
|
+
if (fresh(value)) return { session: toSession(value), status: "authenticated", cookies: stale ? await write(request, value, had) : [] };
|
|
2657
2384
|
const renewed = await renew(value);
|
|
2658
2385
|
if (renewed === "ended") {
|
|
2659
2386
|
return { session: null, status: "unauthenticated", cookies: clearParts(cookieName, attrs(request, 0), had) };
|
|
@@ -2664,7 +2391,7 @@ function createCauthy(given = {}) {
|
|
|
2664
2391
|
async function getSession(request, responseHeaders) {
|
|
2665
2392
|
if (responseHeaders === void 0) {
|
|
2666
2393
|
const { value } = await read(request);
|
|
2667
|
-
return value !== null && fresh(value)
|
|
2394
|
+
return value !== null && fresh(value) ? toSession(value) : null;
|
|
2668
2395
|
}
|
|
2669
2396
|
const r = await resolve2(request);
|
|
2670
2397
|
for (const c of r.cookies) responseHeaders.append("set-cookie", c);
|
|
@@ -2672,7 +2399,7 @@ function createCauthy(given = {}) {
|
|
|
2672
2399
|
}
|
|
2673
2400
|
async function status(request) {
|
|
2674
2401
|
const { value } = await read(request);
|
|
2675
|
-
if (value === null
|
|
2402
|
+
if (value === null) return "unauthenticated";
|
|
2676
2403
|
return fresh(value) ? "authenticated" : "expired";
|
|
2677
2404
|
}
|
|
2678
2405
|
const orgCache = /* @__PURE__ */ new Map();
|
|
@@ -2759,7 +2486,7 @@ function createCauthy(given = {}) {
|
|
|
2759
2486
|
const flow = { state: random(), verifier: random(48), returnTo: localPath(url.searchParams.get("return_to"), afterSignIn) };
|
|
2760
2487
|
const challenge = b64url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(flow.verifier))));
|
|
2761
2488
|
const q = new URLSearchParams({
|
|
2762
|
-
client_id:
|
|
2489
|
+
client_id: await signInClient(),
|
|
2763
2490
|
redirect_uri: `${originOf(request)}${basePath}/callback`,
|
|
2764
2491
|
response_type: "code",
|
|
2765
2492
|
state: flow.state,
|
|
@@ -2821,28 +2548,17 @@ function createCauthy(given = {}) {
|
|
|
2821
2548
|
);
|
|
2822
2549
|
};
|
|
2823
2550
|
if (!res.ok && res.code === "email_not_confirmed") return unconfirmed();
|
|
2824
|
-
|
|
2825
|
-
try {
|
|
2826
|
-
s = res.ok ? await stored(res.body) : null;
|
|
2827
|
-
} catch (err) {
|
|
2828
|
-
if (!(err instanceof CauthyApiError && err.code === "email_not_confirmed")) throw err;
|
|
2829
|
-
return unconfirmed();
|
|
2830
|
-
}
|
|
2551
|
+
const s = res.ok ? await stored(res.body) : null;
|
|
2831
2552
|
if (s === null) return failed(request, "exchange_failed");
|
|
2832
2553
|
return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0)), writeCookie(againCookie, "", attrs(request, 0))]);
|
|
2833
2554
|
}
|
|
2834
2555
|
async function logout(request) {
|
|
2835
2556
|
const { had, value } = await read(request);
|
|
2836
2557
|
if (value !== null) {
|
|
2837
|
-
|
|
2838
|
-
await doFetch(`${issuer}/v1/oauth/revoke`, {
|
|
2839
|
-
method: "POST",
|
|
2840
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
2841
|
-
body: body2
|
|
2842
|
-
}).catch(() => void 0);
|
|
2558
|
+
await revoke(value.rt);
|
|
2843
2559
|
orgCache.delete(value.at);
|
|
2844
2560
|
}
|
|
2845
|
-
const q = new URLSearchParams({ client_id:
|
|
2561
|
+
const q = new URLSearchParams({ client_id: signInClientNow(), post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
|
|
2846
2562
|
return redirect(`${issuer}/v1/oauth/logout?${q}`, clearParts(cookieName, attrs(request, 0), had));
|
|
2847
2563
|
}
|
|
2848
2564
|
const STEP_SECONDS = 600;
|
|
@@ -2860,7 +2576,7 @@ function createCauthy(given = {}) {
|
|
|
2860
2576
|
createdAt: String(d?.created_at ?? ""),
|
|
2861
2577
|
lastUsedAt: d?.last_used_at ? String(d.last_used_at) : null
|
|
2862
2578
|
});
|
|
2863
|
-
const
|
|
2579
|
+
const UUID2 = /^[0-9a-f-]{36}$/i;
|
|
2864
2580
|
const seatOf = (d) => ({
|
|
2865
2581
|
id: String(d?.id ?? ""),
|
|
2866
2582
|
system: String(d?.system ?? ""),
|
|
@@ -2965,10 +2681,10 @@ function createCauthy(given = {}) {
|
|
|
2965
2681
|
...page ? { "x-cauthy-link-to": `${originOf(request)}${page}` } : { "x-cauthy-links": "hosted" },
|
|
2966
2682
|
// Whose request it is: what Cauthy mails goes in this application's
|
|
2967
2683
|
// name, under its logo.
|
|
2968
|
-
...
|
|
2684
|
+
...signInClientNow() ? { "x-cauthy-client-id": signInClientNow() } : {},
|
|
2969
2685
|
...await passedOn(request),
|
|
2970
2686
|
...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
|
|
2971
|
-
// The person asked to stay signed in: Cauthy keeps the session long if the
|
|
2687
|
+
// The person asked to stay signed in: Cauthy keeps the session long if the workspace allows it.
|
|
2972
2688
|
...remember ? { "x-cauthy-remember": "1" } : {},
|
|
2973
2689
|
// The answer to the workspace's Turnstile challenge, from the
|
|
2974
2690
|
// person's browser: Cauthy checks it at the doors that ask for it.
|
|
@@ -3109,7 +2825,7 @@ function createCauthy(given = {}) {
|
|
|
3109
2825
|
}
|
|
3110
2826
|
if (route === "/look" && !post) {
|
|
3111
2827
|
if (lookCache === null || lookCache.until < Date.now()) {
|
|
3112
|
-
const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(
|
|
2828
|
+
const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(signInClientNow())}`);
|
|
3113
2829
|
lookCache = { until: Date.now() + LOOK_TTL_MS, look: res.error ? {} : res.data ?? {} };
|
|
3114
2830
|
}
|
|
3115
2831
|
return json(lookCache.look);
|
|
@@ -3134,7 +2850,7 @@ function createCauthy(given = {}) {
|
|
|
3134
2850
|
signUp: settings.data?.allow_signup !== false && settings.data?.signup?.mode !== "invite_only",
|
|
3135
2851
|
// The application's system makes an organization per sign-up: the form asks the company's name.
|
|
3136
2852
|
...settings.data?.signup?.organization?.ask === true ? { organization: { ask: true, required: settings.data.signup.organization.required === true } } : {},
|
|
3137
|
-
// Whether the person may ask to stay signed in: the
|
|
2853
|
+
// Whether the person may ask to stay signed in: the workspace's setting in Cauthy (persistent_sessions).
|
|
3138
2854
|
...typeof settings.data?.persistent_sessions === "boolean" ? { persistentSessions: settings.data.persistent_sessions } : {},
|
|
3139
2855
|
// A sign-in link needs a page of the application to open.
|
|
3140
2856
|
magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
|
|
@@ -3298,7 +3014,7 @@ function createCauthy(given = {}) {
|
|
|
3298
3014
|
let path = "/v1/auth/me/organizations";
|
|
3299
3015
|
if (route !== "/me/organizations") {
|
|
3300
3016
|
const id = url.searchParams.get("id") ?? "";
|
|
3301
|
-
if (!
|
|
3017
|
+
if (!UUID2.test(id)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
|
|
3302
3018
|
path += `/${id}/${route.endsWith("/roles") ? "roles" : "members"}`;
|
|
3303
3019
|
} else if (url.searchParams.get("system")) {
|
|
3304
3020
|
path += `?system=${encodeURIComponent(url.searchParams.get("system"))}`;
|
|
@@ -3359,8 +3075,7 @@ function createCauthy(given = {}) {
|
|
|
3359
3075
|
if (route === "/sign-out") {
|
|
3360
3076
|
const { had, value } = await read(request);
|
|
3361
3077
|
if (value !== null) {
|
|
3362
|
-
|
|
3363
|
-
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);
|
|
3364
3079
|
orgCache.delete(value.at);
|
|
3365
3080
|
}
|
|
3366
3081
|
const h = new Headers();
|
|
@@ -3587,7 +3302,7 @@ function createCauthy(given = {}) {
|
|
|
3587
3302
|
}
|
|
3588
3303
|
if (route === "/me/organizations/rename" || route === "/me/organizations/archive" || route === "/me/organizations/delete") {
|
|
3589
3304
|
const org = typeof b.organizationId === "string" ? b.organizationId : "";
|
|
3590
|
-
if (!
|
|
3305
|
+
if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
|
|
3591
3306
|
const path = `/v1/auth/me/organizations/${org}`;
|
|
3592
3307
|
if (route === "/me/organizations/rename") {
|
|
3593
3308
|
const bad = checkOrganizationName(b.name);
|
|
@@ -3604,7 +3319,7 @@ function createCauthy(given = {}) {
|
|
|
3604
3319
|
}
|
|
3605
3320
|
if (route.startsWith("/me/organizations/members/")) {
|
|
3606
3321
|
const org = typeof b.organizationId === "string" ? b.organizationId : "";
|
|
3607
|
-
if (!
|
|
3322
|
+
if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
|
|
3608
3323
|
const base = `/v1/auth/me/organizations/${org}/members`;
|
|
3609
3324
|
if (route === "/me/organizations/members/add") {
|
|
3610
3325
|
const bad = checkEmail(b.email);
|
|
@@ -3615,7 +3330,7 @@ function createCauthy(given = {}) {
|
|
|
3615
3330
|
email: String(b.email).trim(),
|
|
3616
3331
|
role,
|
|
3617
3332
|
name: typeof b.name === "string" ? b.name.trim() : "",
|
|
3618
|
-
client_id:
|
|
3333
|
+
client_id: signInClientNow(),
|
|
3619
3334
|
only_new: true,
|
|
3620
3335
|
...typeof b.locale === "string" && b.locale ? { locale: b.locale } : {}
|
|
3621
3336
|
}, pages.acceptInvite, mine.token);
|
|
@@ -3624,7 +3339,7 @@ function createCauthy(given = {}) {
|
|
|
3624
3339
|
return json({ status: "added", member: orgMemberOf(res.data) }, 200, mine.headers);
|
|
3625
3340
|
}
|
|
3626
3341
|
const user = typeof b.userId === "string" ? b.userId : "";
|
|
3627
|
-
if (!
|
|
3342
|
+
if (!UUID2.test(user)) return problem(400, "invalid_member", "Say which member.", void 0, mine.headers);
|
|
3628
3343
|
if (route === "/me/organizations/members/role") {
|
|
3629
3344
|
const role = typeof b.role === "string" ? b.role : "";
|
|
3630
3345
|
if (role === "") return problem(400, "invalid_role", "Choose a role.", "role", mine.headers);
|
|
@@ -3639,7 +3354,7 @@ function createCauthy(given = {}) {
|
|
|
3639
3354
|
return json({ status: "removed", userId: user }, 200, mine.headers);
|
|
3640
3355
|
}
|
|
3641
3356
|
if (route === "/me/organizations/members/invite") {
|
|
3642
|
-
const res = await ask(request, "POST", `${base}/${user}/invite`, { client_id:
|
|
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);
|
|
3643
3358
|
if (res.error) return refused(res);
|
|
3644
3359
|
return json({ status: "sent", userId: user }, 200, mine.headers);
|
|
3645
3360
|
}
|
|
@@ -3757,18 +3472,15 @@ function createCauthy(given = {}) {
|
|
|
3757
3472
|
const url = new URL(request.url);
|
|
3758
3473
|
const inside = url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
|
|
3759
3474
|
const route = inside ? withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/" : null;
|
|
3760
|
-
const published3 = pact !== void 0 && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD");
|
|
3761
3475
|
if (identity && route !== null) {
|
|
3762
3476
|
const unready = await identityGate();
|
|
3763
3477
|
if (unready) return finish(unready, route);
|
|
3764
3478
|
}
|
|
3765
3479
|
try {
|
|
3766
|
-
return finish(await serve(request), route
|
|
3480
|
+
return finish(await serve(request), route);
|
|
3767
3481
|
} catch (err) {
|
|
3768
3482
|
if (err instanceof UnknownOriginError) return finish(problem(400, "unknown_origin", err.message), route);
|
|
3769
|
-
|
|
3770
|
-
const wantsPage = request.method === "GET" && (request.headers.get("sec-fetch-mode") ?? "navigate") === "navigate";
|
|
3771
|
-
return finish(wantsPage ? new Response("Sign-in refused (email_not_confirmed). Confirm your email address first.", { status: 403, headers: { "content-type": "text/plain; charset=utf-8" } }) : problem(403, "email_not_confirmed", "Confirm your email address first."), route);
|
|
3483
|
+
throw err;
|
|
3772
3484
|
}
|
|
3773
3485
|
}
|
|
3774
3486
|
async function identityGate() {
|
|
@@ -3792,7 +3504,6 @@ function createCauthy(given = {}) {
|
|
|
3792
3504
|
}
|
|
3793
3505
|
async function serve(request) {
|
|
3794
3506
|
const url = new URL(request.url);
|
|
3795
|
-
if (pact && url.pathname === contract?.jwksPath && (request.method === "GET" || request.method === "HEAD")) return pact.jwks();
|
|
3796
3507
|
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return new Response("Not found", { status: 404 });
|
|
3797
3508
|
const route = withoutTrailingSlashes(url.pathname.slice(basePath.length)) || "/";
|
|
3798
3509
|
const get = request.method === "GET" || request.method === "HEAD";
|
|
@@ -3816,7 +3527,7 @@ function createCauthy(given = {}) {
|
|
|
3816
3527
|
return redirect(back, r.cookies);
|
|
3817
3528
|
}
|
|
3818
3529
|
if (route === "/account" && get) {
|
|
3819
|
-
const q = new URLSearchParams({ client_id:
|
|
3530
|
+
const q = new URLSearchParams({ client_id: signInClientNow() });
|
|
3820
3531
|
const back = localPath(url.searchParams.get("return_to"), "");
|
|
3821
3532
|
if (back !== "") q.set("return_to", `${originOf(request)}${back}`);
|
|
3822
3533
|
const locale = url.searchParams.get("locale");
|
|
@@ -3835,7 +3546,7 @@ function createCauthy(given = {}) {
|
|
|
3835
3546
|
const asker = await signedInAs(request);
|
|
3836
3547
|
if ("response" in asker) return asker.response;
|
|
3837
3548
|
const org = typeof b.organizationId === "string" ? b.organizationId : "";
|
|
3838
|
-
if (!
|
|
3549
|
+
if (!UUID2.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
|
|
3839
3550
|
const named = (v) => typeof v === "string" && v !== "" && v.length <= 200;
|
|
3840
3551
|
if (!named(b.resource) || !named(b.action)) return problem(400, "bad_request", "Say which resource and action.", void 0, asker.headers);
|
|
3841
3552
|
const attributes2 = b.attributes;
|
|
@@ -3858,8 +3569,7 @@ function createCauthy(given = {}) {
|
|
|
3858
3569
|
}
|
|
3859
3570
|
}
|
|
3860
3571
|
async function protect(request) {
|
|
3861
|
-
const
|
|
3862
|
-
const value = read1.value !== null && unverified(read1.value.u) ? null : read1.value;
|
|
3572
|
+
const { value } = await read(request);
|
|
3863
3573
|
if (value !== null && fresh(value)) return { session: toSession(value) };
|
|
3864
3574
|
const url = new URL(request.url);
|
|
3865
3575
|
const state = value === null ? "none" : "expired";
|
|
@@ -3872,85 +3582,36 @@ function createCauthy(given = {}) {
|
|
|
3872
3582
|
return { response: redirect(to) };
|
|
3873
3583
|
}
|
|
3874
3584
|
const replay = given.webhookReplay ?? memoryReplayStore();
|
|
3875
|
-
let lateSelf;
|
|
3876
3585
|
const tokenVerifier = (o = {}) => {
|
|
3877
|
-
if (
|
|
3878
|
-
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3879
|
-
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3880
|
-
}
|
|
3586
|
+
if (identity) {
|
|
3881
3587
|
let built;
|
|
3882
3588
|
return async (from) => {
|
|
3883
|
-
const id = await
|
|
3589
|
+
const id = await identity.ready();
|
|
3884
3590
|
if (built?.from !== id) {
|
|
3885
|
-
built = { from: id, verify: createTokenVerifier({
|
|
3886
|
-
issuer,
|
|
3887
|
-
tenantId: contract?.tenantId ?? id.tenant.id,
|
|
3888
|
-
audience: id.audience,
|
|
3889
|
-
fetch: given.fetch,
|
|
3890
|
-
...contract ? { clientId: contract.clientId } : {},
|
|
3891
|
-
...o
|
|
3892
|
-
}) };
|
|
3591
|
+
built = { from: id, verify: createTokenVerifier({ issuer, tenantId: id.tenant.id, audience: id.audience, fetch: given.fetch, ...o }) };
|
|
3893
3592
|
}
|
|
3894
3593
|
return built.verify(from);
|
|
3895
3594
|
};
|
|
3896
3595
|
}
|
|
3897
|
-
if (contract && o.clientId !== void 0 && o.clientId !== contract.clientId) {
|
|
3898
|
-
throw new CauthyConfigError(`clientId is the contract's (${contract.clientId}): remove it from the call`);
|
|
3899
|
-
}
|
|
3900
3596
|
return createTokenVerifier({
|
|
3901
3597
|
issuer,
|
|
3902
|
-
tenantId:
|
|
3598
|
+
tenantId: given.tenantId,
|
|
3903
3599
|
audience: given.audience,
|
|
3904
3600
|
fetch: given.fetch,
|
|
3905
|
-
...contract ? { clientId: contract.clientId } : {},
|
|
3906
3601
|
...o
|
|
3907
3602
|
});
|
|
3908
3603
|
};
|
|
3909
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 });
|
|
3910
|
-
async function keyJwks() {
|
|
3911
|
-
const s = await fromKey.signer;
|
|
3912
|
-
const keys2 = [{ ...s.publicJwk, kid: s.kid, alg: "EdDSA", use: "sig" }];
|
|
3913
|
-
if (fromKey.previousJwk) keys2.push({ ...await fromKey.previousJwk, alg: "EdDSA", use: "sig" });
|
|
3914
|
-
if (ownKeys) keys2.push(...(await ownKeys.published()).keys);
|
|
3915
|
-
return new Response(JSON.stringify({ keys: keys2 }), {
|
|
3916
|
-
headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
|
|
3917
|
-
});
|
|
3918
|
-
}
|
|
3919
|
-
const noStore = () => {
|
|
3920
|
-
throw new CauthyConfigError("on CAUTHY_KEY the application keeps no key of its own: make a new key on the application's card in Cauthy's dashboard");
|
|
3921
|
-
};
|
|
3922
|
-
const keyOnly = {
|
|
3923
|
-
current: async () => noStore(),
|
|
3924
|
-
published: async () => ({ keys: [] }),
|
|
3925
|
-
rotate: async () => noStore(),
|
|
3926
|
-
reseal: async () => 0
|
|
3927
|
-
};
|
|
3928
|
-
const pact = contract && fromKey ? {
|
|
3929
|
-
contract,
|
|
3930
|
-
keys: ownKeys ?? keyOnly,
|
|
3931
|
-
jwks: keyJwks,
|
|
3932
|
-
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3933
|
-
} : contract && ownKeys ? {
|
|
3934
|
-
contract,
|
|
3935
|
-
keys: ownKeys,
|
|
3936
|
-
async jwks() {
|
|
3937
|
-
return new Response(JSON.stringify(await ownKeys.published()), {
|
|
3938
|
-
headers: { "content-type": "application/json; charset=utf-8", "cache-control": `public, max-age=${JWKS_MAX_AGE_S}` }
|
|
3939
|
-
});
|
|
3940
|
-
},
|
|
3941
|
-
status: () => ownSystem.request("GET", `/v1/pact/status?system=${encodeURIComponent(contract.system)}`)
|
|
3942
|
-
} : void 0;
|
|
3943
3605
|
const configured = () => ({
|
|
3944
3606
|
issuer,
|
|
3945
3607
|
audience: given.audience ?? env3("CAUTHY_AUDIENCE") ?? "",
|
|
3946
|
-
tenant: { id:
|
|
3608
|
+
tenant: { id: given.tenantId ?? env3("CAUTHY_TENANT_ID") ?? "", slug: tenant },
|
|
3947
3609
|
system,
|
|
3948
3610
|
client_id: clientId,
|
|
3949
3611
|
sign_in_client_id: clientId,
|
|
3950
3612
|
app_urls: baseURL ? [withoutTrailingSlashes(baseURL)] : [],
|
|
3951
3613
|
callback_path: `${basePath}/callback`,
|
|
3952
|
-
key: null
|
|
3953
|
-
contract: contract ? { link: contract.link, system_proof: contract.systemProof, require_verified_email: contract.requireVerifiedEmail } : null
|
|
3614
|
+
key: null
|
|
3954
3615
|
});
|
|
3955
3616
|
return {
|
|
3956
3617
|
handler,
|
|
@@ -3972,7 +3633,6 @@ function createCauthy(given = {}) {
|
|
|
3972
3633
|
check,
|
|
3973
3634
|
toPublic,
|
|
3974
3635
|
urls,
|
|
3975
|
-
...pact ? { pact } : {},
|
|
3976
3636
|
options: { issuer, clientId, basePath, afterSignIn, afterSignOut, cookieName }
|
|
3977
3637
|
};
|
|
3978
3638
|
}
|
|
@@ -4057,7 +3717,6 @@ async function connectSystem(o) {
|
|
|
4057
3717
|
KEY_REPLACES,
|
|
4058
3718
|
MIN_PASSWORD,
|
|
4059
3719
|
PROTOCOL_VERSION,
|
|
4060
|
-
STANDARD_VARIABLES,
|
|
4061
3720
|
can,
|
|
4062
3721
|
checkCode,
|
|
4063
3722
|
checkEmail,
|
|
@@ -4073,7 +3732,6 @@ async function connectSystem(o) {
|
|
|
4073
3732
|
definePolicy,
|
|
4074
3733
|
describePasswordPolicy,
|
|
4075
3734
|
explainPasswordProblems,
|
|
4076
|
-
failClosed,
|
|
4077
3735
|
memoryReplayStore,
|
|
4078
3736
|
parseKey,
|
|
4079
3737
|
passwordPolicyOf,
|
|
@@ -4084,7 +3742,6 @@ async function connectSystem(o) {
|
|
|
4084
3742
|
pushCatalog,
|
|
4085
3743
|
pushPolicy,
|
|
4086
3744
|
pushTheme,
|
|
4087
|
-
readContract,
|
|
4088
3745
|
remoteKeys,
|
|
4089
3746
|
themeFromStackmyth,
|
|
4090
3747
|
tokensFromStackmyth,
|