create-authhero 0.41.0 → 0.41.1

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.
@@ -2,18 +2,18 @@
2
2
  import { Command as R } from "commander";
3
3
  import m from "inquirer";
4
4
  import i from "fs";
5
- import a from "path";
6
- import { fileURLToPath as U } from "url";
7
- import { spawn as O } from "child_process";
5
+ import s from "path";
6
+ import { fileURLToPath as O } from "url";
7
+ import { spawn as U } from "child_process";
8
8
  const T = new R(), p = {
9
9
  local: {
10
10
  name: "Local (SQLite)",
11
11
  description: "Local development setup with SQLite database - great for getting started",
12
12
  templateDir: "local",
13
- packageJson: (s, e, o, r, n) => {
13
+ packageJson: (a, e, o, r, n) => {
14
14
  const t = r ? "workspace:*" : "latest";
15
15
  return {
16
- name: s,
16
+ name: a,
17
17
  version: "1.0.0",
18
18
  type: "module",
19
19
  scripts: {
@@ -50,10 +50,10 @@ const T = new R(), p = {
50
50
  name: "Cloudflare Workers (D1)",
51
51
  description: "Cloudflare Workers setup with D1 database",
52
52
  templateDir: "cloudflare",
53
- packageJson: (s, e, o, r, n) => {
53
+ packageJson: (a, e, o, r, n) => {
54
54
  const t = r ? "workspace:*" : "latest";
55
55
  return {
56
- name: s,
56
+ name: a,
57
57
  version: "1.0.0",
58
58
  type: "module",
59
59
  scripts: {
@@ -99,10 +99,10 @@ const T = new R(), p = {
99
99
  name: "AWS SST (Lambda + DynamoDB)",
100
100
  description: "Serverless AWS deployment with Lambda, DynamoDB, and SST",
101
101
  templateDir: "aws-sst",
102
- packageJson: (s, e, o, r, n) => {
102
+ packageJson: (a, e, o, r, n) => {
103
103
  const t = r ? "workspace:*" : "latest";
104
104
  return {
105
- name: s,
105
+ name: a,
106
106
  version: "1.0.0",
107
107
  type: "module",
108
108
  scripts: {
@@ -137,14 +137,14 @@ const T = new R(), p = {
137
137
  seedFile: "seed.ts"
138
138
  }
139
139
  };
140
- function P(s, e) {
141
- i.readdirSync(s).forEach((r) => {
142
- const n = a.join(s, r), t = a.join(e, r);
140
+ function P(a, e) {
141
+ i.readdirSync(a).forEach((r) => {
142
+ const n = s.join(a, r), t = s.join(e, r);
143
143
  i.lstatSync(n).isDirectory() ? (i.mkdirSync(t, { recursive: !0 }), P(n, t)) : i.copyFileSync(n, t);
144
144
  });
145
145
  }
146
- function L(s, e = !1, o = "authhero-local", r) {
147
- const n = s ? "control_plane" : "main", t = s ? "Control Plane" : "Main", c = [
146
+ function L(a, e = !1, o = "authhero-local", r) {
147
+ const n = a ? "control_plane" : "main", t = a ? "Control Plane" : "Main", c = [
148
148
  "https://manage.authhero.net/auth-callback",
149
149
  "https://local.authhero.net/auth-callback",
150
150
  "http://localhost:5173/auth-callback",
@@ -158,7 +158,7 @@ function L(s, e = !1, o = "authhero-local", r) {
158
158
  "https://local.authhero.net",
159
159
  "http://localhost:5173",
160
160
  "http://localhost:3000"
161
- ], C = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], y = [...h, ...C], A = e ? `
161
+ ], C = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], y = [...h, ...C], _ = e ? `
162
162
  // Create OpenID Conformance Suite test clients and user
163
163
  console.log("Creating conformance test clients and user...");
164
164
 
@@ -273,7 +273,7 @@ function L(s, e = !1, o = "authhero-local", r) {
273
273
  return `import { SqliteDialect, Kysely } from "kysely";
274
274
  import Database from "better-sqlite3";
275
275
  import createAdapters from "@authhero/kysely-adapter";
276
- import { seed } from "authhero";
276
+ import { seed${e ? ", USERNAME_PASSWORD_PROVIDER" : ""} } from "authhero";
277
277
 
278
278
  interface ExtraClient {
279
279
  client_id: string;
@@ -282,6 +282,7 @@ interface ExtraClient {
282
282
  callbacks?: string[];
283
283
  allowed_logout_urls?: string[];
284
284
  web_origins?: string[];
285
+ auth0_conformant?: boolean;
285
286
  }
286
287
 
287
288
  function parseFlag(name: string): string | undefined {
@@ -333,7 +334,7 @@ async function main() {
333
334
  adminPassword,
334
335
  tenantId: "${n}",
335
336
  tenantName: "${t}",
336
- isControlPlane: ${!!s},
337
+ isControlPlane: ${!!a},
337
338
  clientId: "default",
338
339
  callbacks: ${JSON.stringify(f)},
339
340
  allowedLogoutUrls: ${JSON.stringify(y)},
@@ -354,6 +355,9 @@ async function main() {
354
355
  web_origins: c.web_origins ?? [],
355
356
  connections: ["Username-Password-Authentication"],
356
357
  client_metadata: { universal_login_version: "2" },
358
+ ...(c.auth0_conformant !== undefined && {
359
+ auth0_conformant: c.auth0_conformant,
360
+ }),
357
361
  });
358
362
  console.log(\`✅ Created client "\${c.client_id}"\`);
359
363
  }
@@ -362,7 +366,7 @@ async function main() {
362
366
  await adapters.users.update(seedResult.tenantId, seedResult.userId, userProfile);
363
367
  console.log(\`✅ Updated profile of user "\${seedResult.username}"\`);
364
368
  }
365
- ${A}
369
+ ${_}
366
370
  await db.destroy();
367
371
  }
368
372
 
@@ -372,7 +376,7 @@ main().catch((err) => {
372
376
  });
373
377
  `;
374
378
  }
375
- function $(s, e) {
379
+ function $(a, e) {
376
380
  const o = e ? `import fs from "fs";
377
381
  ` : "", r = e ? `
378
382
  const adminDistPath = path.resolve(
@@ -390,7 +394,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
390
394
  .replace(/href="\\.\\//g, 'href="/admin/');
391
395
  const configJson = JSON.stringify({
392
396
  domain: issuer.replace(/\\/$/, ""),
393
- clientId: ${s ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
397
+ clientId: ${a ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
394
398
  basePath: "/admin",
395
399
  }).replace(/</g, "\\\\u003c");
396
400
  configWithHandlers.adminIndexHtml = rawHtml.replace(
@@ -403,7 +407,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
403
407
  });
404
408
  }
405
409
  ` : "";
406
- return s ? `import { Context } from "hono";
410
+ return a ? `import { Context } from "hono";
407
411
  import { swaggerUI } from "@hono/swagger-ui";
408
412
  import { AuthHeroConfig, DataAdapters } from "authhero";
409
413
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -508,7 +512,7 @@ ${n}
508
512
  }
509
513
  `;
510
514
  }
511
- function j(s) {
515
+ function j(a) {
512
516
  return `import { D1Dialect } from "kysely-d1";
513
517
  import { Kysely } from "kysely";
514
518
  import createAdapters from "@authhero/kysely-adapter";
@@ -535,9 +539,9 @@ export default {
535
539
  adminUsername,
536
540
  adminPassword,
537
541
  issuer,
538
- tenantId: "${s ? "control_plane" : "main"}",
539
- tenantName: "${s ? "Control Plane" : "Main"}",
540
- isControlPlane: ${!!s},
542
+ tenantId: "${a ? "control_plane" : "main"}",
543
+ tenantName: "${a ? "Control Plane" : "Main"}",
544
+ isControlPlane: ${!!a},
541
545
  clientId: "default",
542
546
  });
543
547
 
@@ -569,11 +573,11 @@ export default {
569
573
  };
570
574
  `;
571
575
  }
572
- function H(s, e) {
576
+ function H(a, e) {
573
577
  const o = e ? `import adminIndexHtml from "./admin-index-html";
574
578
  ` : "", r = e ? ` adminIndexHtml,
575
579
  ` : "";
576
- return s ? `import { Context } from "hono";
580
+ return a ? `import { Context } from "hono";
577
581
  import { swaggerUI } from "@hono/swagger-ui";
578
582
  import { AuthHeroConfig, DataAdapters } from "authhero";
579
583
  import { initMultiTenant } from "@authhero/multi-tenancy";
@@ -654,8 +658,8 @@ ${r} });
654
658
  }
655
659
  `;
656
660
  }
657
- function F(s) {
658
- return s ? `import { Context } from "hono";
661
+ function M(a) {
662
+ return a ? `import { Context } from "hono";
659
663
  import { swaggerUI } from "@hono/swagger-ui";
660
664
  import { AuthHeroConfig, DataAdapters } from "authhero";
661
665
  import { initMultiTenant } from "@authhero/multi-tenancy";
@@ -760,7 +764,7 @@ export default function createApp(config: AppConfig) {
760
764
  }
761
765
  `;
762
766
  }
763
- function M(s) {
767
+ function F(a) {
764
768
  return `import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
765
769
  import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
766
770
  import createAdapters from "@authhero/aws";
@@ -789,9 +793,9 @@ async function main() {
789
793
  await seed(adapters, {
790
794
  adminUsername,
791
795
  adminPassword,
792
- tenantId: "${s ? "control_plane" : "main"}",
793
- tenantName: "${s ? "Control Plane" : "Main"}",
794
- isControlPlane: ${!!s},
796
+ tenantId: "${a ? "control_plane" : "main"}",
797
+ tenantName: "${a ? "Control Plane" : "Main"}",
798
+ isControlPlane: ${!!a},
795
799
  });
796
800
 
797
801
  console.log("✅ Database seeded successfully!");
@@ -800,21 +804,21 @@ async function main() {
800
804
  main().catch(console.error);
801
805
  `;
802
806
  }
803
- function W(s, e) {
804
- const o = a.join(s, "src");
807
+ function W(a, e) {
808
+ const o = s.join(a, "src");
805
809
  i.writeFileSync(
806
- a.join(o, "app.ts"),
807
- F(e)
808
- ), i.writeFileSync(
809
- a.join(o, "seed.ts"),
810
+ s.join(o, "app.ts"),
810
811
  M(e)
812
+ ), i.writeFileSync(
813
+ s.join(o, "seed.ts"),
814
+ F(e)
811
815
  );
812
816
  }
813
- function I() {
817
+ function k() {
814
818
  console.log("\\n" + "─".repeat(50)), console.log("🔐 AuthHero deployed to AWS!"), console.log("📚 Check SST output for your API URL"), console.log("🚀 Open your server URL /setup to complete initial setup"), console.log("🌐 Portal available at https://local.authhero.net"), console.log("─".repeat(50) + "\\n");
815
819
  }
816
- function q(s) {
817
- const e = a.join(s, ".github", "workflows");
820
+ function q(a) {
821
+ const e = s.join(a, ".github", "workflows");
818
822
  i.mkdirSync(e, { recursive: !0 });
819
823
  const o = `name: Unit tests
820
824
 
@@ -902,9 +906,9 @@ jobs:
902
906
  apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
903
907
  command: deploy --env production
904
908
  `;
905
- i.writeFileSync(a.join(e, "unit-tests.yml"), o), i.writeFileSync(a.join(e, "deploy-dev.yml"), r), i.writeFileSync(a.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
909
+ i.writeFileSync(s.join(e, "unit-tests.yml"), o), i.writeFileSync(s.join(e, "deploy-dev.yml"), r), i.writeFileSync(s.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
906
910
  }
907
- function J(s) {
911
+ function J(a) {
908
912
  const e = {
909
913
  branches: ["main"],
910
914
  plugins: [
@@ -914,10 +918,10 @@ function J(s) {
914
918
  ]
915
919
  };
916
920
  i.writeFileSync(
917
- a.join(s, ".releaserc.json"),
921
+ s.join(a, ".releaserc.json"),
918
922
  JSON.stringify(e, null, 2)
919
923
  );
920
- const o = a.join(s, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
924
+ const o = s.join(a, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
921
925
  r.devDependencies = {
922
926
  ...r.devDependencies,
923
927
  "semantic-release": "^24.0.0"
@@ -927,9 +931,9 @@ function J(s) {
927
931
  "type-check": "tsc --noEmit"
928
932
  }, i.writeFileSync(o, JSON.stringify(r, null, 2));
929
933
  }
930
- function b(s, e) {
934
+ function b(a, e) {
931
935
  return new Promise((o, r) => {
932
- const n = O(s, [], {
936
+ const n = U(a, [], {
933
937
  cwd: e,
934
938
  shell: !0,
935
939
  stdio: "inherit"
@@ -939,13 +943,13 @@ function b(s, e) {
939
943
  }), n.on("error", r);
940
944
  });
941
945
  }
942
- function z(s, e, o) {
943
- const r = a.join(s, "src");
946
+ function z(a, e, o) {
947
+ const r = s.join(a, "src");
944
948
  i.writeFileSync(
945
- a.join(r, "app.ts"),
949
+ s.join(r, "app.ts"),
946
950
  H(e, o)
947
951
  ), i.writeFileSync(
948
- a.join(r, "seed.ts"),
952
+ s.join(r, "seed.ts"),
949
953
  j(e)
950
954
  );
951
955
  }
@@ -968,12 +972,12 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
968
972
  ).option(
969
973
  "--workspace",
970
974
  "use workspace:* dependencies for local monorepo development"
971
- ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (s, e) => {
975
+ ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (a, e) => {
972
976
  const o = e.yes === !0;
973
977
  console.log(`
974
978
  🔐 Welcome to AuthHero!
975
979
  `);
976
- let r = s;
980
+ let r = a;
977
981
  r || (o ? (r = "auth-server", console.log(`Using default project name: ${r}`)) : r = (await m.prompt([
978
982
  {
979
983
  type: "input",
@@ -983,7 +987,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
983
987
  validate: (u) => u !== "" || "Project name cannot be empty"
984
988
  }
985
989
  ])).projectName);
986
- const n = a.join(process.cwd(), r);
990
+ const n = s.join(process.cwd(), r);
987
991
  i.existsSync(n) && (console.error(`❌ Project "${r}" already exists.`), process.exit(1));
988
992
  let t;
989
993
  e.template ? (["local", "cloudflare", "aws-sst"].includes(e.template) || (console.error(`❌ Invalid template: ${e.template}`), console.error("Valid options: local, cloudflare, aws-sst"), process.exit(1)), t = e.template, console.log(`Using template: ${p[t].name}`)) : t = (await m.prompt([
@@ -1039,7 +1043,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1039
1043
  C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
1040
1044
  const y = p[t];
1041
1045
  i.mkdirSync(n, { recursive: !0 }), i.writeFileSync(
1042
- a.join(n, "package.json"),
1046
+ s.join(n, "package.json"),
1043
1047
  JSON.stringify(
1044
1048
  y.packageJson(
1045
1049
  r,
@@ -1052,40 +1056,40 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1052
1056
  2
1053
1057
  )
1054
1058
  );
1055
- const A = y.templateDir, S = a.dirname(U(import.meta.url)), x = [
1056
- a.join(S, A),
1057
- a.join(S, "..", "templates", A)
1058
- ], k = x.find((l) => i.existsSync(l));
1059
- if (k ? P(k, n) : (console.error(
1059
+ const _ = y.templateDir, S = s.dirname(O(import.meta.url)), x = [
1060
+ s.join(S, _),
1061
+ s.join(S, "..", "templates", _)
1062
+ ], I = x.find((l) => i.existsSync(l));
1063
+ if (I ? P(I, n) : (console.error(
1060
1064
  `❌ Template directory not found. Looked in:
1061
1065
  ${x.join(`
1062
1066
  `)}`
1063
1067
  ), process.exit(1)), t === "cloudflare" && z(n, c, d), t === "cloudflare") {
1064
- const l = a.join(n, "wrangler.toml"), u = a.join(n, "wrangler.local.toml");
1068
+ const l = s.join(n, "wrangler.toml"), u = s.join(n, "wrangler.local.toml");
1065
1069
  i.existsSync(l) && i.copyFileSync(l, u);
1066
- const g = a.join(n, ".dev.vars.example"), w = a.join(n, ".dev.vars");
1070
+ const g = s.join(n, ".dev.vars.example"), w = s.join(n, ".dev.vars");
1067
1071
  i.existsSync(g) && i.copyFileSync(g, w), console.log(
1068
1072
  "📁 Created wrangler.local.toml and .dev.vars for local development"
1069
1073
  );
1070
1074
  }
1071
- let _ = !1;
1072
- if (t === "cloudflare" && (e.githubCi !== void 0 ? (_ = e.githubCi, _ && console.log("Including GitHub CI workflows with semantic versioning")) : o || (_ = (await m.prompt([
1075
+ let A = !1;
1076
+ if (t === "cloudflare" && (e.githubCi !== void 0 ? (A = e.githubCi, A && console.log("Including GitHub CI workflows with semantic versioning")) : o || (A = (await m.prompt([
1073
1077
  {
1074
1078
  type: "confirm",
1075
1079
  name: "includeGithubCi",
1076
1080
  message: "Would you like to include GitHub CI with semantic versioning?",
1077
1081
  default: !1
1078
1082
  }
1079
- ])).includeGithubCi), _ && (q(n), J(n))), t === "local") {
1083
+ ])).includeGithubCi), A && (q(n), J(n))), t === "local") {
1080
1084
  const l = L(
1081
1085
  c,
1082
1086
  f,
1083
1087
  h,
1084
1088
  d
1085
1089
  );
1086
- i.writeFileSync(a.join(n, "src/seed.ts"), l);
1090
+ i.writeFileSync(s.join(n, "src/seed.ts"), l);
1087
1091
  const u = $(c, d);
1088
- i.writeFileSync(a.join(n, "src/app.ts"), u);
1092
+ i.writeFileSync(s.join(n, "src/app.ts"), u);
1089
1093
  }
1090
1094
  if (t === "aws-sst" && W(n, c), f) {
1091
1095
  const l = {
@@ -1107,7 +1111,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1107
1111
  }
1108
1112
  };
1109
1113
  i.writeFileSync(
1110
- a.join(n, "conformance-config.json"),
1114
+ s.join(n, "conformance-config.json"),
1111
1115
  JSON.stringify(l, null, 2)
1112
1116
  ), console.log(
1113
1117
  "📝 Created conformance-config.json for OpenID Conformance Suite"
@@ -1174,10 +1178,10 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1174
1178
  message: "Would you like to start the development server?",
1175
1179
  default: !0
1176
1180
  }
1177
- ])).shouldStart, g && (t === "cloudflare" ? D() : t === "aws-sst" ? I() : N(), console.log(`🚀 Starting development server...
1181
+ ])).shouldStart, g && (t === "cloudflare" ? D() : t === "aws-sst" ? k() : N(), console.log(`🚀 Starting development server...
1178
1182
  `), await b(`${l} run dev`, n)), o && !g && (console.log(`
1179
1183
  ✅ Setup complete!`), console.log(`
1180
- To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ? D() : t === "aws-sst" ? I() : N());
1184
+ To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ? D() : t === "aws-sst" ? k() : N());
1181
1185
  } catch (u) {
1182
1186
  console.error(`
1183
1187
  ❌ An error occurred:`, u), process.exit(1);
@@ -4,6 +4,76 @@ import { Kysely } from "kysely";
4
4
  import Database from "better-sqlite3";
5
5
  import createAdapters from "@authhero/kysely-adapter";
6
6
  import createApp from "./app";
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import { execFileSync } from "child_process";
10
+ import https from "https";
11
+
12
+ // Generate self-signed certificates for local HTTPS if they don't exist
13
+ const certDir = path.join(process.cwd(), ".certs");
14
+ const keyPath = path.join(certDir, "localhost-key.pem");
15
+ const certPath = path.join(certDir, "localhost.pem");
16
+
17
+ function ensureCertificates() {
18
+ if (!fs.existsSync(certDir)) {
19
+ fs.mkdirSync(certDir, { recursive: true });
20
+ }
21
+
22
+ if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
23
+ console.log("🔑 Generating self-signed certificates for local HTTPS...");
24
+
25
+ // Try mkcert first (if installed), otherwise fall back to openssl
26
+ try {
27
+ execFileSync("which", ["mkcert"], { stdio: "ignore" });
28
+ execFileSync(
29
+ "mkcert",
30
+ ["-key-file", keyPath, "-cert-file", certPath, "localhost", "127.0.0.1"],
31
+ { stdio: "inherit" },
32
+ );
33
+ console.log("✅ Certificates generated with mkcert");
34
+ } catch {
35
+ // Fall back to openssl
36
+ try {
37
+ execFileSync(
38
+ "openssl",
39
+ [
40
+ "req",
41
+ "-x509",
42
+ "-newkey",
43
+ "rsa:2048",
44
+ "-keyout",
45
+ keyPath,
46
+ "-out",
47
+ certPath,
48
+ "-days",
49
+ "365",
50
+ "-nodes",
51
+ "-subj",
52
+ "/CN=localhost",
53
+ ],
54
+ { stdio: "inherit" },
55
+ );
56
+ console.log("✅ Self-signed certificates generated with openssl");
57
+ console.log(
58
+ "⚠️ You may need to trust the certificate in your browser",
59
+ );
60
+ } catch (err) {
61
+ console.error(
62
+ "❌ Failed to generate certificates. Please install mkcert or openssl",
63
+ );
64
+ console.error(
65
+ " Install mkcert: brew install mkcert && mkcert -install",
66
+ );
67
+ process.exit(1);
68
+ }
69
+ }
70
+ }
71
+
72
+ return {
73
+ key: fs.readFileSync(keyPath),
74
+ cert: fs.readFileSync(certPath),
75
+ };
76
+ }
7
77
 
8
78
  // Initialize SQLite database
9
79
  let db: Kysely<any>;
@@ -34,15 +104,19 @@ const app = createApp({
34
104
  "https://manage.authhero.net",
35
105
  "https://local.authhero.net",
36
106
  "http://localhost:5173",
107
+ "https://localhost:5173",
37
108
  ],
38
109
  });
39
110
 
40
111
  // Start the server
41
112
  const port = Number(process.env.PORT) || 3000;
42
- const issuer = process.env.ISSUER || `http://localhost:${port}/`;
113
+ const issuer = process.env.ISSUER || `https://localhost:${port}/`;
114
+
115
+ // Get or generate certificates
116
+ const { key, cert } = ensureCertificates();
43
117
 
44
- console.log(`🔐 AuthHero server running at http://localhost:${port}`);
45
- console.log(`📚 API documentation available at http://localhost:${port}/docs`);
118
+ console.log(`🔐 AuthHero server running at https://localhost:${port}`);
119
+ console.log(`📚 API documentation available at https://localhost:${port}/docs`);
46
120
  console.log(`🌐 Portal available at https://local.authhero.net`);
47
121
 
48
122
  serve({
@@ -53,4 +127,11 @@ serve({
53
127
  });
54
128
  },
55
129
  port,
130
+ // Bind to all IPv4 interfaces explicitly. Node's default (`::`) is
131
+ // supposed to be dual-stack but on Docker Desktop Mac the docker bridge
132
+ // gateway IPv4 (e.g. 192.168.65.254) often isn't reachable that way,
133
+ // causing connections from inside the suite container to be refused.
134
+ hostname: "0.0.0.0",
135
+ createServer: https.createServer,
136
+ serverOptions: { key, cert },
56
137
  });
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "https://github.com/markusahlstrand/authhero"
7
7
  },
8
- "version": "0.41.0",
8
+ "version": "0.41.1",
9
9
  "type": "module",
10
10
  "main": "dist/create-authhero.js",
11
11
  "bin": {