cauthy-auth 0.1.0 → 0.2.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/dist/{cauthy-DsUNqjg6.d.mts → cauthy-BrIMaoj1.d.mts} +48 -2
- package/dist/{cauthy-By2cryXN.d.ts → cauthy-CCZc9w8r.d.ts} +48 -2
- package/dist/cli.js +79 -0
- package/dist/fastify.d.mts +3 -2
- package/dist/fastify.d.ts +3 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +118 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -2
- 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 +3 -2
- package/dist/react.d.ts +3 -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/{types-BwCjBn-K.d.mts → types-CYmUVWzY.d.mts} +9 -0
- package/dist/{types-BwCjBn-K.d.ts → types-CYmUVWzY.d.ts} +9 -0
- package/package.json +1 -1
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JWTVerifyGetKey } from 'jose';
|
|
2
|
+
import { J as JsonValue, S as Session, c as SessionStatus, b as SessionOrganization, P as PublicSession, a as CauthyOptions } from './types-CYmUVWzY.mjs';
|
|
3
|
+
|
|
4
|
+
/** Something that happened in Cauthy, as a subscriber is told. Moments are UTC. */
|
|
5
|
+
interface CauthyEvent {
|
|
6
|
+
/** This delivery's id: the same event delivered again carries the same one. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** What happened: "organization.member_added". */
|
|
9
|
+
type: string;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
/** The person it happened to, when it is about one. */
|
|
13
|
+
userId?: string;
|
|
14
|
+
/** Who did it. */
|
|
15
|
+
actorId?: string;
|
|
16
|
+
/** What the event says: for an organization, its id, system, external_id, role… */
|
|
17
|
+
data: Record<string, JsonValue>;
|
|
18
|
+
}
|
|
19
|
+
declare class CauthyWebhookError extends Error {
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
constructor(reason: string);
|
|
22
|
+
}
|
|
23
|
+
interface VerifyWebhookOptions {
|
|
24
|
+
/** Cauthy's address. Default: `CAUTHY_ISSUER`. */
|
|
25
|
+
issuer?: string;
|
|
26
|
+
/**
|
|
27
|
+
* This endpoint's own public address, exactly as it is registered in Cauthy.
|
|
28
|
+
* Default: the request's URL, which is right unless a proxy changes it.
|
|
29
|
+
*/
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Cauthy's keys. Default: fetched from the issuer and cached. */
|
|
32
|
+
jwks?: JWTVerifyGetKey;
|
|
33
|
+
/** The moment to verify at. Default: now. */
|
|
34
|
+
now?: Date;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Verifies a delivery and answers the event it carries. Throws
|
|
38
|
+
* `CauthyWebhookError` for anything that is not a delivery Cauthy made for
|
|
39
|
+
* this endpoint just now: answer it 401 and nothing else.
|
|
40
|
+
*/
|
|
41
|
+
declare function verifyWebhook(request: Request, options?: VerifyWebhookOptions): Promise<CauthyEvent>;
|
|
2
42
|
|
|
3
43
|
/** What `resolve` answers: who is there, and the cookies that must go back. */
|
|
4
44
|
interface Resolved {
|
|
@@ -40,6 +80,12 @@ interface Cauthy {
|
|
|
40
80
|
}>;
|
|
41
81
|
/** The organizations the person belongs to (of `system`, when set), with their role in each. */
|
|
42
82
|
organizations(session: Session): Promise<SessionOrganization[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Verifies a webhook delivery from Cauthy and answers the event it carries;
|
|
85
|
+
* throws `CauthyWebhookError` for anything else. No secret is involved: the
|
|
86
|
+
* delivery is signed with Cauthy's own key.
|
|
87
|
+
*/
|
|
88
|
+
webhook(request: Request, options?: Pick<VerifyWebhookOptions, 'jwks' | 'now' | 'url'>): Promise<CauthyEvent>;
|
|
43
89
|
/** What the browser may be told of a session. */
|
|
44
90
|
toPublic(session: Session): Promise<PublicSession>;
|
|
45
91
|
/** Paths of this application that start a sign-in, a sign-out, or open the account page. */
|
|
@@ -53,4 +99,4 @@ interface Cauthy {
|
|
|
53
99
|
}
|
|
54
100
|
declare function createCauthy(given?: CauthyOptions): Cauthy;
|
|
55
101
|
|
|
56
|
-
export { type Cauthy as C, type Resolved as R, createCauthy as c };
|
|
102
|
+
export { type Cauthy as C, type Resolved as R, type VerifyWebhookOptions as V, type CauthyEvent as a, CauthyWebhookError as b, createCauthy as c, verifyWebhook as v };
|
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JWTVerifyGetKey } from 'jose';
|
|
2
|
+
import { J as JsonValue, S as Session, c as SessionStatus, b as SessionOrganization, P as PublicSession, a as CauthyOptions } from './types-CYmUVWzY.js';
|
|
3
|
+
|
|
4
|
+
/** Something that happened in Cauthy, as a subscriber is told. Moments are UTC. */
|
|
5
|
+
interface CauthyEvent {
|
|
6
|
+
/** This delivery's id: the same event delivered again carries the same one. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** What happened: "organization.member_added". */
|
|
9
|
+
type: string;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
/** The person it happened to, when it is about one. */
|
|
13
|
+
userId?: string;
|
|
14
|
+
/** Who did it. */
|
|
15
|
+
actorId?: string;
|
|
16
|
+
/** What the event says: for an organization, its id, system, external_id, role… */
|
|
17
|
+
data: Record<string, JsonValue>;
|
|
18
|
+
}
|
|
19
|
+
declare class CauthyWebhookError extends Error {
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
constructor(reason: string);
|
|
22
|
+
}
|
|
23
|
+
interface VerifyWebhookOptions {
|
|
24
|
+
/** Cauthy's address. Default: `CAUTHY_ISSUER`. */
|
|
25
|
+
issuer?: string;
|
|
26
|
+
/**
|
|
27
|
+
* This endpoint's own public address, exactly as it is registered in Cauthy.
|
|
28
|
+
* Default: the request's URL, which is right unless a proxy changes it.
|
|
29
|
+
*/
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Cauthy's keys. Default: fetched from the issuer and cached. */
|
|
32
|
+
jwks?: JWTVerifyGetKey;
|
|
33
|
+
/** The moment to verify at. Default: now. */
|
|
34
|
+
now?: Date;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Verifies a delivery and answers the event it carries. Throws
|
|
38
|
+
* `CauthyWebhookError` for anything that is not a delivery Cauthy made for
|
|
39
|
+
* this endpoint just now: answer it 401 and nothing else.
|
|
40
|
+
*/
|
|
41
|
+
declare function verifyWebhook(request: Request, options?: VerifyWebhookOptions): Promise<CauthyEvent>;
|
|
2
42
|
|
|
3
43
|
/** What `resolve` answers: who is there, and the cookies that must go back. */
|
|
4
44
|
interface Resolved {
|
|
@@ -40,6 +80,12 @@ interface Cauthy {
|
|
|
40
80
|
}>;
|
|
41
81
|
/** The organizations the person belongs to (of `system`, when set), with their role in each. */
|
|
42
82
|
organizations(session: Session): Promise<SessionOrganization[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Verifies a webhook delivery from Cauthy and answers the event it carries;
|
|
85
|
+
* throws `CauthyWebhookError` for anything else. No secret is involved: the
|
|
86
|
+
* delivery is signed with Cauthy's own key.
|
|
87
|
+
*/
|
|
88
|
+
webhook(request: Request, options?: Pick<VerifyWebhookOptions, 'jwks' | 'now' | 'url'>): Promise<CauthyEvent>;
|
|
43
89
|
/** What the browser may be told of a session. */
|
|
44
90
|
toPublic(session: Session): Promise<PublicSession>;
|
|
45
91
|
/** Paths of this application that start a sign-in, a sign-out, or open the account page. */
|
|
@@ -53,4 +99,4 @@ interface Cauthy {
|
|
|
53
99
|
}
|
|
54
100
|
declare function createCauthy(given?: CauthyOptions): Cauthy;
|
|
55
101
|
|
|
56
|
-
export { type Cauthy as C, type Resolved as R, createCauthy as c };
|
|
102
|
+
export { type Cauthy as C, type Resolved as R, type VerifyWebhookOptions as V, type CauthyEvent as a, CauthyWebhookError as b, createCauthy as c, verifyWebhook as v };
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,27 @@
|
|
|
4
4
|
// src/cli.ts
|
|
5
5
|
var import_node_fs = require("fs");
|
|
6
6
|
|
|
7
|
+
// src/permissions.ts
|
|
8
|
+
async function pushPolicy(o) {
|
|
9
|
+
const env = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
10
|
+
const issuer = (o.issuer ?? env("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
11
|
+
const system = o.system ?? env("CAUTHY_SYSTEM") ?? "";
|
|
12
|
+
const tenant = o.tenant ?? env("CAUTHY_TENANT") ?? "";
|
|
13
|
+
const token = o.token ?? env("CAUTHY_TOKEN") ?? "";
|
|
14
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
15
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
|
|
16
|
+
}
|
|
17
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
|
|
18
|
+
method: "PUT",
|
|
19
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
20
|
+
body: JSON.stringify({ roles: o.roles })
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
const body = await res.json().catch(() => null);
|
|
24
|
+
throw new Error(`cauthy-auth: Cauthy refused the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
7
28
|
// src/theme.ts
|
|
8
29
|
var MAX_RADIUS = 40;
|
|
9
30
|
function declarations(css) {
|
|
@@ -225,6 +246,63 @@ async function pushTheme(o) {
|
|
|
225
246
|
}
|
|
226
247
|
|
|
227
248
|
// src/cli.ts
|
|
249
|
+
var POLICY_HELP = `cauthy-auth policy --file <roles.json> [--system <name>] [--push]
|
|
250
|
+
|
|
251
|
+
Loads a system's catalog of roles into Cauthy from a file:
|
|
252
|
+
|
|
253
|
+
{ "roles": [ { "role": "accountant", "name": "Accountant", "inherits": ["reader"],
|
|
254
|
+
"permissions": [ { "resource": "ledger", "action": "write" },
|
|
255
|
+
{ "kind": "business", "resource": "books.close", "action": "write" } ] } ] }
|
|
256
|
+
|
|
257
|
+
--file <file> The catalog, as JSON.
|
|
258
|
+
--system <name> The system it is of. Default: CAUTHY_SYSTEM.
|
|
259
|
+
--push Send it to Cauthy, REPLACING the catalog it has. Without it
|
|
260
|
+
the catalog is only checked and shown.
|
|
261
|
+
|
|
262
|
+
Use it to load a first catalog. Afterwards the catalog is Cauthy's, edited in
|
|
263
|
+
its dashboard: pushing again overwrites what was edited there.
|
|
264
|
+
|
|
265
|
+
To push: CAUTHY_ISSUER, CAUTHY_TENANT (the workspace's slug) and CAUTHY_TOKEN
|
|
266
|
+
(a personal access token with organizations:manage).
|
|
267
|
+
`;
|
|
268
|
+
async function policy(argv) {
|
|
269
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
270
|
+
process.stdout.write(POLICY_HELP);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
let file;
|
|
274
|
+
let system;
|
|
275
|
+
let push = false;
|
|
276
|
+
for (let i = 0; i < argv.length; i++) {
|
|
277
|
+
const arg = argv[i];
|
|
278
|
+
const value = () => argv[++i] ?? fail(`${arg} needs a value`);
|
|
279
|
+
if (arg === "--file") file = value();
|
|
280
|
+
else if (arg === "--system") system = value();
|
|
281
|
+
else if (arg === "--push") push = true;
|
|
282
|
+
else fail(`unknown option ${arg}
|
|
283
|
+
|
|
284
|
+
${POLICY_HELP}`);
|
|
285
|
+
}
|
|
286
|
+
if (!file) fail("give the catalog with --file\n\n" + POLICY_HELP);
|
|
287
|
+
let roles;
|
|
288
|
+
try {
|
|
289
|
+
const parsed = JSON.parse((0, import_node_fs.readFileSync)(file, "utf8"));
|
|
290
|
+
const list = Array.isArray(parsed) ? parsed : parsed.roles;
|
|
291
|
+
if (!Array.isArray(list)) throw new Error('no "roles" list');
|
|
292
|
+
roles = list;
|
|
293
|
+
} catch (err) {
|
|
294
|
+
return fail(`cannot read ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
295
|
+
}
|
|
296
|
+
const permissions = roles.reduce((n, r) => n + (r.permissions?.length ?? 0), 0);
|
|
297
|
+
process.stdout.write(`${roles.length} roles, ${permissions} permissions: ${roles.map((r) => r.role).join(", ")}
|
|
298
|
+
`);
|
|
299
|
+
if (push) {
|
|
300
|
+
await pushPolicy({ system, roles });
|
|
301
|
+
process.stdout.write("Sent to Cauthy: this is now the catalog of the system.\n");
|
|
302
|
+
} else {
|
|
303
|
+
process.stdout.write("Not sent. Add --push to give it to Cauthy.\n");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
228
306
|
var HELP = `cauthy-auth theme [options]
|
|
229
307
|
|
|
230
308
|
Reads the application's Stackmyth theme and gives it to Cauthy's hosted pages.
|
|
@@ -247,6 +325,7 @@ function fail(message) {
|
|
|
247
325
|
process.exit(1);
|
|
248
326
|
}
|
|
249
327
|
async function main(argv) {
|
|
328
|
+
if (argv[0] === "policy") return policy(argv.slice(1));
|
|
250
329
|
if (argv[0] !== "theme" || argv.includes("--help") || argv.includes("-h")) {
|
|
251
330
|
process.stdout.write(HELP);
|
|
252
331
|
process.exit(argv[0] === "theme" || argv.length === 0 || argv.includes("--help") || argv.includes("-h") ? 0 : 1);
|
package/dist/fastify.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FastifyReply, FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import { C as Cauthy } from './cauthy-
|
|
3
|
-
import { S as Session } from './types-
|
|
2
|
+
import { C as Cauthy } from './cauthy-BrIMaoj1.mjs';
|
|
3
|
+
import { S as Session } from './types-CYmUVWzY.mjs';
|
|
4
|
+
import 'jose';
|
|
4
5
|
|
|
5
6
|
declare module 'fastify' {
|
|
6
7
|
interface FastifyRequest {
|
package/dist/fastify.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FastifyReply, FastifyPluginAsync } from 'fastify';
|
|
2
|
-
import { C as Cauthy } from './cauthy-
|
|
3
|
-
import { S as Session } from './types-
|
|
2
|
+
import { C as Cauthy } from './cauthy-CCZc9w8r.js';
|
|
3
|
+
import { S as Session } from './types-CYmUVWzY.js';
|
|
4
|
+
import 'jose';
|
|
4
5
|
|
|
5
6
|
declare module 'fastify' {
|
|
6
7
|
interface FastifyRequest {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { C as Cauthy, R as Resolved, c as createCauthy } from './cauthy-
|
|
2
|
-
export { C as CauthyConfigError, a as CauthyOptions, J as JsonValue, P as PublicSession, S as Session, b as SessionOrganization, c as SessionStatus, d as SessionUser } from './types-
|
|
1
|
+
export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-BrIMaoj1.mjs';
|
|
2
|
+
export { C as CauthyConfigError, a as CauthyOptions, J as JsonValue, P as PublicSession, S as Session, b as SessionOrganization, c as SessionStatus, d as SessionUser } from './types-CYmUVWzY.mjs';
|
|
3
3
|
export { CauthyTheme, ImportedTheme, PushThemeOptions, StackmythThemeOptions, pushTheme, themeFromStackmyth } from './theme.mjs';
|
|
4
|
+
export { P as Permitted, a as PolicyPermission, b as PolicyRole, c as PushPolicyOptions, d as can, p as pushPolicy } from './permissions-B-a3eN0h.mjs';
|
|
5
|
+
import 'jose';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { C as Cauthy, R as Resolved, c as createCauthy } from './cauthy-
|
|
2
|
-
export { C as CauthyConfigError, a as CauthyOptions, J as JsonValue, P as PublicSession, S as Session, b as SessionOrganization, c as SessionStatus, d as SessionUser } from './types-
|
|
1
|
+
export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-CCZc9w8r.js';
|
|
2
|
+
export { C as CauthyConfigError, a as CauthyOptions, J as JsonValue, P as PublicSession, S as Session, b as SessionOrganization, c as SessionStatus, d as SessionUser } from './types-CYmUVWzY.js';
|
|
3
3
|
export { CauthyTheme, ImportedTheme, PushThemeOptions, StackmythThemeOptions, pushTheme, themeFromStackmyth } from './theme.js';
|
|
4
|
+
export { P as Permitted, a as PolicyPermission, b as PolicyRole, c as PushPolicyOptions, d as can, p as pushPolicy } from './permissions-B-a3eN0h.js';
|
|
5
|
+
import 'jose';
|
package/dist/index.js
CHANGED
|
@@ -21,9 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
CauthyConfigError: () => CauthyConfigError,
|
|
24
|
+
CauthyWebhookError: () => CauthyWebhookError,
|
|
25
|
+
can: () => can,
|
|
24
26
|
createCauthy: () => createCauthy,
|
|
27
|
+
pushPolicy: () => pushPolicy,
|
|
25
28
|
pushTheme: () => pushTheme,
|
|
26
|
-
themeFromStackmyth: () => themeFromStackmyth
|
|
29
|
+
themeFromStackmyth: () => themeFromStackmyth,
|
|
30
|
+
verifyWebhook: () => verifyWebhook
|
|
27
31
|
});
|
|
28
32
|
module.exports = __toCommonJS(src_exports);
|
|
29
33
|
|
|
@@ -83,6 +87,77 @@ async function unseal(sealed, key) {
|
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
// src/webhook.ts
|
|
91
|
+
var import_jose2 = require("jose");
|
|
92
|
+
var CauthyWebhookError = class extends Error {
|
|
93
|
+
constructor(reason) {
|
|
94
|
+
super(`cauthy-auth: webhook refused: ${reason}`);
|
|
95
|
+
this.reason = reason;
|
|
96
|
+
this.name = "CauthyWebhookError";
|
|
97
|
+
}
|
|
98
|
+
reason;
|
|
99
|
+
};
|
|
100
|
+
var keys = /* @__PURE__ */ new Map();
|
|
101
|
+
function keysOf(issuer) {
|
|
102
|
+
let set = keys.get(issuer);
|
|
103
|
+
if (set === void 0) {
|
|
104
|
+
set = (0, import_jose2.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
|
|
105
|
+
keys.set(issuer, set);
|
|
106
|
+
}
|
|
107
|
+
return set;
|
|
108
|
+
}
|
|
109
|
+
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
110
|
+
async function verifyWebhook(request, options = {}) {
|
|
111
|
+
const env2 = typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
112
|
+
const issuer = (options.issuer ?? env2 ?? "").replace(/\/+$/, "");
|
|
113
|
+
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
114
|
+
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
115
|
+
if (!signature) throw new CauthyWebhookError("no signature");
|
|
116
|
+
const raw = new Uint8Array(await request.arrayBuffer());
|
|
117
|
+
const audience = options.url ?? request.url.split("?")[0];
|
|
118
|
+
let claims;
|
|
119
|
+
try {
|
|
120
|
+
const verified = await (0, import_jose2.jwtVerify)(signature, options.jwks ?? keysOf(issuer), {
|
|
121
|
+
algorithms: ["RS256"],
|
|
122
|
+
issuer,
|
|
123
|
+
audience,
|
|
124
|
+
typ: "cauthy-webhook+jwt",
|
|
125
|
+
requiredClaims: ["exp", "iat", "jti", "body_sha256"],
|
|
126
|
+
currentDate: options.now,
|
|
127
|
+
clockTolerance: 30
|
|
128
|
+
});
|
|
129
|
+
claims = verified.payload;
|
|
130
|
+
} catch (err) {
|
|
131
|
+
throw new CauthyWebhookError(err instanceof Error ? err.message : "bad signature");
|
|
132
|
+
}
|
|
133
|
+
const now = Math.floor((options.now ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
134
|
+
if (typeof claims.iat !== "number" || claims.iat > now + 30) throw new CauthyWebhookError("signed in the future");
|
|
135
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", raw));
|
|
136
|
+
let b64 = "";
|
|
137
|
+
for (const b of digest) b64 += String.fromCharCode(b);
|
|
138
|
+
const hash = btoa(b64).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
139
|
+
if (hash !== claims.body_sha256) throw new CauthyWebhookError("the body is not the one that was signed");
|
|
140
|
+
let body;
|
|
141
|
+
try {
|
|
142
|
+
body = JSON.parse(new TextDecoder().decode(raw));
|
|
143
|
+
} catch {
|
|
144
|
+
throw new CauthyWebhookError("the body is not JSON");
|
|
145
|
+
}
|
|
146
|
+
if (body.id !== claims.jti || body.event !== claims.evt || body.tenant_id !== claims.tid) {
|
|
147
|
+
throw new CauthyWebhookError("the body and its signature disagree");
|
|
148
|
+
}
|
|
149
|
+
const inner = body.data ?? {};
|
|
150
|
+
return {
|
|
151
|
+
id: String(body.id),
|
|
152
|
+
type: String(body.event),
|
|
153
|
+
tenantId: String(body.tenant_id),
|
|
154
|
+
createdAt: String(inner.created_at ?? body.created_at ?? ""),
|
|
155
|
+
...hex(inner.user_id) ? { userId: inner.user_id } : {},
|
|
156
|
+
...hex(inner.actor_id) ? { actorId: inner.actor_id } : {},
|
|
157
|
+
data: inner.data ?? {}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
86
161
|
// src/types.ts
|
|
87
162
|
var CauthyConfigError = class extends Error {
|
|
88
163
|
constructor(message) {
|
|
@@ -265,7 +340,8 @@ function createCauthy(given = {}) {
|
|
|
265
340
|
kind: String(o.kind ?? ""),
|
|
266
341
|
role: String(o.role ?? ""),
|
|
267
342
|
metadata: o.metadata ?? {},
|
|
268
|
-
...o.external_id ? { externalId: String(o.external_id) } : {}
|
|
343
|
+
...o.external_id ? { externalId: String(o.external_id) } : {},
|
|
344
|
+
...Array.isArray(o.permissions) ? { permissions: o.permissions.map(String), denied: Array.isArray(o.denied) ? o.denied.map(String) : [] } : {}
|
|
269
345
|
})
|
|
270
346
|
);
|
|
271
347
|
if (orgCache.size > 5e3) orgCache.clear();
|
|
@@ -391,8 +467,10 @@ function createCauthy(given = {}) {
|
|
|
391
467
|
const to = value === null ? urls.signIn(back) : `${basePath}/refresh?return_to=${encodeURIComponent(back)}`;
|
|
392
468
|
return { response: redirect(to) };
|
|
393
469
|
}
|
|
470
|
+
const webhook = (request, o = {}) => verifyWebhook(request, { issuer, url: o.url ?? `${originOf(request)}${new URL(request.url).pathname}`, jwks: o.jwks, now: o.now });
|
|
394
471
|
return {
|
|
395
472
|
handler,
|
|
473
|
+
webhook,
|
|
396
474
|
getSession,
|
|
397
475
|
status,
|
|
398
476
|
resolve: resolve2,
|
|
@@ -623,11 +701,48 @@ async function pushTheme(o) {
|
|
|
623
701
|
throw new Error(`cauthy-auth: Cauthy refused the theme (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
624
702
|
}
|
|
625
703
|
}
|
|
704
|
+
|
|
705
|
+
// src/permissions.ts
|
|
706
|
+
function matches(entry, resource, action) {
|
|
707
|
+
const i = entry.lastIndexOf(":");
|
|
708
|
+
if (i < 0) return false;
|
|
709
|
+
const r = entry.slice(0, i);
|
|
710
|
+
const a = entry.slice(i + 1);
|
|
711
|
+
return (r === "*" || r === resource) && (a === "*" || a === action);
|
|
712
|
+
}
|
|
713
|
+
function can(organization, resource, action) {
|
|
714
|
+
if (!organization?.permissions) return false;
|
|
715
|
+
if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
|
|
716
|
+
return organization.permissions.some((p) => matches(p, resource, action));
|
|
717
|
+
}
|
|
718
|
+
async function pushPolicy(o) {
|
|
719
|
+
const env2 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
720
|
+
const issuer = (o.issuer ?? env2("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
721
|
+
const system = o.system ?? env2("CAUTHY_SYSTEM") ?? "";
|
|
722
|
+
const tenant = o.tenant ?? env2("CAUTHY_TENANT") ?? "";
|
|
723
|
+
const token = o.token ?? env2("CAUTHY_TOKEN") ?? "";
|
|
724
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
725
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
|
|
726
|
+
}
|
|
727
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
|
|
728
|
+
method: "PUT",
|
|
729
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
|
|
730
|
+
body: JSON.stringify({ roles: o.roles })
|
|
731
|
+
});
|
|
732
|
+
if (!res.ok) {
|
|
733
|
+
const body = await res.json().catch(() => null);
|
|
734
|
+
throw new Error(`cauthy-auth: Cauthy refused the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
626
737
|
// Annotate the CommonJS export names for ESM import in node:
|
|
627
738
|
0 && (module.exports = {
|
|
628
739
|
CauthyConfigError,
|
|
740
|
+
CauthyWebhookError,
|
|
741
|
+
can,
|
|
629
742
|
createCauthy,
|
|
743
|
+
pushPolicy,
|
|
630
744
|
pushTheme,
|
|
631
|
-
themeFromStackmyth
|
|
745
|
+
themeFromStackmyth,
|
|
746
|
+
verifyWebhook
|
|
632
747
|
});
|
|
633
748
|
//# sourceMappingURL=index.js.map
|