create-authhero 0.24.0 → 0.26.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,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { Command as P } from "commander";
2
+ import { Command as L } from "commander";
3
3
  import u from "inquirer";
4
4
  import s from "fs";
5
- import i from "path";
6
- import { spawn as I } from "child_process";
7
- const N = new P(), p = {
5
+ import l from "path";
6
+ import { spawn as N } from "child_process";
7
+ const T = new L(), p = {
8
8
  local: {
9
9
  name: "Local (SQLite)",
10
10
  description: "Local development setup with SQLite database - great for getting started",
11
11
  templateDir: "local",
12
- packageJson: (t, e) => ({
12
+ packageJson: (t, e, o) => ({
13
13
  name: t,
14
14
  version: "1.0.0",
15
15
  type: "module",
@@ -29,7 +29,8 @@ const N = new P(), p = {
29
29
  "better-sqlite3": "latest",
30
30
  hono: "^4.6.0",
31
31
  kysely: "latest",
32
- ...e && { "@authhero/multi-tenancy": "latest" }
32
+ ...e && { "@authhero/multi-tenancy": "latest" },
33
+ ...o && { bcryptjs: "latest" }
33
34
  },
34
35
  devDependencies: {
35
36
  "@types/better-sqlite3": "^7.6.0",
@@ -44,7 +45,7 @@ const N = new P(), p = {
44
45
  name: "Cloudflare Workers (D1)",
45
46
  description: "Cloudflare Workers setup with D1 database",
46
47
  templateDir: "cloudflare",
47
- packageJson: (t, e) => ({
48
+ packageJson: (t, e, o) => ({
48
49
  name: t,
49
50
  version: "1.0.0",
50
51
  type: "module",
@@ -72,7 +73,8 @@ const N = new P(), p = {
72
73
  hono: "^4.6.0",
73
74
  kysely: "latest",
74
75
  "kysely-d1": "latest",
75
- ...e && { "@authhero/multi-tenancy": "latest" }
76
+ ...e && { "@authhero/multi-tenancy": "latest" },
77
+ ...o && { bcryptjs: "latest" }
76
78
  },
77
79
  devDependencies: {
78
80
  "@cloudflare/workers-types": "^4.0.0",
@@ -88,7 +90,7 @@ const N = new P(), p = {
88
90
  name: "AWS SST (Lambda + DynamoDB)",
89
91
  description: "Serverless AWS deployment with Lambda, DynamoDB, and SST",
90
92
  templateDir: "aws-sst",
91
- packageJson: (t, e) => ({
93
+ packageJson: (t, e, o) => ({
92
94
  name: t,
93
95
  version: "1.0.0",
94
96
  type: "module",
@@ -108,7 +110,8 @@ const N = new P(), p = {
108
110
  "@hono/zod-openapi": "^0.19.0",
109
111
  authhero: "latest",
110
112
  hono: "^4.6.0",
111
- ...e && { "@authhero/multi-tenancy": "latest" }
113
+ ...e && { "@authhero/multi-tenancy": "latest" },
114
+ ...o && { bcryptjs: "latest" }
112
115
  },
113
116
  devDependencies: {
114
117
  "@types/aws-lambda": "^8.10.0",
@@ -121,13 +124,130 @@ const N = new P(), p = {
121
124
  seedFile: "seed.ts"
122
125
  }
123
126
  };
124
- function k(t, e) {
125
- s.readdirSync(t).forEach((a) => {
126
- const n = i.join(t, a), o = i.join(e, a);
127
- s.lstatSync(n).isDirectory() ? (s.mkdirSync(o, { recursive: !0 }), k(n, o)) : s.copyFileSync(n, o);
127
+ function E(t, e) {
128
+ s.readdirSync(t).forEach((r) => {
129
+ const n = l.join(t, r), a = l.join(e, r);
130
+ s.lstatSync(n).isDirectory() ? (s.mkdirSync(a, { recursive: !0 }), E(n, a)) : s.copyFileSync(n, a);
128
131
  });
129
132
  }
130
- function _(t) {
133
+ function R(t, e = !1, o = "authhero-local") {
134
+ const r = t ? "control_plane" : "main", n = t ? "Control Plane" : "Main", a = [
135
+ "https://manage.authhero.net/auth-callback",
136
+ "https://local.authhero.net/auth-callback",
137
+ "http://localhost:5173/auth-callback",
138
+ "https://localhost:3000/auth-callback"
139
+ ], c = e ? [
140
+ `https://localhost.emobix.co.uk:8443/test/a/${o}/callback`,
141
+ `https://localhost:8443/test/a/${o}/callback`
142
+ ] : [], g = [...a, ...c], w = [
143
+ "https://manage.authhero.net",
144
+ "https://local.authhero.net",
145
+ "http://localhost:5173",
146
+ "https://localhost:3000"
147
+ ], y = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], x = [...w, ...y], v = e ? `
148
+ // Create OpenID Conformance Suite test clients and user
149
+ console.log("Creating conformance test clients and user...");
150
+
151
+ const conformanceCallbacks = [
152
+ "https://localhost.emobix.co.uk:8443/test/a/${o}/callback",
153
+ "https://localhost:8443/test/a/${o}/callback",
154
+ ];
155
+ const conformanceLogoutUrls = [
156
+ "https://localhost:8443/",
157
+ "https://localhost.emobix.co.uk:8443/",
158
+ ];
159
+ const conformanceWebOrigins = [
160
+ "https://localhost:8443",
161
+ "https://localhost.emobix.co.uk:8443",
162
+ ];
163
+
164
+ try {
165
+ await adapters.clients.create("${r}", {
166
+ client_id: "conformance-test",
167
+ client_secret: "conformanceTestSecret123",
168
+ name: "Conformance Test Client",
169
+ callbacks: conformanceCallbacks,
170
+ allowed_logout_urls: conformanceLogoutUrls,
171
+ web_origins: conformanceWebOrigins,
172
+ });
173
+ console.log("✅ Created conformance-test client");
174
+ } catch (e: any) {
175
+ if (e.message?.includes("UNIQUE constraint")) {
176
+ console.log("ℹ️ conformance-test client already exists");
177
+ } else {
178
+ throw e;
179
+ }
180
+ }
181
+
182
+ try {
183
+ await adapters.clients.create("${r}", {
184
+ client_id: "conformance-test2",
185
+ client_secret: "conformanceTestSecret456",
186
+ name: "Conformance Test Client 2",
187
+ callbacks: conformanceCallbacks,
188
+ allowed_logout_urls: conformanceLogoutUrls,
189
+ web_origins: conformanceWebOrigins,
190
+ });
191
+ console.log("✅ Created conformance-test2 client");
192
+ } catch (e: any) {
193
+ if (e.message?.includes("UNIQUE constraint")) {
194
+ console.log("ℹ️ conformance-test2 client already exists");
195
+ } else {
196
+ throw e;
197
+ }
198
+ }
199
+
200
+ // Create a conformance test user with ALL OIDC profile claims populated
201
+ // This is required for OIDCC-5.4 (VerifyScopesReturnedInUserInfoClaims) test
202
+ try {
203
+ await adapters.users.create("${r}", {
204
+ user_id: "auth2|conformance-user",
205
+ email: "conformance@example.com",
206
+ email_verified: true,
207
+ name: "Conformance Test User",
208
+ given_name: "Conformance",
209
+ family_name: "User",
210
+ middle_name: "Test",
211
+ nickname: "conformance",
212
+ username: "conformance_user",
213
+ picture: "https://example.com/conformance.png",
214
+ profile: "https://example.com/conformance",
215
+ website: "https://example.com",
216
+ gender: "other",
217
+ birthdate: "2000-01-01",
218
+ zoneinfo: "Europe/London",
219
+ locale: "en-US",
220
+ connection: "Username-Password-Authentication",
221
+ provider: "auth2",
222
+ is_social: false,
223
+ });
224
+ console.log("✅ Created conformance test user (conformance@example.com)");
225
+ } catch (e: any) {
226
+ if (e.message?.includes("UNIQUE constraint")) {
227
+ console.log("ℹ️ conformance test user already exists");
228
+ } else {
229
+ throw e;
230
+ }
231
+ }
232
+
233
+ // Create password for conformance test user
234
+ // Password: ConformanceTest123!
235
+ try {
236
+ const bcrypt = await import("bcryptjs");
237
+ const hashedPassword = await bcrypt.hash("ConformanceTest123!", 10);
238
+ await adapters.passwords.create("${r}", {
239
+ user_id: "auth2|conformance-user",
240
+ password: hashedPassword,
241
+ });
242
+ console.log("✅ Created password for conformance test user");
243
+ } catch (e: any) {
244
+ if (e.message?.includes("UNIQUE constraint")) {
245
+ console.log("ℹ️ conformance test user password already exists");
246
+ } else {
247
+ throw e;
248
+ }
249
+ }
250
+ ` : "";
131
251
  return `import { SqliteDialect, Kysely } from "kysely";
132
252
  import Database from "better-sqlite3";
133
253
  import createAdapters from "@authhero/kysely-adapter";
@@ -153,32 +273,38 @@ async function main() {
153
273
  await seed(adapters, {
154
274
  adminEmail,
155
275
  adminPassword,
156
- tenantId: "${t ? "control_plane" : "main"}",
157
- tenantName: "${t ? "Control Plane" : "Main"}",
276
+ tenantId: "${r}",
277
+ tenantName: "${n}",
158
278
  isControlPlane: ${t},
279
+ callbacks: ${JSON.stringify(g)},
280
+ allowedLogoutUrls: ${JSON.stringify(x)},
159
281
  });
160
-
282
+ ${v}
161
283
  await db.destroy();
162
284
  }
163
285
 
164
286
  main().catch(console.error);
165
287
  `;
166
288
  }
167
- function R(t) {
289
+ function O(t) {
168
290
  return t ? `import { Context } from "hono";
169
291
  import { swaggerUI } from "@hono/swagger-ui";
170
292
  import { AuthHeroConfig, DataAdapters } from "authhero";
171
293
  import { serveStatic } from "@hono/node-server/serve-static";
172
294
  import { initMultiTenant } from "@authhero/multi-tenancy";
173
295
 
174
- // Control plane tenant ID - the tenant that manages all other tenants
296
+ // Control plane configuration
175
297
  const CONTROL_PLANE_TENANT_ID = "control_plane";
298
+ const CONTROL_PLANE_CLIENT_ID = "default_client";
176
299
 
177
300
  export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAdapters }) {
178
301
  // Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
179
302
  const { app } = initMultiTenant({
180
303
  ...config,
181
- controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
304
+ controlPlane: {
305
+ tenantId: CONTROL_PLANE_TENANT_ID,
306
+ clientId: CONTROL_PLANE_CLIENT_ID,
307
+ },
182
308
  });
183
309
 
184
310
  app
@@ -264,7 +390,7 @@ export default function createApp(config: AuthHeroConfig) {
264
390
  }
265
391
  `;
266
392
  }
267
- function M(t) {
393
+ function j(t) {
268
394
  return `import { D1Dialect } from "kysely-d1";
269
395
  import { Kysely } from "kysely";
270
396
  import createAdapters from "@authhero/kysely-adapter";
@@ -337,20 +463,24 @@ export default {
337
463
  };
338
464
  `;
339
465
  }
340
- function j(t) {
466
+ function U(t) {
341
467
  return t ? `import { Context } from "hono";
342
468
  import { swaggerUI } from "@hono/swagger-ui";
343
469
  import { AuthHeroConfig, DataAdapters } from "authhero";
344
470
  import { initMultiTenant } from "@authhero/multi-tenancy";
345
471
 
346
- // Control plane tenant ID - the tenant that manages all other tenants
472
+ // Control plane configuration
347
473
  const CONTROL_PLANE_TENANT_ID = "control_plane";
474
+ const CONTROL_PLANE_CLIENT_ID = "default_client";
348
475
 
349
476
  export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAdapters }) {
350
477
  // Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
351
478
  const { app } = initMultiTenant({
352
479
  ...config,
353
- controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
480
+ controlPlane: {
481
+ tenantId: CONTROL_PLANE_TENANT_ID,
482
+ clientId: CONTROL_PLANE_CLIENT_ID,
483
+ },
354
484
  });
355
485
 
356
486
  app
@@ -413,14 +543,15 @@ export default function createApp(config: AuthHeroConfig) {
413
543
  }
414
544
  `;
415
545
  }
416
- function L(t) {
546
+ function M(t) {
417
547
  return t ? `import { Context } from "hono";
418
548
  import { swaggerUI } from "@hono/swagger-ui";
419
549
  import { AuthHeroConfig, DataAdapters } from "authhero";
420
550
  import { initMultiTenant } from "@authhero/multi-tenancy";
421
551
 
422
- // Control plane tenant ID - the tenant that manages all other tenants
552
+ // Control plane configuration
423
553
  const CONTROL_PLANE_TENANT_ID = "control_plane";
554
+ const CONTROL_PLANE_CLIENT_ID = "default_client";
424
555
 
425
556
  interface AppConfig extends AuthHeroConfig {
426
557
  dataAdapter: DataAdapters;
@@ -431,7 +562,10 @@ export default function createApp(config: AppConfig) {
431
562
  // Initialize multi-tenant AuthHero
432
563
  const { app } = initMultiTenant({
433
564
  ...config,
434
- controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
565
+ controlPlane: {
566
+ tenantId: CONTROL_PLANE_TENANT_ID,
567
+ clientId: CONTROL_PLANE_CLIENT_ID,
568
+ },
435
569
  });
436
570
 
437
571
  app
@@ -561,23 +695,23 @@ async function main() {
561
695
  main().catch(console.error);
562
696
  `;
563
697
  }
564
- function O(t, e) {
565
- const r = i.join(t, "src");
698
+ function H(t, e) {
699
+ const o = l.join(t, "src");
566
700
  s.writeFileSync(
567
- i.join(r, "app.ts"),
568
- L(e)
701
+ l.join(o, "app.ts"),
702
+ M(e)
569
703
  ), s.writeFileSync(
570
- i.join(r, "seed.ts"),
704
+ l.join(o, "seed.ts"),
571
705
  $(e)
572
706
  );
573
707
  }
574
- function C(t) {
708
+ function I(t) {
575
709
  console.log("\\n" + "─".repeat(50)), console.log("🔐 AuthHero deployed to AWS!"), console.log("📚 Check SST output for your API URL"), console.log("🌐 Portal available at https://local.authhero.net"), console.log(t ? "🏢 Multi-tenant mode enabled with control_plane tenant" : "🏠 Single-tenant mode with 'main' tenant"), console.log("─".repeat(50) + "\\n");
576
710
  }
577
- function H(t) {
578
- const e = i.join(t, ".github", "workflows");
711
+ function F(t) {
712
+ const e = l.join(t, ".github", "workflows");
579
713
  s.mkdirSync(e, { recursive: !0 });
580
- const r = `name: Unit tests
714
+ const o = `name: Unit tests
581
715
 
582
716
  on: push
583
717
 
@@ -598,7 +732,7 @@ jobs:
598
732
 
599
733
  - run: npm run type-check
600
734
  - run: npm test
601
- `, a = `name: Deploy to Dev
735
+ `, r = `name: Deploy to Dev
602
736
 
603
737
  on:
604
738
  push:
@@ -663,9 +797,9 @@ jobs:
663
797
  apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
664
798
  command: deploy --env production
665
799
  `;
666
- s.writeFileSync(i.join(e, "unit-tests.yml"), r), s.writeFileSync(i.join(e, "deploy-dev.yml"), a), s.writeFileSync(i.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
800
+ s.writeFileSync(l.join(e, "unit-tests.yml"), o), s.writeFileSync(l.join(e, "deploy-dev.yml"), r), s.writeFileSync(l.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
667
801
  }
668
- function U(t) {
802
+ function W(t) {
669
803
  const e = {
670
804
  branches: ["main"],
671
805
  plugins: [
@@ -675,74 +809,77 @@ function U(t) {
675
809
  ]
676
810
  };
677
811
  s.writeFileSync(
678
- i.join(t, ".releaserc.json"),
812
+ l.join(t, ".releaserc.json"),
679
813
  JSON.stringify(e, null, 2)
680
814
  );
681
- const r = i.join(t, "package.json"), a = JSON.parse(s.readFileSync(r, "utf-8"));
682
- a.devDependencies = {
683
- ...a.devDependencies,
815
+ const o = l.join(t, "package.json"), r = JSON.parse(s.readFileSync(o, "utf-8"));
816
+ r.devDependencies = {
817
+ ...r.devDependencies,
684
818
  "semantic-release": "^24.0.0"
685
- }, a.scripts = {
686
- ...a.scripts,
819
+ }, r.scripts = {
820
+ ...r.scripts,
687
821
  test: 'echo "No tests yet"',
688
822
  "type-check": "tsc --noEmit"
689
- }, s.writeFileSync(r, JSON.stringify(a, null, 2));
823
+ }, s.writeFileSync(o, JSON.stringify(r, null, 2));
690
824
  }
691
- function v(t, e) {
692
- return new Promise((r, a) => {
693
- const n = I(t, [], {
825
+ function b(t, e) {
826
+ return new Promise((o, r) => {
827
+ const n = N(t, [], {
694
828
  cwd: e,
695
829
  shell: !0,
696
830
  stdio: "inherit"
697
831
  });
698
- n.on("close", (o) => {
699
- o === 0 ? r() : a(new Error(`Command failed with exit code ${o}`));
700
- }), n.on("error", a);
832
+ n.on("close", (a) => {
833
+ a === 0 ? o() : r(new Error(`Command failed with exit code ${a}`));
834
+ }), n.on("error", r);
701
835
  });
702
836
  }
703
- function D(t, e, r) {
704
- return new Promise((a, n) => {
705
- const o = I(t, [], {
837
+ function D(t, e, o) {
838
+ return new Promise((r, n) => {
839
+ const a = N(t, [], {
706
840
  cwd: e,
707
841
  shell: !0,
708
842
  stdio: "inherit",
709
- env: { ...process.env, ...r }
843
+ env: { ...process.env, ...o }
710
844
  });
711
- o.on("close", (c) => {
712
- c === 0 ? a() : n(new Error(`Command failed with exit code ${c}`));
713
- }), o.on("error", n);
845
+ a.on("close", (c) => {
846
+ c === 0 ? r() : n(new Error(`Command failed with exit code ${c}`));
847
+ }), a.on("error", n);
714
848
  });
715
849
  }
716
- function F(t, e) {
717
- const r = i.join(t, "src");
850
+ function q(t, e) {
851
+ const o = l.join(t, "src");
718
852
  s.writeFileSync(
719
- i.join(r, "app.ts"),
720
- j(e)
853
+ l.join(o, "app.ts"),
854
+ U(e)
721
855
  ), s.writeFileSync(
722
- i.join(r, "seed.ts"),
723
- M(e)
856
+ l.join(o, "seed.ts"),
857
+ j(e)
724
858
  );
725
859
  }
726
- function x(t) {
860
+ function k(t) {
727
861
  console.log(`
728
862
  ` + "─".repeat(50)), console.log("🔐 AuthHero server running at https://localhost:3000"), console.log("📚 API documentation available at https://localhost:3000/docs"), console.log("🌐 Portal available at https://local.authhero.net"), console.log(t ? "🏢 Multi-tenant mode enabled with control_plane tenant" : "🏠 Single-tenant mode with 'main' tenant"), console.log("─".repeat(50) + `
729
863
  `);
730
864
  }
731
- function b(t) {
865
+ function _(t) {
732
866
  console.log(`
733
867
  ` + "─".repeat(50)), console.log("✅ Self-signed certificates generated with openssl"), console.log("⚠️ You may need to trust the certificate in your browser"), console.log("🔐 AuthHero server running at https://localhost:3000"), console.log("📚 API documentation available at https://localhost:3000/docs"), console.log("🌐 Portal available at https://local.authhero.net"), console.log(t ? "🏢 Multi-tenant mode enabled with control_plane tenant" : "🏠 Single-tenant mode with 'main' tenant"), console.log("─".repeat(50) + `
734
868
  `);
735
869
  }
736
- N.version("1.0.0").description("Create a new AuthHero project").argument("[project-name]", "name of the project").option("-t, --template <type>", "template type: local or cloudflare").option("-e, --email <email>", "admin email address").option("-p, --password <password>", "admin password (min 8 characters)").option(
870
+ T.version("1.0.0").description("Create a new AuthHero project").argument("[project-name]", "name of the project").option("-t, --template <type>", "template type: local or cloudflare").option("-e, --email <email>", "admin email address").option("-p, --password <password>", "admin password (min 8 characters)").option(
737
871
  "--package-manager <pm>",
738
872
  "package manager to use: npm, yarn, pnpm, or bun"
739
- ).option("--multi-tenant", "enable multi-tenant mode").option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-seed", "skip seeding the database").option("--skip-start", "skip starting the development server").option("--github-ci", "include GitHub CI workflows with semantic versioning").option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (t, e) => {
740
- const r = e.yes === !0;
873
+ ).option("--multi-tenant", "enable multi-tenant mode").option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-seed", "skip seeding the database").option("--skip-start", "skip starting the development server").option("--github-ci", "include GitHub CI workflows with semantic versioning").option("--conformance", "add OpenID conformance suite test clients").option(
874
+ "--conformance-alias <alias>",
875
+ "alias for conformance suite (default: authhero-local)"
876
+ ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (t, e) => {
877
+ const o = e.yes === !0;
741
878
  console.log(`
742
879
  🔐 Welcome to AuthHero!
743
880
  `);
744
- let a = t;
745
- a || (r ? (a = "auth-server", console.log(`Using default project name: ${a}`)) : a = (await u.prompt([
881
+ let r = t;
882
+ r || (o ? (r = "auth-server", console.log(`Using default project name: ${r}`)) : r = (await u.prompt([
746
883
  {
747
884
  type: "input",
748
885
  name: "projectName",
@@ -751,10 +888,10 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
751
888
  validate: (d) => d !== "" || "Project name cannot be empty"
752
889
  }
753
890
  ])).projectName);
754
- const n = i.join(process.cwd(), a);
755
- s.existsSync(n) && (console.error(`❌ Project "${a}" already exists.`), process.exit(1));
756
- let o;
757
- 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)), o = e.template, console.log(`Using template: ${p[o].name}`)) : o = (await u.prompt([
891
+ const n = l.join(process.cwd(), r);
892
+ s.existsSync(n) && (console.error(`❌ Project "${r}" already exists.`), process.exit(1));
893
+ let a;
894
+ 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)), a = e.template, console.log(`Using template: ${p[a].name}`)) : a = (await u.prompt([
758
895
  {
759
896
  type: "list",
760
897
  name: "setupType",
@@ -782,7 +919,7 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
782
919
  }
783
920
  ])).setupType;
784
921
  let c;
785
- e.multiTenant !== void 0 ? (c = e.multiTenant, console.log(`Multi-tenant mode: ${c ? "enabled" : "disabled"}`)) : r ? c = !1 : c = (await u.prompt([
922
+ e.multiTenant !== void 0 ? (c = e.multiTenant, console.log(`Multi-tenant mode: ${c ? "enabled" : "disabled"}`)) : o ? c = !1 : c = (await u.prompt([
786
923
  {
787
924
  type: "confirm",
788
925
  name: "multiTenant",
@@ -791,57 +928,90 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
791
928
  default: !1
792
929
  }
793
930
  ])).multiTenant;
794
- const A = p[o];
931
+ const g = e.conformance || !1, w = e.conformanceAlias || "authhero-local";
932
+ g && console.log(
933
+ `OpenID Conformance Suite: enabled (alias: ${w})`
934
+ );
935
+ const y = p[a];
795
936
  s.mkdirSync(n, { recursive: !0 }), s.writeFileSync(
796
- i.join(n, "package.json"),
797
- JSON.stringify(A.packageJson(a, c), null, 2)
937
+ l.join(n, "package.json"),
938
+ JSON.stringify(y.packageJson(r, c, g), null, 2)
798
939
  );
799
- const E = A.templateDir, S = i.join(
940
+ const x = y.templateDir, v = l.join(
800
941
  import.meta.url.replace("file://", "").replace("/create-authhero.js", ""),
801
- E
942
+ x
802
943
  );
803
- if (s.existsSync(S) ? k(S, n) : (console.error(`❌ Template directory not found: ${S}`), process.exit(1)), o === "cloudflare" && F(n, c), o === "cloudflare") {
804
- const l = i.join(n, "wrangler.toml"), d = i.join(n, "wrangler.local.toml");
805
- s.existsSync(l) && s.copyFileSync(l, d);
806
- const m = i.join(n, ".dev.vars.example"), g = i.join(n, ".dev.vars");
807
- s.existsSync(m) && s.copyFileSync(m, g), console.log(
944
+ if (s.existsSync(v) ? E(v, n) : (console.error(`❌ Template directory not found: ${v}`), process.exit(1)), a === "cloudflare" && q(n, c), a === "cloudflare") {
945
+ const i = l.join(n, "wrangler.toml"), d = l.join(n, "wrangler.local.toml");
946
+ s.existsSync(i) && s.copyFileSync(i, d);
947
+ const m = l.join(n, ".dev.vars.example"), f = l.join(n, ".dev.vars");
948
+ s.existsSync(m) && s.copyFileSync(m, f), console.log(
808
949
  "📁 Created wrangler.local.toml and .dev.vars for local development"
809
950
  );
810
951
  }
811
- let w = !1;
812
- if (o === "cloudflare" && (e.githubCi !== void 0 ? (w = e.githubCi, w && console.log("Including GitHub CI workflows with semantic versioning")) : r || (w = (await u.prompt([
952
+ let C = !1;
953
+ if (a === "cloudflare" && (e.githubCi !== void 0 ? (C = e.githubCi, C && console.log("Including GitHub CI workflows with semantic versioning")) : o || (C = (await u.prompt([
813
954
  {
814
955
  type: "confirm",
815
956
  name: "includeGithubCi",
816
957
  message: "Would you like to include GitHub CI with semantic versioning?",
817
958
  default: !1
818
959
  }
819
- ])).includeGithubCi), w && (H(n), U(n))), o === "local") {
820
- const l = _(c);
821
- s.writeFileSync(i.join(n, "src/seed.ts"), l);
822
- const d = R(c);
823
- s.writeFileSync(i.join(n, "src/app.ts"), d);
960
+ ])).includeGithubCi), C && (F(n), W(n))), a === "local") {
961
+ const i = R(
962
+ c,
963
+ g,
964
+ w
965
+ );
966
+ s.writeFileSync(l.join(n, "src/seed.ts"), i);
967
+ const d = O(c);
968
+ s.writeFileSync(l.join(n, "src/app.ts"), d);
969
+ }
970
+ if (a === "aws-sst" && H(n, c), g) {
971
+ const i = {
972
+ alias: w,
973
+ description: "AuthHero Conformance Test",
974
+ server: {
975
+ discoveryUrl: "http://host.docker.internal:3000/.well-known/openid-configuration"
976
+ },
977
+ client: {
978
+ client_id: "conformance-test",
979
+ client_secret: "conformanceTestSecret123"
980
+ },
981
+ client2: {
982
+ client_id: "conformance-test2",
983
+ client_secret: "conformanceTestSecret456"
984
+ },
985
+ resource: {
986
+ resourceUrl: "http://host.docker.internal:3000/userinfo"
987
+ }
988
+ };
989
+ s.writeFileSync(
990
+ l.join(n, "conformance-config.json"),
991
+ JSON.stringify(i, null, 2)
992
+ ), console.log(
993
+ "📝 Created conformance-config.json for OpenID Conformance Suite"
994
+ );
824
995
  }
825
- o === "aws-sst" && O(n, c);
826
- const T = c ? "multi-tenant" : "single-tenant";
996
+ const P = c ? "multi-tenant" : "single-tenant";
827
997
  console.log(
828
998
  `
829
- ✅ Project "${a}" has been created with ${A.name} (${T}) setup!
999
+ ✅ Project "${r}" has been created with ${y.name} (${P}) setup!
830
1000
  `
831
1001
  );
832
- let h;
833
- if (e.skipInstall ? h = !1 : r ? h = !0 : h = (await u.prompt([
1002
+ let A;
1003
+ if (e.skipInstall ? A = !1 : o ? A = !0 : A = (await u.prompt([
834
1004
  {
835
1005
  type: "confirm",
836
1006
  name: "shouldInstall",
837
1007
  message: "Would you like to install dependencies now?",
838
1008
  default: !0
839
1009
  }
840
- ])).shouldInstall, h) {
841
- let l;
1010
+ ])).shouldInstall, A) {
1011
+ let i;
842
1012
  e.packageManager ? (["npm", "yarn", "pnpm", "bun"].includes(e.packageManager) || (console.error(
843
1013
  `❌ Invalid package manager: ${e.packageManager}`
844
- ), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l = e.packageManager) : r ? l = "pnpm" : l = (await u.prompt([
1014
+ ), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), i = e.packageManager) : o ? i = "pnpm" : i = (await u.prompt([
845
1015
  {
846
1016
  type: "list",
847
1017
  name: "packageManager",
@@ -855,92 +1025,99 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
855
1025
  default: "pnpm"
856
1026
  }
857
1027
  ])).packageManager, console.log(`
858
- 📦 Installing dependencies with ${l}...
1028
+ 📦 Installing dependencies with ${i}...
859
1029
  `);
860
1030
  try {
861
- const d = l === "pnpm" ? "pnpm install --ignore-workspace" : `${l} install`;
862
- if (await v(d, n), o === "local" && (console.log(`
1031
+ const d = i === "pnpm" ? "pnpm install --ignore-workspace" : `${i} install`;
1032
+ if (await b(d, n), a === "local" && (console.log(`
863
1033
  🔧 Building native modules...
864
- `), await v("npm rebuild better-sqlite3", n)), console.log(`
1034
+ `), await b("npm rebuild better-sqlite3", n)), console.log(`
865
1035
  ✅ Dependencies installed successfully!
866
- `), o === "local" || o === "cloudflare") {
867
- let g;
868
- if (e.skipMigrate && e.skipSeed ? g = !1 : r ? g = !e.skipMigrate || !e.skipSeed : g = (await u.prompt([
1036
+ `), a === "local" || a === "cloudflare") {
1037
+ let f;
1038
+ if (e.skipMigrate && e.skipSeed ? f = !1 : o ? f = !e.skipMigrate || !e.skipSeed : f = (await u.prompt([
869
1039
  {
870
1040
  type: "confirm",
871
1041
  name: "shouldSetup",
872
1042
  message: "Would you like to run migrations and seed the database?",
873
1043
  default: !0
874
1044
  }
875
- ])).shouldSetup, g) {
876
- let f;
877
- e.email && e.password ? (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.email) || (console.error("❌ Invalid email address provided"), process.exit(1)), e.password.length < 8 && (console.error("❌ Password must be at least 8 characters"), process.exit(1)), f = {
1045
+ ])).shouldSetup, f) {
1046
+ let h;
1047
+ e.email && e.password ? (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.email) || (console.error("❌ Invalid email address provided"), process.exit(1)), e.password.length < 8 && (console.error("❌ Password must be at least 8 characters"), process.exit(1)), h = {
878
1048
  username: e.email,
879
1049
  password: e.password
880
- }, console.log(`Using admin email: ${e.email}`)) : f = await u.prompt([
1050
+ }, console.log(`Using admin email: ${e.email}`)) : h = await u.prompt([
881
1051
  {
882
1052
  type: "input",
883
1053
  name: "username",
884
1054
  message: "Admin email:",
885
1055
  default: "admin@example.com",
886
- validate: (y) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(y) || "Please enter a valid email address"
1056
+ validate: (S) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(S) || "Please enter a valid email address"
887
1057
  },
888
1058
  {
889
1059
  type: "password",
890
1060
  name: "password",
891
1061
  message: "Admin password:",
892
1062
  mask: "*",
893
- validate: (y) => y.length < 8 ? "Password must be at least 8 characters" : !0
1063
+ validate: (S) => S.length < 8 ? "Password must be at least 8 characters" : !0
894
1064
  }
895
1065
  ]), e.skipMigrate || (console.log(`
896
1066
  🔄 Running migrations...
897
- `), await v(`${l} run migrate`, n)), e.skipSeed || (console.log(`
1067
+ `), await b(`${i} run migrate`, n)), e.skipSeed || (console.log(`
898
1068
  🌱 Seeding database...
899
- `), o === "local" ? await D(
900
- `${l} run seed`,
1069
+ `), a === "local" ? await D(
1070
+ `${i} run seed`,
901
1071
  n,
902
1072
  {
903
- ADMIN_EMAIL: f.username,
904
- ADMIN_PASSWORD: f.password
1073
+ ADMIN_EMAIL: h.username,
1074
+ ADMIN_PASSWORD: h.password
905
1075
  }
906
1076
  ) : await D(
907
- `${l} run seed:local`,
1077
+ `${i} run seed:local`,
908
1078
  n,
909
1079
  {
910
- ADMIN_EMAIL: f.username,
911
- ADMIN_PASSWORD: f.password
1080
+ ADMIN_EMAIL: h.username,
1081
+ ADMIN_PASSWORD: h.password
912
1082
  }
913
1083
  ));
914
1084
  }
915
1085
  }
916
1086
  let m;
917
- e.skipStart || r ? m = !1 : m = (await u.prompt([
1087
+ e.skipStart || o ? m = !1 : m = (await u.prompt([
918
1088
  {
919
1089
  type: "confirm",
920
1090
  name: "shouldStart",
921
1091
  message: "Would you like to start the development server?",
922
1092
  default: !0
923
1093
  }
924
- ])).shouldStart, m && (o === "cloudflare" ? x(c) : o === "aws-sst" ? C(c) : b(c), console.log(`🚀 Starting development server...
925
- `), await v(`${l} run dev`, n)), r && !m && (console.log(`
1094
+ ])).shouldStart, m && (a === "cloudflare" ? k(c) : a === "aws-sst" ? I(c) : _(c), console.log(`🚀 Starting development server...
1095
+ `), await b(`${i} run dev`, n)), o && !m && (console.log(`
926
1096
  ✅ Setup complete!`), console.log(`
927
- To start the development server:`), console.log(` cd ${a}`), console.log(" npm run dev"), o === "cloudflare" ? x(c) : o === "aws-sst" ? C(c) : b(c));
1097
+ To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), a === "cloudflare" ? k(c) : a === "aws-sst" ? I(c) : _(c));
928
1098
  } catch (d) {
929
1099
  console.error(`
930
1100
  ❌ An error occurred:`, d), process.exit(1);
931
1101
  }
932
1102
  }
933
- h || (console.log("Next steps:"), console.log(` cd ${a}`), o === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(
1103
+ A || (console.log("Next steps:"), console.log(` cd ${r}`), a === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(
934
1104
  " ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
935
- ), console.log(" npm run dev")) : o === "cloudflare" ? (console.log(" npm install"), console.log(
1105
+ ), console.log(" npm run dev")) : a === "cloudflare" ? (console.log(" npm install"), console.log(
936
1106
  " npm run migrate # or npm run db:migrate:remote for production"
937
1107
  ), console.log(
938
1108
  " ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
939
- ), console.log(" npm run dev # or npm run dev:remote for production")) : o === "aws-sst" && (console.log(" npm install"), console.log(" npm run dev # Deploys to AWS in development mode"), console.log(" # After deploy, get TABLE_NAME from output, then:"), console.log(
1109
+ ), console.log(" npm run dev # or npm run dev:remote for production")) : a === "aws-sst" && (console.log(" npm install"), console.log(" npm run dev # Deploys to AWS in development mode"), console.log(" # After deploy, get TABLE_NAME from output, then:"), console.log(
940
1110
  " TABLE_NAME=<your-table> ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
941
1111
  )), console.log(`
942
- Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), console.log(`
1112
+ Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), g && (console.log(`
1113
+ 🧪 OpenID Conformance Suite Testing:`), console.log(
1114
+ " 1. Clone and start the conformance suite (if not already running):"
1115
+ ), console.log(
1116
+ " git clone https://gitlab.com/openid/conformance-suite.git"
1117
+ ), console.log(" cd conformance-suite && mvn clean package"), console.log(" docker-compose up -d"), console.log(" 2. Open https://localhost.emobix.co.uk:8443"), console.log(
1118
+ " 3. Create a test plan and use conformance-config.json for settings"
1119
+ ), console.log(` 4. Use alias: ${w}`)), console.log(`
943
1120
  For more information, visit: https://authhero.net/docs
944
1121
  `));
945
1122
  });
946
- N.parse(process.argv);
1123
+ T.parse(process.argv);
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.24.0",
8
+ "version": "0.26.0",
9
9
  "type": "module",
10
10
  "main": "dist/create-authhero.js",
11
11
  "bin": {