cauthy-auth 0.2.0 → 0.4.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/README.md +12 -1
- package/dist/{cauthy-BrIMaoj1.d.mts → cauthy--iqtLWNa.d.ts} +7 -1
- package/dist/{cauthy-CCZc9w8r.d.ts → cauthy-Dg-DNIo9.d.mts} +7 -1
- package/dist/cli.js +129 -4
- package/dist/fastify.d.mts +3 -2
- package/dist/fastify.d.ts +3 -2
- package/dist/index.d.mts +47 -3
- package/dist/index.d.ts +47 -3
- package/dist/index.js +495 -244
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +489 -241
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +4 -2
- package/dist/react.d.ts +4 -2
- package/dist/system-CL3Q30FT.d.ts +243 -0
- package/dist/system-CdvktS7x.d.mts +243 -0
- package/dist/theme.d.mts +16 -3
- package/dist/theme.d.ts +16 -3
- package/dist/theme.js +32 -2
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +31 -2
- package/dist/theme.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/types-CYmUVWzY.d.mts +0 -106
- package/dist/types-CYmUVWzY.d.ts +0 -106
package/dist/index.js
CHANGED
|
@@ -20,10 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
CauthyApiError: () => CauthyApiError,
|
|
23
24
|
CauthyConfigError: () => CauthyConfigError,
|
|
24
25
|
CauthyWebhookError: () => CauthyWebhookError,
|
|
25
26
|
can: () => can,
|
|
27
|
+
connectSystem: () => connectSystem,
|
|
26
28
|
createCauthy: () => createCauthy,
|
|
29
|
+
createSystem: () => createSystem,
|
|
27
30
|
pushPolicy: () => pushPolicy,
|
|
28
31
|
pushTheme: () => pushTheme,
|
|
29
32
|
themeFromStackmyth: () => themeFromStackmyth,
|
|
@@ -87,8 +90,436 @@ async function unseal(sealed, key) {
|
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
// src/
|
|
93
|
+
// src/system.ts
|
|
91
94
|
var import_jose2 = require("jose");
|
|
95
|
+
|
|
96
|
+
// src/theme.ts
|
|
97
|
+
var MAX_RADIUS = 40;
|
|
98
|
+
function declarations(css) {
|
|
99
|
+
const text = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
100
|
+
const out2 = [];
|
|
101
|
+
const within = [];
|
|
102
|
+
let chunk = "";
|
|
103
|
+
let quote = "";
|
|
104
|
+
let depth = 0;
|
|
105
|
+
const flush = () => {
|
|
106
|
+
const m = /^\s*(--[\w-]+)\s*:\s*([\s\S]+?)\s*$/.exec(chunk);
|
|
107
|
+
if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
|
|
108
|
+
chunk = "";
|
|
109
|
+
};
|
|
110
|
+
for (const ch of text) {
|
|
111
|
+
if (quote) {
|
|
112
|
+
chunk += ch;
|
|
113
|
+
if (ch === quote) quote = "";
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (ch === '"' || ch === "'") {
|
|
117
|
+
quote = ch;
|
|
118
|
+
chunk += ch;
|
|
119
|
+
} else if (ch === "(") {
|
|
120
|
+
depth++;
|
|
121
|
+
chunk += ch;
|
|
122
|
+
} else if (ch === ")") {
|
|
123
|
+
depth = Math.max(0, depth - 1);
|
|
124
|
+
chunk += ch;
|
|
125
|
+
} else if (ch === "{" && depth === 0) {
|
|
126
|
+
within.push(chunk.trim().replace(/\s+/g, " "));
|
|
127
|
+
chunk = "";
|
|
128
|
+
} else if (ch === "}" && depth === 0) {
|
|
129
|
+
flush();
|
|
130
|
+
within.pop();
|
|
131
|
+
} else if (ch === ";" && depth === 0) {
|
|
132
|
+
flush();
|
|
133
|
+
} else {
|
|
134
|
+
chunk += ch;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return out2;
|
|
138
|
+
}
|
|
139
|
+
var DARK = /prefers-color-scheme:\s*dark|data-(mode|theme|color-scheme)=["']?dark|\.dark\b/i;
|
|
140
|
+
var LIGHT = /prefers-color-scheme:\s*light|data-(mode|theme|color-scheme)=["']?light|\.light\b/i;
|
|
141
|
+
var NAMED = /data-theme=["']?([\w-]+)/i;
|
|
142
|
+
function applies(d, o) {
|
|
143
|
+
for (const scope of d.within) {
|
|
144
|
+
if (o.mode === "light" && DARK.test(scope)) return false;
|
|
145
|
+
if (o.mode === "dark" && LIGHT.test(scope)) return false;
|
|
146
|
+
const named = NAMED.exec(scope);
|
|
147
|
+
if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
|
|
148
|
+
if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
function resolve(value, vars, seen = 0) {
|
|
153
|
+
if (seen > 16) return null;
|
|
154
|
+
let unresolved = false;
|
|
155
|
+
let out2 = value;
|
|
156
|
+
for (let guard = 0; guard < 32 && /var\(/.test(out2); guard++) {
|
|
157
|
+
out2 = out2.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
|
|
158
|
+
const known = vars.get(name);
|
|
159
|
+
const next = known !== void 0 ? resolve(known, vars, seen + 1) : fallback !== void 0 ? resolve(fallback.trim(), vars, seen + 1) : null;
|
|
160
|
+
if (next === null) unresolved = true;
|
|
161
|
+
return next ?? "";
|
|
162
|
+
});
|
|
163
|
+
if (unresolved) return null;
|
|
164
|
+
}
|
|
165
|
+
return /var\(/.test(out2) ? null : out2.trim();
|
|
166
|
+
}
|
|
167
|
+
var clamp01 = (n) => Math.min(1, Math.max(0, n));
|
|
168
|
+
var hex2 = (n) => Math.round(clamp01(n) * 255).toString(16).padStart(2, "0");
|
|
169
|
+
var gamma = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
170
|
+
function out(r, g, b, a = 1) {
|
|
171
|
+
if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`;
|
|
172
|
+
return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1e3) / 1e3})`;
|
|
173
|
+
}
|
|
174
|
+
var alpha = (s) => s === void 0 ? 1 : s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s);
|
|
175
|
+
var KEYWORDS = { white: "#ffffff", black: "#000000" };
|
|
176
|
+
function toColor(value) {
|
|
177
|
+
const v = value.trim().toLowerCase();
|
|
178
|
+
if (KEYWORDS[v]) return KEYWORDS[v];
|
|
179
|
+
if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v;
|
|
180
|
+
let m = /^rgba?\(\s*([\d.]+%?)[\s,]+([\d.]+%?)[\s,]+([\d.]+%?)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
181
|
+
if (m) {
|
|
182
|
+
const ch = (s) => s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s) / 255;
|
|
183
|
+
return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]));
|
|
184
|
+
}
|
|
185
|
+
m = /^hsla?\(\s*([\d.]+)(?:deg)?[\s,]+([\d.]+)%[\s,]+([\d.]+)%(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
186
|
+
if (m) {
|
|
187
|
+
const h = (parseFloat(m[1]) % 360 + 360) % 360;
|
|
188
|
+
const s = parseFloat(m[2]) / 100;
|
|
189
|
+
const l = parseFloat(m[3]) / 100;
|
|
190
|
+
const f = (n) => {
|
|
191
|
+
const k = (n + h / 30) % 12;
|
|
192
|
+
return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
|
193
|
+
};
|
|
194
|
+
return out(f(0), f(8), f(4), alpha(m[4]));
|
|
195
|
+
}
|
|
196
|
+
m = /^oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
197
|
+
if (m) {
|
|
198
|
+
const L = m[1].endsWith("%") ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
|
|
199
|
+
const C = m[2].endsWith("%") ? parseFloat(m[2]) / 100 * 0.4 : parseFloat(m[2]);
|
|
200
|
+
const H = parseFloat(m[3]) * Math.PI / 180;
|
|
201
|
+
const a = C * Math.cos(H);
|
|
202
|
+
const b = C * Math.sin(H);
|
|
203
|
+
const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
|
|
204
|
+
const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
|
|
205
|
+
const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3);
|
|
206
|
+
return out(
|
|
207
|
+
gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),
|
|
208
|
+
gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),
|
|
209
|
+
gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),
|
|
210
|
+
alpha(m[4])
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
function toRadius(value) {
|
|
216
|
+
const m = /^([\d.]+)(px|rem|em)?$/.exec(value.trim());
|
|
217
|
+
if (!m) return null;
|
|
218
|
+
const px = parseFloat(m[1]) * (m[2] === "rem" || m[2] === "em" ? 16 : 1);
|
|
219
|
+
return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null;
|
|
220
|
+
}
|
|
221
|
+
function toFont(value) {
|
|
222
|
+
const v = value.trim();
|
|
223
|
+
return /^[A-Za-z0-9 ,'"_-]{1,200}$/.test(v) && (v.match(/"/g)?.length ?? 0) % 2 === 0 && (v.match(/'/g)?.length ?? 0) % 2 === 0 ? v : null;
|
|
224
|
+
}
|
|
225
|
+
var COLORS = [
|
|
226
|
+
["background", ["--sm-bg-surface", "--sm-surface"]],
|
|
227
|
+
["surface", ["--sm-bg-surface-raised", "--sm-surface-raised"]],
|
|
228
|
+
["text", ["--sm-text-primary", "--sm-surface-text"]],
|
|
229
|
+
["textMuted", ["--sm-text-secondary"]],
|
|
230
|
+
["border", ["--sm-border-default", "--sm-surface-border"]],
|
|
231
|
+
["primary", ["--sm-primary", "--sm-bg-interactive"]],
|
|
232
|
+
["primaryText", ["--sm-primary-text", "--sm-text-inverse"]],
|
|
233
|
+
["accent", ["--sm-text-brand", "--sm-border-focus", "--sm-accent"]],
|
|
234
|
+
["error", ["--sm-error-soft-text", "--sm-error"]],
|
|
235
|
+
["errorBackground", ["--sm-error-soft"]],
|
|
236
|
+
["success", ["--sm-success-soft-text", "--sm-success"]],
|
|
237
|
+
["successBackground", ["--sm-success-soft"]]
|
|
238
|
+
];
|
|
239
|
+
function themeFromStackmyth(css, options = {}) {
|
|
240
|
+
const o = { mode: options.mode ?? "light", theme: options.theme };
|
|
241
|
+
const vars = /* @__PURE__ */ new Map();
|
|
242
|
+
for (const sheet of Array.isArray(css) ? css : [css]) {
|
|
243
|
+
for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value);
|
|
244
|
+
}
|
|
245
|
+
const theme = {};
|
|
246
|
+
const warnings = [];
|
|
247
|
+
const read = (tokens, to, what) => {
|
|
248
|
+
for (const token of tokens) {
|
|
249
|
+
const raw = vars.get(token);
|
|
250
|
+
if (raw === void 0) continue;
|
|
251
|
+
const value = resolve(raw, vars);
|
|
252
|
+
const got = value === null ? null : to(value);
|
|
253
|
+
if (got !== null) return got;
|
|
254
|
+
warnings.push(`${token}: "${raw}" is not ${what} Cauthy can take`);
|
|
255
|
+
}
|
|
256
|
+
return void 0;
|
|
257
|
+
};
|
|
258
|
+
for (const [field, tokens] of COLORS) {
|
|
259
|
+
const c = read(tokens, toColor, "a colour");
|
|
260
|
+
if (c !== void 0) theme[field] = c;
|
|
261
|
+
}
|
|
262
|
+
const radius = read(["--sm-radius-container", "--sm-radius-md"], toRadius, "a length");
|
|
263
|
+
if (radius !== void 0) theme.radius = radius;
|
|
264
|
+
const button = read(["--sm-radius-interactive", "--sm-radius-sm"], toRadius, "a length");
|
|
265
|
+
if (button !== void 0) theme.buttonRadius = button;
|
|
266
|
+
const font = read(["--sm-font-family"], toFont, "a font list");
|
|
267
|
+
if (font !== void 0) theme.fontFamily = font;
|
|
268
|
+
if (options.logoUrl) theme.logoUrl = options.logoUrl;
|
|
269
|
+
if (vars.size === 0) warnings.push("no custom properties were found: is this the stylesheet that declares the --sm-* tokens?");
|
|
270
|
+
else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
|
|
271
|
+
return { theme, warnings };
|
|
272
|
+
}
|
|
273
|
+
function toWire(theme) {
|
|
274
|
+
const names = {
|
|
275
|
+
background: "background",
|
|
276
|
+
surface: "surface",
|
|
277
|
+
text: "text",
|
|
278
|
+
textMuted: "text_muted",
|
|
279
|
+
border: "border",
|
|
280
|
+
primary: "primary",
|
|
281
|
+
primaryText: "primary_text",
|
|
282
|
+
accent: "accent",
|
|
283
|
+
error: "error",
|
|
284
|
+
errorBackground: "error_background",
|
|
285
|
+
success: "success",
|
|
286
|
+
successBackground: "success_background",
|
|
287
|
+
radius: "radius",
|
|
288
|
+
buttonRadius: "button_radius",
|
|
289
|
+
fontFamily: "font_family",
|
|
290
|
+
logoUrl: "logo_url",
|
|
291
|
+
fonts: "fonts"
|
|
292
|
+
};
|
|
293
|
+
const wire = {};
|
|
294
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
295
|
+
if (v === void 0 || v === "" || Array.isArray(v) && v.length === 0) continue;
|
|
296
|
+
wire[names[k]] = v;
|
|
297
|
+
}
|
|
298
|
+
return wire;
|
|
299
|
+
}
|
|
300
|
+
function fromWire(wire) {
|
|
301
|
+
const names = {
|
|
302
|
+
background: "background",
|
|
303
|
+
surface: "surface",
|
|
304
|
+
text: "text",
|
|
305
|
+
text_muted: "textMuted",
|
|
306
|
+
border: "border",
|
|
307
|
+
primary: "primary",
|
|
308
|
+
primary_text: "primaryText",
|
|
309
|
+
accent: "accent",
|
|
310
|
+
error: "error",
|
|
311
|
+
error_background: "errorBackground",
|
|
312
|
+
success: "success",
|
|
313
|
+
success_background: "successBackground",
|
|
314
|
+
radius: "radius",
|
|
315
|
+
button_radius: "buttonRadius",
|
|
316
|
+
font_family: "fontFamily",
|
|
317
|
+
logo_url: "logoUrl",
|
|
318
|
+
fonts: "fonts"
|
|
319
|
+
};
|
|
320
|
+
const theme = {};
|
|
321
|
+
for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== void 0 && v !== null && v !== "") theme[names[k]] = v;
|
|
322
|
+
return theme;
|
|
323
|
+
}
|
|
324
|
+
async function pushTheme(o) {
|
|
325
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
326
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
327
|
+
const clientId = o.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
|
|
328
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
329
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
330
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
331
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
|
|
332
|
+
}
|
|
333
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {
|
|
334
|
+
method: "PUT",
|
|
335
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
336
|
+
body: JSON.stringify(toWire(o.theme))
|
|
337
|
+
});
|
|
338
|
+
if (!res.ok) {
|
|
339
|
+
const body = await res.json().catch(() => null);
|
|
340
|
+
throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/types.ts
|
|
345
|
+
var CauthyConfigError = class extends Error {
|
|
346
|
+
constructor(message) {
|
|
347
|
+
super(`cauthy-auth: ${message}`);
|
|
348
|
+
this.name = "CauthyConfigError";
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// src/system.ts
|
|
353
|
+
var ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
354
|
+
var SKEW_MS = 3e4;
|
|
355
|
+
var CauthyApiError = class extends Error {
|
|
356
|
+
constructor(status, code, message) {
|
|
357
|
+
super(`cauthy-auth: ${message}`);
|
|
358
|
+
this.status = status;
|
|
359
|
+
this.code = code;
|
|
360
|
+
this.name = "CauthyApiError";
|
|
361
|
+
}
|
|
362
|
+
status;
|
|
363
|
+
code;
|
|
364
|
+
};
|
|
365
|
+
function env(name) {
|
|
366
|
+
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
367
|
+
}
|
|
368
|
+
function toOrganization(o) {
|
|
369
|
+
return {
|
|
370
|
+
id: o.id,
|
|
371
|
+
system: o.system,
|
|
372
|
+
name: o.name,
|
|
373
|
+
kind: o.kind,
|
|
374
|
+
externalId: o.external_id,
|
|
375
|
+
status: o.status,
|
|
376
|
+
metadata: o.metadata ?? {},
|
|
377
|
+
members: o.members,
|
|
378
|
+
createdAt: o.created_at,
|
|
379
|
+
updatedAt: o.updated_at
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function toMember(m) {
|
|
383
|
+
return { userId: m.user_id, email: m.email, name: m.name, status: m.status, role: m.role, ...m.invited === void 0 ? {} : { invited: m.invited } };
|
|
384
|
+
}
|
|
385
|
+
function asJwk(k) {
|
|
386
|
+
if (typeof k.privateKey !== "string") return k.privateKey;
|
|
387
|
+
return k.privateKey.includes("BEGIN") ? void 0 : JSON.parse(k.privateKey);
|
|
388
|
+
}
|
|
389
|
+
async function loadKey(k) {
|
|
390
|
+
const jwk = asJwk(k);
|
|
391
|
+
return jwk ? (0, import_jose2.importJWK)(jwk, "RS256") : (0, import_jose2.importPKCS8)(k.privateKey, "RS256", { extractable: true });
|
|
392
|
+
}
|
|
393
|
+
function createSystem(given = {}) {
|
|
394
|
+
const issuer = (given.issuer ?? env("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
395
|
+
const clientId = given.clientId ?? env("CAUTHY_CLIENT_ID") ?? "";
|
|
396
|
+
const clientSecret = given.clientSecret ?? env("CAUTHY_CLIENT_SECRET") ?? "";
|
|
397
|
+
const envKey = env("CAUTHY_SYSTEM_KEY");
|
|
398
|
+
const key = given.key ?? (envKey ? { privateKey: envKey, keyId: env("CAUTHY_SYSTEM_KEY_ID") ?? "" } : void 0);
|
|
399
|
+
const doFetch = (...args) => (given.fetch ?? fetch)(...args);
|
|
400
|
+
const tokenEndpoint = `${issuer}/v1/oauth/token`;
|
|
401
|
+
function configured() {
|
|
402
|
+
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
403
|
+
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
404
|
+
if (key ? key.keyId === "" : clientSecret === "") {
|
|
405
|
+
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)");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
let imported;
|
|
409
|
+
const signingKey = () => imported ??= loadKey(key);
|
|
410
|
+
async function fail(res, what) {
|
|
411
|
+
const body = await res.json().catch(() => null);
|
|
412
|
+
const first = body?.errors?.[0];
|
|
413
|
+
throw new CauthyApiError(res.status, first?.code ?? "error", `Cauthy refused ${what} (${res.status}): ${first?.message ?? res.statusText}`);
|
|
414
|
+
}
|
|
415
|
+
let held;
|
|
416
|
+
let asking;
|
|
417
|
+
async function ask() {
|
|
418
|
+
configured();
|
|
419
|
+
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: clientId });
|
|
420
|
+
if (key) {
|
|
421
|
+
const assertion = await new import_jose2.SignJWT({}).setProtectedHeader({ alg: "RS256", kid: key.keyId }).setIssuer(clientId).setSubject(clientId).setAudience(tokenEndpoint).setJti(crypto.randomUUID()).setIssuedAt().setExpirationTime("2m").sign(await signingKey());
|
|
422
|
+
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
423
|
+
form.set("client_assertion", assertion);
|
|
424
|
+
} else {
|
|
425
|
+
form.set("client_secret", clientSecret);
|
|
426
|
+
}
|
|
427
|
+
const res = await doFetch(tokenEndpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
|
|
428
|
+
if (!res.ok) await fail(res, "the application's own token");
|
|
429
|
+
const t = await res.json();
|
|
430
|
+
held = { value: t.access_token, until: Date.now() + t.expires_in * 1e3 - SKEW_MS };
|
|
431
|
+
return t.access_token;
|
|
432
|
+
}
|
|
433
|
+
function token() {
|
|
434
|
+
if (held && Date.now() < held.until) return Promise.resolve(held.value);
|
|
435
|
+
return asking ??= ask().finally(() => {
|
|
436
|
+
asking = void 0;
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async function call(method, path, body) {
|
|
440
|
+
const send = async () => doFetch(`${issuer}${path}`, {
|
|
441
|
+
method,
|
|
442
|
+
headers: { authorization: `Bearer ${await token()}`, ...body === void 0 ? {} : { "content-type": "application/json" } },
|
|
443
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
444
|
+
});
|
|
445
|
+
let res = await send();
|
|
446
|
+
if (res.status === 401 && held) {
|
|
447
|
+
held = void 0;
|
|
448
|
+
res = await send();
|
|
449
|
+
}
|
|
450
|
+
if (!res.ok) await fail(res, `${method} ${path}`);
|
|
451
|
+
return res.status === 204 ? void 0 : res.json();
|
|
452
|
+
}
|
|
453
|
+
const data = async (method, path, body) => (await call(method, path, body))?.data;
|
|
454
|
+
const id = encodeURIComponent;
|
|
455
|
+
return {
|
|
456
|
+
token,
|
|
457
|
+
request: data,
|
|
458
|
+
organizations: {
|
|
459
|
+
async list(query = {}) {
|
|
460
|
+
const q = new URLSearchParams();
|
|
461
|
+
for (const [k, v] of Object.entries(query)) if (v !== void 0) q.set(k, String(v));
|
|
462
|
+
const out2 = await call("GET", `/v1/organizations${q.size ? `?${q}` : ""}`);
|
|
463
|
+
return { items: out2.data.map(toOrganization), total: out2.total_count };
|
|
464
|
+
},
|
|
465
|
+
async create(input) {
|
|
466
|
+
return toOrganization(await data(
|
|
467
|
+
"POST",
|
|
468
|
+
"/v1/organizations",
|
|
469
|
+
{ name: input.name, kind: input.kind, external_id: input.externalId, metadata: input.metadata }
|
|
470
|
+
));
|
|
471
|
+
},
|
|
472
|
+
async get(orgId) {
|
|
473
|
+
return toOrganization(await data("GET", `/v1/organizations/${id(orgId)}`));
|
|
474
|
+
},
|
|
475
|
+
async update(orgId, patch) {
|
|
476
|
+
return toOrganization(await data("PATCH", `/v1/organizations/${id(orgId)}`, patch));
|
|
477
|
+
},
|
|
478
|
+
async remove(orgId) {
|
|
479
|
+
await call("DELETE", `/v1/organizations/${id(orgId)}`);
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
members: {
|
|
483
|
+
async list(orgId) {
|
|
484
|
+
return (await data("GET", `/v1/organizations/${id(orgId)}/members`)).map(toMember);
|
|
485
|
+
},
|
|
486
|
+
async add(orgId, m) {
|
|
487
|
+
return toMember(await data(
|
|
488
|
+
"POST",
|
|
489
|
+
`/v1/organizations/${id(orgId)}/members`,
|
|
490
|
+
{ role: m.role, user_id: m.userId, email: m.email, name: m.name, locale: m.locale, client_id: clientId }
|
|
491
|
+
));
|
|
492
|
+
},
|
|
493
|
+
async setRole(orgId, userId, role) {
|
|
494
|
+
return toMember(await data("PUT", `/v1/organizations/${id(orgId)}/members/${id(userId)}`, { role }));
|
|
495
|
+
},
|
|
496
|
+
async remove(orgId, userId) {
|
|
497
|
+
await call("DELETE", `/v1/organizations/${id(orgId)}/members/${id(userId)}`);
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
async invite(person) {
|
|
501
|
+
const u = await data("POST", "/v1/users", { ...person, client_id: clientId });
|
|
502
|
+
return { id: u.id, email: u.email };
|
|
503
|
+
},
|
|
504
|
+
theme: {
|
|
505
|
+
async get(of = clientId) {
|
|
506
|
+
return fromWire(await data("GET", `/v1/oauth/clients/${id(of)}/theme`));
|
|
507
|
+
},
|
|
508
|
+
async set(theme, of = clientId) {
|
|
509
|
+
if (theme === null) await call("DELETE", `/v1/oauth/clients/${id(of)}/theme`);
|
|
510
|
+
else await call("PUT", `/v1/oauth/clients/${id(of)}/theme`, toWire(theme));
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
async publicJwks() {
|
|
514
|
+
if (!key) return { keys: [] };
|
|
515
|
+
const { kty, n, e } = asJwk(key) ?? await (0, import_jose2.exportJWK)(await signingKey());
|
|
516
|
+
return { keys: [{ kty, n, e, kid: key.keyId, alg: "RS256", use: "sig" }] };
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/webhook.ts
|
|
522
|
+
var import_jose3 = require("jose");
|
|
92
523
|
var CauthyWebhookError = class extends Error {
|
|
93
524
|
constructor(reason) {
|
|
94
525
|
super(`cauthy-auth: webhook refused: ${reason}`);
|
|
@@ -101,15 +532,15 @@ var keys = /* @__PURE__ */ new Map();
|
|
|
101
532
|
function keysOf(issuer) {
|
|
102
533
|
let set = keys.get(issuer);
|
|
103
534
|
if (set === void 0) {
|
|
104
|
-
set = (0,
|
|
535
|
+
set = (0, import_jose3.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
|
|
105
536
|
keys.set(issuer, set);
|
|
106
537
|
}
|
|
107
538
|
return set;
|
|
108
539
|
}
|
|
109
540
|
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
110
541
|
async function verifyWebhook(request, options = {}) {
|
|
111
|
-
const
|
|
112
|
-
const issuer = (options.issuer ??
|
|
542
|
+
const env3 = typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
543
|
+
const issuer = (options.issuer ?? env3 ?? "").replace(/\/+$/, "");
|
|
113
544
|
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
114
545
|
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
115
546
|
if (!signature) throw new CauthyWebhookError("no signature");
|
|
@@ -117,7 +548,7 @@ async function verifyWebhook(request, options = {}) {
|
|
|
117
548
|
const audience = options.url ?? request.url.split("?")[0];
|
|
118
549
|
let claims;
|
|
119
550
|
try {
|
|
120
|
-
const verified = await (0,
|
|
551
|
+
const verified = await (0, import_jose3.jwtVerify)(signature, options.jwks ?? keysOf(issuer), {
|
|
121
552
|
algorithms: ["RS256"],
|
|
122
553
|
issuer,
|
|
123
554
|
audience,
|
|
@@ -158,20 +589,12 @@ async function verifyWebhook(request, options = {}) {
|
|
|
158
589
|
};
|
|
159
590
|
}
|
|
160
591
|
|
|
161
|
-
// src/types.ts
|
|
162
|
-
var CauthyConfigError = class extends Error {
|
|
163
|
-
constructor(message) {
|
|
164
|
-
super(`cauthy-auth: ${message}`);
|
|
165
|
-
this.name = "CauthyConfigError";
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
592
|
// src/cauthy.ts
|
|
170
593
|
var SESSION_DAYS = 30;
|
|
171
594
|
var FLOW_SECONDS = 600;
|
|
172
595
|
var SKEW_SECONDS = 30;
|
|
173
596
|
var ORGANIZATIONS_TTL_MS = 6e4;
|
|
174
|
-
function
|
|
597
|
+
function env2(name) {
|
|
175
598
|
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
176
599
|
}
|
|
177
600
|
function b64url(bytes) {
|
|
@@ -198,11 +621,11 @@ function redirect(location, cookies = []) {
|
|
|
198
621
|
return new Response(null, { status: 302, headers: h });
|
|
199
622
|
}
|
|
200
623
|
function createCauthy(given = {}) {
|
|
201
|
-
const issuer = (given.issuer ??
|
|
202
|
-
const clientId = given.clientId ??
|
|
203
|
-
const clientSecret = given.clientSecret ??
|
|
204
|
-
const secret = given.secret ??
|
|
205
|
-
const system = given.system ??
|
|
624
|
+
const issuer = (given.issuer ?? env2("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
625
|
+
const clientId = given.clientId ?? env2("CAUTHY_CLIENT_ID") ?? "";
|
|
626
|
+
const clientSecret = given.clientSecret ?? env2("CAUTHY_CLIENT_SECRET");
|
|
627
|
+
const secret = given.secret ?? env2("CAUTHY_SECRET") ?? "";
|
|
628
|
+
const system = given.system ?? env2("CAUTHY_SYSTEM") ?? "";
|
|
206
629
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
207
630
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
208
631
|
if (secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
@@ -471,6 +894,7 @@ function createCauthy(given = {}) {
|
|
|
471
894
|
return {
|
|
472
895
|
handler,
|
|
473
896
|
webhook,
|
|
897
|
+
system: createSystem({ issuer, clientId, clientSecret, key: given.systemKey, fetch: given.fetch }),
|
|
474
898
|
getSession,
|
|
475
899
|
status,
|
|
476
900
|
resolve: resolve2,
|
|
@@ -482,226 +906,6 @@ function createCauthy(given = {}) {
|
|
|
482
906
|
};
|
|
483
907
|
}
|
|
484
908
|
|
|
485
|
-
// src/theme.ts
|
|
486
|
-
var MAX_RADIUS = 40;
|
|
487
|
-
function declarations(css) {
|
|
488
|
-
const text = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
489
|
-
const out2 = [];
|
|
490
|
-
const within = [];
|
|
491
|
-
let chunk = "";
|
|
492
|
-
let quote = "";
|
|
493
|
-
let depth = 0;
|
|
494
|
-
const flush = () => {
|
|
495
|
-
const m = /^\s*(--[\w-]+)\s*:\s*([\s\S]+?)\s*$/.exec(chunk);
|
|
496
|
-
if (m) out2.push({ name: m[1], value: m[2].replace(/\s+/g, " ").replace(/\s*!important$/, ""), within: [...within] });
|
|
497
|
-
chunk = "";
|
|
498
|
-
};
|
|
499
|
-
for (const ch of text) {
|
|
500
|
-
if (quote) {
|
|
501
|
-
chunk += ch;
|
|
502
|
-
if (ch === quote) quote = "";
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
if (ch === '"' || ch === "'") {
|
|
506
|
-
quote = ch;
|
|
507
|
-
chunk += ch;
|
|
508
|
-
} else if (ch === "(") {
|
|
509
|
-
depth++;
|
|
510
|
-
chunk += ch;
|
|
511
|
-
} else if (ch === ")") {
|
|
512
|
-
depth = Math.max(0, depth - 1);
|
|
513
|
-
chunk += ch;
|
|
514
|
-
} else if (ch === "{" && depth === 0) {
|
|
515
|
-
within.push(chunk.trim().replace(/\s+/g, " "));
|
|
516
|
-
chunk = "";
|
|
517
|
-
} else if (ch === "}" && depth === 0) {
|
|
518
|
-
flush();
|
|
519
|
-
within.pop();
|
|
520
|
-
} else if (ch === ";" && depth === 0) {
|
|
521
|
-
flush();
|
|
522
|
-
} else {
|
|
523
|
-
chunk += ch;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return out2;
|
|
527
|
-
}
|
|
528
|
-
var DARK = /prefers-color-scheme:\s*dark|data-(mode|theme|color-scheme)=["']?dark|\.dark\b/i;
|
|
529
|
-
var LIGHT = /prefers-color-scheme:\s*light|data-(mode|theme|color-scheme)=["']?light|\.light\b/i;
|
|
530
|
-
var NAMED = /data-theme=["']?([\w-]+)/i;
|
|
531
|
-
function applies(d, o) {
|
|
532
|
-
for (const scope of d.within) {
|
|
533
|
-
if (o.mode === "light" && DARK.test(scope)) return false;
|
|
534
|
-
if (o.mode === "dark" && LIGHT.test(scope)) return false;
|
|
535
|
-
const named = NAMED.exec(scope);
|
|
536
|
-
if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
|
|
537
|
-
if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
|
|
538
|
-
}
|
|
539
|
-
return true;
|
|
540
|
-
}
|
|
541
|
-
function resolve(value, vars, seen = 0) {
|
|
542
|
-
if (seen > 16) return null;
|
|
543
|
-
let unresolved = false;
|
|
544
|
-
let out2 = value;
|
|
545
|
-
for (let guard = 0; guard < 32 && /var\(/.test(out2); guard++) {
|
|
546
|
-
out2 = out2.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
|
|
547
|
-
const known = vars.get(name);
|
|
548
|
-
const next = known !== void 0 ? resolve(known, vars, seen + 1) : fallback !== void 0 ? resolve(fallback.trim(), vars, seen + 1) : null;
|
|
549
|
-
if (next === null) unresolved = true;
|
|
550
|
-
return next ?? "";
|
|
551
|
-
});
|
|
552
|
-
if (unresolved) return null;
|
|
553
|
-
}
|
|
554
|
-
return /var\(/.test(out2) ? null : out2.trim();
|
|
555
|
-
}
|
|
556
|
-
var clamp01 = (n) => Math.min(1, Math.max(0, n));
|
|
557
|
-
var hex2 = (n) => Math.round(clamp01(n) * 255).toString(16).padStart(2, "0");
|
|
558
|
-
var gamma = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
559
|
-
function out(r, g, b, a = 1) {
|
|
560
|
-
if (a >= 1) return `#${hex2(r)}${hex2(g)}${hex2(b)}`;
|
|
561
|
-
return `rgba(${Math.round(clamp01(r) * 255)}, ${Math.round(clamp01(g) * 255)}, ${Math.round(clamp01(b) * 255)}, ${Math.round(clamp01(a) * 1e3) / 1e3})`;
|
|
562
|
-
}
|
|
563
|
-
var alpha = (s) => s === void 0 ? 1 : s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s);
|
|
564
|
-
var KEYWORDS = { white: "#ffffff", black: "#000000" };
|
|
565
|
-
function toColor(value) {
|
|
566
|
-
const v = value.trim().toLowerCase();
|
|
567
|
-
if (KEYWORDS[v]) return KEYWORDS[v];
|
|
568
|
-
if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(v)) return v;
|
|
569
|
-
let m = /^rgba?\(\s*([\d.]+%?)[\s,]+([\d.]+%?)[\s,]+([\d.]+%?)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
570
|
-
if (m) {
|
|
571
|
-
const ch = (s) => s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s) / 255;
|
|
572
|
-
return out(ch(m[1]), ch(m[2]), ch(m[3]), alpha(m[4]));
|
|
573
|
-
}
|
|
574
|
-
m = /^hsla?\(\s*([\d.]+)(?:deg)?[\s,]+([\d.]+)%[\s,]+([\d.]+)%(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
575
|
-
if (m) {
|
|
576
|
-
const h = (parseFloat(m[1]) % 360 + 360) % 360;
|
|
577
|
-
const s = parseFloat(m[2]) / 100;
|
|
578
|
-
const l = parseFloat(m[3]) / 100;
|
|
579
|
-
const f = (n) => {
|
|
580
|
-
const k = (n + h / 30) % 12;
|
|
581
|
-
return l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
|
582
|
-
};
|
|
583
|
-
return out(f(0), f(8), f(4), alpha(m[4]));
|
|
584
|
-
}
|
|
585
|
-
m = /^oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/.exec(v);
|
|
586
|
-
if (m) {
|
|
587
|
-
const L = m[1].endsWith("%") ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
|
|
588
|
-
const C = m[2].endsWith("%") ? parseFloat(m[2]) / 100 * 0.4 : parseFloat(m[2]);
|
|
589
|
-
const H = parseFloat(m[3]) * Math.PI / 180;
|
|
590
|
-
const a = C * Math.cos(H);
|
|
591
|
-
const b = C * Math.sin(H);
|
|
592
|
-
const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
|
|
593
|
-
const mm = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
|
|
594
|
-
const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3);
|
|
595
|
-
return out(
|
|
596
|
-
gamma(4.0767416621 * l - 3.3077115913 * mm + 0.2309699292 * s),
|
|
597
|
-
gamma(-1.2684380046 * l + 2.6097574011 * mm - 0.3413193965 * s),
|
|
598
|
-
gamma(-0.0041960863 * l - 0.7034186147 * mm + 1.707614701 * s),
|
|
599
|
-
alpha(m[4])
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
return null;
|
|
603
|
-
}
|
|
604
|
-
function toRadius(value) {
|
|
605
|
-
const m = /^([\d.]+)(px|rem|em)?$/.exec(value.trim());
|
|
606
|
-
if (!m) return null;
|
|
607
|
-
const px = parseFloat(m[1]) * (m[2] === "rem" || m[2] === "em" ? 16 : 1);
|
|
608
|
-
return Number.isFinite(px) ? Math.min(MAX_RADIUS, Math.max(0, Math.round(px))) : null;
|
|
609
|
-
}
|
|
610
|
-
function toFont(value) {
|
|
611
|
-
const v = value.trim();
|
|
612
|
-
return /^[A-Za-z0-9 ,'"_-]{1,200}$/.test(v) && (v.match(/"/g)?.length ?? 0) % 2 === 0 && (v.match(/'/g)?.length ?? 0) % 2 === 0 ? v : null;
|
|
613
|
-
}
|
|
614
|
-
var COLORS = [
|
|
615
|
-
["background", ["--sm-bg-surface", "--sm-surface"]],
|
|
616
|
-
["surface", ["--sm-bg-surface-raised", "--sm-surface-raised"]],
|
|
617
|
-
["text", ["--sm-text-primary", "--sm-surface-text"]],
|
|
618
|
-
["textMuted", ["--sm-text-secondary"]],
|
|
619
|
-
["border", ["--sm-border-default", "--sm-surface-border"]],
|
|
620
|
-
["primary", ["--sm-primary", "--sm-bg-interactive"]],
|
|
621
|
-
["primaryText", ["--sm-primary-text", "--sm-text-inverse"]],
|
|
622
|
-
["accent", ["--sm-text-brand", "--sm-border-focus", "--sm-accent"]],
|
|
623
|
-
["error", ["--sm-error-soft-text", "--sm-error"]],
|
|
624
|
-
["errorBackground", ["--sm-error-soft"]],
|
|
625
|
-
["success", ["--sm-success-soft-text", "--sm-success"]],
|
|
626
|
-
["successBackground", ["--sm-success-soft"]]
|
|
627
|
-
];
|
|
628
|
-
function themeFromStackmyth(css, options = {}) {
|
|
629
|
-
const o = { mode: options.mode ?? "light", theme: options.theme };
|
|
630
|
-
const vars = /* @__PURE__ */ new Map();
|
|
631
|
-
for (const sheet of Array.isArray(css) ? css : [css]) {
|
|
632
|
-
for (const d of declarations(sheet)) if (applies(d, o)) vars.set(d.name, d.value);
|
|
633
|
-
}
|
|
634
|
-
const theme = {};
|
|
635
|
-
const warnings = [];
|
|
636
|
-
const read = (tokens, to, what) => {
|
|
637
|
-
for (const token of tokens) {
|
|
638
|
-
const raw = vars.get(token);
|
|
639
|
-
if (raw === void 0) continue;
|
|
640
|
-
const value = resolve(raw, vars);
|
|
641
|
-
const got = value === null ? null : to(value);
|
|
642
|
-
if (got !== null) return got;
|
|
643
|
-
warnings.push(`${token}: "${raw}" is not ${what} Cauthy can take`);
|
|
644
|
-
}
|
|
645
|
-
return void 0;
|
|
646
|
-
};
|
|
647
|
-
for (const [field, tokens] of COLORS) {
|
|
648
|
-
const c = read(tokens, toColor, "a colour");
|
|
649
|
-
if (c !== void 0) theme[field] = c;
|
|
650
|
-
}
|
|
651
|
-
const radius = read(["--sm-radius-container", "--sm-radius-md"], toRadius, "a length");
|
|
652
|
-
if (radius !== void 0) theme.radius = radius;
|
|
653
|
-
const button = read(["--sm-radius-interactive", "--sm-radius-sm"], toRadius, "a length");
|
|
654
|
-
if (button !== void 0) theme.buttonRadius = button;
|
|
655
|
-
const font = read(["--sm-font-family"], toFont, "a font list");
|
|
656
|
-
if (font !== void 0) theme.fontFamily = font;
|
|
657
|
-
if (options.logoUrl) theme.logoUrl = options.logoUrl;
|
|
658
|
-
if (vars.size === 0) warnings.push("no custom properties were found: is this the stylesheet that declares the --sm-* tokens?");
|
|
659
|
-
else if (![...vars.keys()].some((k) => k.startsWith("--sm-"))) warnings.push("no --sm-* token was found in these stylesheets");
|
|
660
|
-
return { theme, warnings };
|
|
661
|
-
}
|
|
662
|
-
function toWire(theme) {
|
|
663
|
-
const names = {
|
|
664
|
-
background: "background",
|
|
665
|
-
surface: "surface",
|
|
666
|
-
text: "text",
|
|
667
|
-
textMuted: "text_muted",
|
|
668
|
-
border: "border",
|
|
669
|
-
primary: "primary",
|
|
670
|
-
primaryText: "primary_text",
|
|
671
|
-
accent: "accent",
|
|
672
|
-
error: "error",
|
|
673
|
-
errorBackground: "error_background",
|
|
674
|
-
success: "success",
|
|
675
|
-
successBackground: "success_background",
|
|
676
|
-
radius: "radius",
|
|
677
|
-
buttonRadius: "button_radius",
|
|
678
|
-
fontFamily: "font_family",
|
|
679
|
-
logoUrl: "logo_url"
|
|
680
|
-
};
|
|
681
|
-
const wire = {};
|
|
682
|
-
for (const [k, v] of Object.entries(theme)) if (v !== void 0 && v !== "") wire[names[k]] = v;
|
|
683
|
-
return wire;
|
|
684
|
-
}
|
|
685
|
-
async function pushTheme(o) {
|
|
686
|
-
const env2 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
687
|
-
const issuer = (o.issuer ?? env2("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
688
|
-
const clientId = o.clientId ?? env2("CAUTHY_CLIENT_ID") ?? "";
|
|
689
|
-
const tenant = o.tenant ?? env2("CAUTHY_TENANT") ?? "";
|
|
690
|
-
const token = o.token ?? env2("CAUTHY_TOKEN") ?? "";
|
|
691
|
-
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
692
|
-
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
|
|
693
|
-
}
|
|
694
|
-
const res = await (o.fetch ?? fetch)(`${issuer}/v1/oauth/clients/${encodeURIComponent(clientId)}/theme`, {
|
|
695
|
-
method: "PUT",
|
|
696
|
-
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
697
|
-
body: JSON.stringify(toWire(o.theme))
|
|
698
|
-
});
|
|
699
|
-
if (!res.ok) {
|
|
700
|
-
const body = await res.json().catch(() => null);
|
|
701
|
-
throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
909
|
// src/permissions.ts
|
|
706
910
|
function matches(entry, resource, action) {
|
|
707
911
|
const i = entry.lastIndexOf(":");
|
|
@@ -716,11 +920,11 @@ function can(organization, resource, action) {
|
|
|
716
920
|
return organization.permissions.some((p) => matches(p, resource, action));
|
|
717
921
|
}
|
|
718
922
|
async function pushPolicy(o) {
|
|
719
|
-
const
|
|
720
|
-
const issuer = (o.issuer ??
|
|
721
|
-
const system = o.system ??
|
|
722
|
-
const tenant = o.tenant ??
|
|
723
|
-
const token = o.token ??
|
|
923
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
924
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
925
|
+
const system = o.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
926
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
927
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
724
928
|
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
725
929
|
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
|
|
726
930
|
}
|
|
@@ -734,12 +938,59 @@ async function pushPolicy(o) {
|
|
|
734
938
|
throw new Error(`cauthy-auth: Cauthy refused the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
735
939
|
}
|
|
736
940
|
}
|
|
941
|
+
|
|
942
|
+
// src/connect.ts
|
|
943
|
+
function sessionSecret() {
|
|
944
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
945
|
+
let s = "";
|
|
946
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
947
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
948
|
+
}
|
|
949
|
+
async function connectSystem(o) {
|
|
950
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
951
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
952
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
953
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
954
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
955
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to connect a system`);
|
|
956
|
+
}
|
|
957
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/connect`, {
|
|
958
|
+
method: "POST",
|
|
959
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
960
|
+
body: JSON.stringify({
|
|
961
|
+
system: o.system,
|
|
962
|
+
app_url: o.appUrl,
|
|
963
|
+
name: o.name,
|
|
964
|
+
callback_path: o.callbackPath,
|
|
965
|
+
events_url: o.eventsUrl,
|
|
966
|
+
events: o.events,
|
|
967
|
+
jwks_uri: o.jwksUri
|
|
968
|
+
})
|
|
969
|
+
});
|
|
970
|
+
const body = await res.json().catch(() => null);
|
|
971
|
+
if (!res.ok || !body?.data) {
|
|
972
|
+
const why = (body?.errors ?? []).map((e) => e.message).filter(Boolean).join("; ") || res.statusText;
|
|
973
|
+
throw new Error(`cauthy-auth: Cauthy did not connect the system (${res.status}): ${why}`);
|
|
974
|
+
}
|
|
975
|
+
const d = body.data;
|
|
976
|
+
return {
|
|
977
|
+
system: d.system,
|
|
978
|
+
clientId: d.client.client_id,
|
|
979
|
+
clientSecret: d.client_secret,
|
|
980
|
+
redirectUris: d.client.redirect_uris,
|
|
981
|
+
webhook: d.webhook,
|
|
982
|
+
env: { ...d.env, CAUTHY_SECRET: sessionSecret() }
|
|
983
|
+
};
|
|
984
|
+
}
|
|
737
985
|
// Annotate the CommonJS export names for ESM import in node:
|
|
738
986
|
0 && (module.exports = {
|
|
987
|
+
CauthyApiError,
|
|
739
988
|
CauthyConfigError,
|
|
740
989
|
CauthyWebhookError,
|
|
741
990
|
can,
|
|
991
|
+
connectSystem,
|
|
742
992
|
createCauthy,
|
|
993
|
+
createSystem,
|
|
743
994
|
pushPolicy,
|
|
744
995
|
pushTheme,
|
|
745
996
|
themeFromStackmyth,
|