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.
@@ -1,4 +1,44 @@
1
- import { S as Session, c as SessionStatus, b as SessionOrganization, P as PublicSession, a as CauthyOptions } from './types-BwCjBn-K.mjs';
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 { S as Session, c as SessionStatus, b as SessionOrganization, P as PublicSession, a as CauthyOptions } from './types-BwCjBn-K.js';
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);
@@ -1,6 +1,7 @@
1
1
  import { FastifyReply, FastifyPluginAsync } from 'fastify';
2
- import { C as Cauthy } from './cauthy-DsUNqjg6.mjs';
3
- import { S as Session } from './types-BwCjBn-K.mjs';
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-By2cryXN.js';
3
- import { S as Session } from './types-BwCjBn-K.js';
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-DsUNqjg6.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-BwCjBn-K.mjs';
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-By2cryXN.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-BwCjBn-K.js';
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