cauthy-auth 0.1.0 → 0.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/README.md +12 -1
- package/dist/cauthy-C5_lopMQ.d.ts +108 -0
- package/dist/cauthy-mNADGlNl.d.mts +108 -0
- package/dist/cli.js +202 -2
- package/dist/fastify.d.mts +3 -2
- package/dist/fastify.d.ts +3 -2
- package/dist/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +343 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +335 -13
- package/dist/index.mjs.map +1 -1
- package/dist/permissions-B-a3eN0h.d.mts +52 -0
- package/dist/permissions-B-a3eN0h.d.ts +52 -0
- package/dist/react.d.mts +4 -2
- package/dist/react.d.ts +4 -2
- package/dist/react.js +18 -0
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +17 -0
- package/dist/react.mjs.map +1 -1
- package/dist/system-BWLddIt2.d.mts +233 -0
- package/dist/system-BWLddIt2.d.ts +233 -0
- package/package.json +1 -1
- package/dist/cauthy-By2cryXN.d.ts +0 -56
- package/dist/cauthy-DsUNqjg6.d.mts +0 -56
- package/dist/types-BwCjBn-K.d.mts +0 -97
- package/dist/types-BwCjBn-K.d.ts +0 -97
package/dist/index.mjs
CHANGED
|
@@ -54,6 +54,9 @@ async function unseal(sealed, key) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// src/system.ts
|
|
58
|
+
import { exportJWK, importJWK, importPKCS8, SignJWT } from "jose";
|
|
59
|
+
|
|
57
60
|
// src/types.ts
|
|
58
61
|
var CauthyConfigError = class extends Error {
|
|
59
62
|
constructor(message) {
|
|
@@ -62,12 +65,243 @@ var CauthyConfigError = class extends Error {
|
|
|
62
65
|
}
|
|
63
66
|
};
|
|
64
67
|
|
|
68
|
+
// src/system.ts
|
|
69
|
+
var ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
70
|
+
var SKEW_MS = 3e4;
|
|
71
|
+
var CauthyApiError = class extends Error {
|
|
72
|
+
constructor(status, code, message) {
|
|
73
|
+
super(`cauthy-auth: ${message}`);
|
|
74
|
+
this.status = status;
|
|
75
|
+
this.code = code;
|
|
76
|
+
this.name = "CauthyApiError";
|
|
77
|
+
}
|
|
78
|
+
status;
|
|
79
|
+
code;
|
|
80
|
+
};
|
|
81
|
+
function env(name) {
|
|
82
|
+
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
83
|
+
}
|
|
84
|
+
function toOrganization(o) {
|
|
85
|
+
return {
|
|
86
|
+
id: o.id,
|
|
87
|
+
system: o.system,
|
|
88
|
+
name: o.name,
|
|
89
|
+
kind: o.kind,
|
|
90
|
+
externalId: o.external_id,
|
|
91
|
+
status: o.status,
|
|
92
|
+
metadata: o.metadata ?? {},
|
|
93
|
+
members: o.members,
|
|
94
|
+
createdAt: o.created_at,
|
|
95
|
+
updatedAt: o.updated_at
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function toMember(m) {
|
|
99
|
+
return { userId: m.user_id, email: m.email, name: m.name, status: m.status, role: m.role, ...m.invited === void 0 ? {} : { invited: m.invited } };
|
|
100
|
+
}
|
|
101
|
+
function asJwk(k) {
|
|
102
|
+
if (typeof k.privateKey !== "string") return k.privateKey;
|
|
103
|
+
return k.privateKey.includes("BEGIN") ? void 0 : JSON.parse(k.privateKey);
|
|
104
|
+
}
|
|
105
|
+
async function loadKey(k) {
|
|
106
|
+
const jwk = asJwk(k);
|
|
107
|
+
return jwk ? importJWK(jwk, "RS256") : importPKCS8(k.privateKey, "RS256", { extractable: true });
|
|
108
|
+
}
|
|
109
|
+
function createSystem(given = {}) {
|
|
110
|
+
const issuer = (given.issuer ?? env("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
111
|
+
const clientId = given.clientId ?? env("CAUTHY_CLIENT_ID") ?? "";
|
|
112
|
+
const clientSecret = given.clientSecret ?? env("CAUTHY_CLIENT_SECRET") ?? "";
|
|
113
|
+
const envKey = env("CAUTHY_SYSTEM_KEY");
|
|
114
|
+
const key = given.key ?? (envKey ? { privateKey: envKey, keyId: env("CAUTHY_SYSTEM_KEY_ID") ?? "" } : void 0);
|
|
115
|
+
const doFetch = (...args) => (given.fetch ?? fetch)(...args);
|
|
116
|
+
const tokenEndpoint = `${issuer}/v1/oauth/token`;
|
|
117
|
+
function configured() {
|
|
118
|
+
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address");
|
|
119
|
+
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
120
|
+
if (key ? key.keyId === "" : clientSecret === "") {
|
|
121
|
+
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)");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
let imported;
|
|
125
|
+
const signingKey = () => imported ??= loadKey(key);
|
|
126
|
+
async function fail(res, what) {
|
|
127
|
+
const body = await res.json().catch(() => null);
|
|
128
|
+
const first = body?.errors?.[0];
|
|
129
|
+
throw new CauthyApiError(res.status, first?.code ?? "error", `Cauthy refused ${what} (${res.status}): ${first?.message ?? res.statusText}`);
|
|
130
|
+
}
|
|
131
|
+
let held;
|
|
132
|
+
let asking;
|
|
133
|
+
async function ask() {
|
|
134
|
+
configured();
|
|
135
|
+
const form = new URLSearchParams({ grant_type: "client_credentials", client_id: clientId });
|
|
136
|
+
if (key) {
|
|
137
|
+
const assertion = await new SignJWT({}).setProtectedHeader({ alg: "RS256", kid: key.keyId }).setIssuer(clientId).setSubject(clientId).setAudience(tokenEndpoint).setJti(crypto.randomUUID()).setIssuedAt().setExpirationTime("2m").sign(await signingKey());
|
|
138
|
+
form.set("client_assertion_type", ASSERTION_TYPE);
|
|
139
|
+
form.set("client_assertion", assertion);
|
|
140
|
+
} else {
|
|
141
|
+
form.set("client_secret", clientSecret);
|
|
142
|
+
}
|
|
143
|
+
const res = await doFetch(tokenEndpoint, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form });
|
|
144
|
+
if (!res.ok) await fail(res, "the application's own token");
|
|
145
|
+
const t = await res.json();
|
|
146
|
+
held = { value: t.access_token, until: Date.now() + t.expires_in * 1e3 - SKEW_MS };
|
|
147
|
+
return t.access_token;
|
|
148
|
+
}
|
|
149
|
+
function token() {
|
|
150
|
+
if (held && Date.now() < held.until) return Promise.resolve(held.value);
|
|
151
|
+
return asking ??= ask().finally(() => {
|
|
152
|
+
asking = void 0;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function call(method, path, body) {
|
|
156
|
+
const send = async () => doFetch(`${issuer}${path}`, {
|
|
157
|
+
method,
|
|
158
|
+
headers: { authorization: `Bearer ${await token()}`, ...body === void 0 ? {} : { "content-type": "application/json" } },
|
|
159
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
160
|
+
});
|
|
161
|
+
let res = await send();
|
|
162
|
+
if (res.status === 401 && held) {
|
|
163
|
+
held = void 0;
|
|
164
|
+
res = await send();
|
|
165
|
+
}
|
|
166
|
+
if (!res.ok) await fail(res, `${method} ${path}`);
|
|
167
|
+
return res.status === 204 ? void 0 : res.json();
|
|
168
|
+
}
|
|
169
|
+
const data = async (method, path, body) => (await call(method, path, body))?.data;
|
|
170
|
+
const id = encodeURIComponent;
|
|
171
|
+
return {
|
|
172
|
+
token,
|
|
173
|
+
request: data,
|
|
174
|
+
organizations: {
|
|
175
|
+
async list(query = {}) {
|
|
176
|
+
const q = new URLSearchParams();
|
|
177
|
+
for (const [k, v] of Object.entries(query)) if (v !== void 0) q.set(k, String(v));
|
|
178
|
+
const out2 = await call("GET", `/v1/organizations${q.size ? `?${q}` : ""}`);
|
|
179
|
+
return { items: out2.data.map(toOrganization), total: out2.total_count };
|
|
180
|
+
},
|
|
181
|
+
async create(input) {
|
|
182
|
+
return toOrganization(await data(
|
|
183
|
+
"POST",
|
|
184
|
+
"/v1/organizations",
|
|
185
|
+
{ name: input.name, kind: input.kind, external_id: input.externalId, metadata: input.metadata }
|
|
186
|
+
));
|
|
187
|
+
},
|
|
188
|
+
async get(orgId) {
|
|
189
|
+
return toOrganization(await data("GET", `/v1/organizations/${id(orgId)}`));
|
|
190
|
+
},
|
|
191
|
+
async update(orgId, patch) {
|
|
192
|
+
return toOrganization(await data("PATCH", `/v1/organizations/${id(orgId)}`, patch));
|
|
193
|
+
},
|
|
194
|
+
async remove(orgId) {
|
|
195
|
+
await call("DELETE", `/v1/organizations/${id(orgId)}`);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
members: {
|
|
199
|
+
async list(orgId) {
|
|
200
|
+
return (await data("GET", `/v1/organizations/${id(orgId)}/members`)).map(toMember);
|
|
201
|
+
},
|
|
202
|
+
async add(orgId, m) {
|
|
203
|
+
return toMember(await data(
|
|
204
|
+
"POST",
|
|
205
|
+
`/v1/organizations/${id(orgId)}/members`,
|
|
206
|
+
{ role: m.role, user_id: m.userId, email: m.email, name: m.name, locale: m.locale, client_id: clientId }
|
|
207
|
+
));
|
|
208
|
+
},
|
|
209
|
+
async setRole(orgId, userId, role) {
|
|
210
|
+
return toMember(await data("PUT", `/v1/organizations/${id(orgId)}/members/${id(userId)}`, { role }));
|
|
211
|
+
},
|
|
212
|
+
async remove(orgId, userId) {
|
|
213
|
+
await call("DELETE", `/v1/organizations/${id(orgId)}/members/${id(userId)}`);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
async invite(person) {
|
|
217
|
+
const u = await data("POST", "/v1/users", { ...person, client_id: clientId });
|
|
218
|
+
return { id: u.id, email: u.email };
|
|
219
|
+
},
|
|
220
|
+
async publicJwks() {
|
|
221
|
+
if (!key) return { keys: [] };
|
|
222
|
+
const { kty, n, e } = asJwk(key) ?? await exportJWK(await signingKey());
|
|
223
|
+
return { keys: [{ kty, n, e, kid: key.keyId, alg: "RS256", use: "sig" }] };
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/webhook.ts
|
|
229
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
230
|
+
var CauthyWebhookError = class extends Error {
|
|
231
|
+
constructor(reason) {
|
|
232
|
+
super(`cauthy-auth: webhook refused: ${reason}`);
|
|
233
|
+
this.reason = reason;
|
|
234
|
+
this.name = "CauthyWebhookError";
|
|
235
|
+
}
|
|
236
|
+
reason;
|
|
237
|
+
};
|
|
238
|
+
var keys = /* @__PURE__ */ new Map();
|
|
239
|
+
function keysOf(issuer) {
|
|
240
|
+
let set = keys.get(issuer);
|
|
241
|
+
if (set === void 0) {
|
|
242
|
+
set = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
|
|
243
|
+
keys.set(issuer, set);
|
|
244
|
+
}
|
|
245
|
+
return set;
|
|
246
|
+
}
|
|
247
|
+
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
248
|
+
async function verifyWebhook(request, options = {}) {
|
|
249
|
+
const env3 = typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
250
|
+
const issuer = (options.issuer ?? env3 ?? "").replace(/\/+$/, "");
|
|
251
|
+
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
252
|
+
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
253
|
+
if (!signature) throw new CauthyWebhookError("no signature");
|
|
254
|
+
const raw = new Uint8Array(await request.arrayBuffer());
|
|
255
|
+
const audience = options.url ?? request.url.split("?")[0];
|
|
256
|
+
let claims;
|
|
257
|
+
try {
|
|
258
|
+
const verified = await jwtVerify(signature, options.jwks ?? keysOf(issuer), {
|
|
259
|
+
algorithms: ["RS256"],
|
|
260
|
+
issuer,
|
|
261
|
+
audience,
|
|
262
|
+
typ: "cauthy-webhook+jwt",
|
|
263
|
+
requiredClaims: ["exp", "iat", "jti", "body_sha256"],
|
|
264
|
+
currentDate: options.now,
|
|
265
|
+
clockTolerance: 30
|
|
266
|
+
});
|
|
267
|
+
claims = verified.payload;
|
|
268
|
+
} catch (err) {
|
|
269
|
+
throw new CauthyWebhookError(err instanceof Error ? err.message : "bad signature");
|
|
270
|
+
}
|
|
271
|
+
const now = Math.floor((options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
272
|
+
if (typeof claims.iat !== "number" || claims.iat > now + 30) throw new CauthyWebhookError("signed in the future");
|
|
273
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", raw));
|
|
274
|
+
let b64 = "";
|
|
275
|
+
for (const b of digest) b64 += String.fromCharCode(b);
|
|
276
|
+
const hash = btoa(b64).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
277
|
+
if (hash !== claims.body_sha256) throw new CauthyWebhookError("the body is not the one that was signed");
|
|
278
|
+
let body;
|
|
279
|
+
try {
|
|
280
|
+
body = JSON.parse(new TextDecoder().decode(raw));
|
|
281
|
+
} catch {
|
|
282
|
+
throw new CauthyWebhookError("the body is not JSON");
|
|
283
|
+
}
|
|
284
|
+
if (body.id !== claims.jti || body.event !== claims.evt || body.tenant_id !== claims.tid) {
|
|
285
|
+
throw new CauthyWebhookError("the body and its signature disagree");
|
|
286
|
+
}
|
|
287
|
+
const inner = body.data ?? {};
|
|
288
|
+
return {
|
|
289
|
+
id: String(body.id),
|
|
290
|
+
type: String(body.event),
|
|
291
|
+
tenantId: String(body.tenant_id),
|
|
292
|
+
createdAt: String(inner.created_at ?? body.created_at ?? ""),
|
|
293
|
+
...hex(inner.user_id) ? { userId: inner.user_id } : {},
|
|
294
|
+
...hex(inner.actor_id) ? { actorId: inner.actor_id } : {},
|
|
295
|
+
data: inner.data ?? {}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
65
299
|
// src/cauthy.ts
|
|
66
300
|
var SESSION_DAYS = 30;
|
|
67
301
|
var FLOW_SECONDS = 600;
|
|
68
302
|
var SKEW_SECONDS = 30;
|
|
69
303
|
var ORGANIZATIONS_TTL_MS = 6e4;
|
|
70
|
-
function
|
|
304
|
+
function env2(name) {
|
|
71
305
|
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
72
306
|
}
|
|
73
307
|
function b64url(bytes) {
|
|
@@ -94,11 +328,11 @@ function redirect(location, cookies = []) {
|
|
|
94
328
|
return new Response(null, { status: 302, headers: h });
|
|
95
329
|
}
|
|
96
330
|
function createCauthy(given = {}) {
|
|
97
|
-
const issuer = (given.issuer ??
|
|
98
|
-
const clientId = given.clientId ??
|
|
99
|
-
const clientSecret = given.clientSecret ??
|
|
100
|
-
const secret = given.secret ??
|
|
101
|
-
const system = given.system ??
|
|
331
|
+
const issuer = (given.issuer ?? env2("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
332
|
+
const clientId = given.clientId ?? env2("CAUTHY_CLIENT_ID") ?? "";
|
|
333
|
+
const clientSecret = given.clientSecret ?? env2("CAUTHY_CLIENT_SECRET");
|
|
334
|
+
const secret = given.secret ?? env2("CAUTHY_SECRET") ?? "";
|
|
335
|
+
const system = given.system ?? env2("CAUTHY_SYSTEM") ?? "";
|
|
102
336
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
103
337
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
104
338
|
if (secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
@@ -236,7 +470,8 @@ function createCauthy(given = {}) {
|
|
|
236
470
|
kind: String(o.kind ?? ""),
|
|
237
471
|
role: String(o.role ?? ""),
|
|
238
472
|
metadata: o.metadata ?? {},
|
|
239
|
-
...o.external_id ? { externalId: String(o.external_id) } : {}
|
|
473
|
+
...o.external_id ? { externalId: String(o.external_id) } : {},
|
|
474
|
+
...Array.isArray(o.permissions) ? { permissions: o.permissions.map(String), denied: Array.isArray(o.denied) ? o.denied.map(String) : [] } : {}
|
|
240
475
|
})
|
|
241
476
|
);
|
|
242
477
|
if (orgCache.size > 5e3) orgCache.clear();
|
|
@@ -362,8 +597,11 @@ function createCauthy(given = {}) {
|
|
|
362
597
|
const to = value === null ? urls.signIn(back) : `${basePath}/refresh?return_to=${encodeURIComponent(back)}`;
|
|
363
598
|
return { response: redirect(to) };
|
|
364
599
|
}
|
|
600
|
+
const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now });
|
|
365
601
|
return {
|
|
366
602
|
handler,
|
|
603
|
+
webhook,
|
|
604
|
+
system: createSystem({ issuer, clientId, clientSecret, key: given.systemKey, fetch: given.fetch }),
|
|
367
605
|
getSession,
|
|
368
606
|
status,
|
|
369
607
|
resolve: resolve2,
|
|
@@ -576,11 +814,11 @@ function toWire(theme) {
|
|
|
576
814
|
return wire;
|
|
577
815
|
}
|
|
578
816
|
async function pushTheme(o) {
|
|
579
|
-
const
|
|
580
|
-
const issuer = (o.issuer ??
|
|
581
|
-
const clientId = o.clientId ??
|
|
582
|
-
const tenant = o.tenant ??
|
|
583
|
-
const token = o.token ??
|
|
817
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
818
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
819
|
+
const clientId = o.clientId ?? env3("CAUTHY_CLIENT_ID") ?? "";
|
|
820
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
821
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
584
822
|
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
585
823
|
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
|
|
586
824
|
}
|
|
@@ -594,10 +832,94 @@ async function pushTheme(o) {
|
|
|
594
832
|
throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
595
833
|
}
|
|
596
834
|
}
|
|
835
|
+
|
|
836
|
+
// src/permissions.ts
|
|
837
|
+
function matches(entry, resource, action) {
|
|
838
|
+
const i = entry.lastIndexOf(":");
|
|
839
|
+
if (i < 0) return false;
|
|
840
|
+
const r = entry.slice(0, i);
|
|
841
|
+
const a = entry.slice(i + 1);
|
|
842
|
+
return (r === "*" || r === resource) && (a === "*" || a === action);
|
|
843
|
+
}
|
|
844
|
+
function can(organization, resource, action) {
|
|
845
|
+
if (!organization?.permissions) return false;
|
|
846
|
+
if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
|
|
847
|
+
return organization.permissions.some((p) => matches(p, resource, action));
|
|
848
|
+
}
|
|
849
|
+
async function pushPolicy(o) {
|
|
850
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
851
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
852
|
+
const system = o.system ?? env3("CAUTHY_SYSTEM") ?? "";
|
|
853
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
854
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
855
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
856
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
|
|
857
|
+
}
|
|
858
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
|
|
859
|
+
method: "PUT",
|
|
860
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
861
|
+
body: JSON.stringify({ roles: o.roles })
|
|
862
|
+
});
|
|
863
|
+
if (!res.ok) {
|
|
864
|
+
const body = await res.json().catch(() => null);
|
|
865
|
+
throw new Error(`cauthy-auth: Cauthy refused the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/connect.ts
|
|
870
|
+
function sessionSecret() {
|
|
871
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
872
|
+
let s = "";
|
|
873
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
874
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
875
|
+
}
|
|
876
|
+
async function connectSystem(o) {
|
|
877
|
+
const env3 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
878
|
+
const issuer = (o.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
879
|
+
const tenant = o.tenant ?? env3("CAUTHY_TENANT") ?? "";
|
|
880
|
+
const token = o.token ?? env3("CAUTHY_TOKEN") ?? "";
|
|
881
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
882
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to connect a system`);
|
|
883
|
+
}
|
|
884
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/connect`, {
|
|
885
|
+
method: "POST",
|
|
886
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
887
|
+
body: JSON.stringify({
|
|
888
|
+
system: o.system,
|
|
889
|
+
app_url: o.appUrl,
|
|
890
|
+
name: o.name,
|
|
891
|
+
callback_path: o.callbackPath,
|
|
892
|
+
events_url: o.eventsUrl,
|
|
893
|
+
events: o.events,
|
|
894
|
+
jwks_uri: o.jwksUri
|
|
895
|
+
})
|
|
896
|
+
});
|
|
897
|
+
const body = await res.json().catch(() => null);
|
|
898
|
+
if (!res.ok || !body?.data) {
|
|
899
|
+
const why = (body?.errors ?? []).map((e) => e.message).filter(Boolean).join("; ") || res.statusText;
|
|
900
|
+
throw new Error(`cauthy-auth: Cauthy did not connect the system (${res.status}): ${why}`);
|
|
901
|
+
}
|
|
902
|
+
const d = body.data;
|
|
903
|
+
return {
|
|
904
|
+
system: d.system,
|
|
905
|
+
clientId: d.client.client_id,
|
|
906
|
+
clientSecret: d.client_secret,
|
|
907
|
+
redirectUris: d.client.redirect_uris,
|
|
908
|
+
webhook: d.webhook,
|
|
909
|
+
env: { ...d.env, CAUTHY_SECRET: sessionSecret() }
|
|
910
|
+
};
|
|
911
|
+
}
|
|
597
912
|
export {
|
|
913
|
+
CauthyApiError,
|
|
598
914
|
CauthyConfigError,
|
|
915
|
+
CauthyWebhookError,
|
|
916
|
+
can,
|
|
917
|
+
connectSystem,
|
|
599
918
|
createCauthy,
|
|
919
|
+
createSystem,
|
|
920
|
+
pushPolicy,
|
|
600
921
|
pushTheme,
|
|
601
|
-
themeFromStackmyth
|
|
922
|
+
themeFromStackmyth,
|
|
923
|
+
verifyWebhook
|
|
602
924
|
};
|
|
603
925
|
//# sourceMappingURL=index.mjs.map
|