create-authhero 0.41.2 → 0.42.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.
@@ -86,12 +86,12 @@ try {
86
86
  );
87
87
  }
88
88
 
89
- // Copy admin UI files from @authhero/react-admin package
89
+ // Copy admin UI files from @authhero/admin package
90
90
  const adminSourceDir = path.join(
91
91
  __dirname,
92
92
  "node_modules",
93
93
  "@authhero",
94
- "react-admin",
94
+ "admin",
95
95
  "dist",
96
96
  );
97
97
 
@@ -2,7 +2,7 @@
2
2
  import { Command as R } from "commander";
3
3
  import m from "inquirer";
4
4
  import i from "fs";
5
- import s from "path";
5
+ import a from "path";
6
6
  import { fileURLToPath as O } from "url";
7
7
  import { spawn as U } from "child_process";
8
8
  const N = new R(), p = {
@@ -10,10 +10,10 @@ const N = new R(), p = {
10
10
  name: "Local (SQLite)",
11
11
  description: "Local development setup with SQLite database - great for getting started",
12
12
  templateDir: "local",
13
- packageJson: (a, e, o, r, n) => {
13
+ packageJson: (s, e, o, r, n) => {
14
14
  const t = r ? "workspace:*" : "latest";
15
15
  return {
16
- name: a,
16
+ name: s,
17
17
  version: "1.0.0",
18
18
  type: "module",
19
19
  scripts: {
@@ -24,7 +24,7 @@ const N = new R(), p = {
24
24
  },
25
25
  dependencies: {
26
26
  "@authhero/kysely-adapter": t,
27
- ...n && { "@authhero/react-admin": t },
27
+ ...n && { "@authhero/admin": t },
28
28
  "@authhero/widget": t,
29
29
  "@hono/swagger-ui": "^0.5.0",
30
30
  "@hono/zod-openapi": "^0.19.0",
@@ -50,10 +50,10 @@ const N = new R(), p = {
50
50
  name: "Cloudflare Workers (D1)",
51
51
  description: "Cloudflare Workers setup with D1 database",
52
52
  templateDir: "cloudflare",
53
- packageJson: (a, e, o, r, n) => {
53
+ packageJson: (s, e, o, r, n) => {
54
54
  const t = r ? "workspace:*" : "latest";
55
55
  return {
56
- name: a,
56
+ name: s,
57
57
  version: "1.0.0",
58
58
  type: "module",
59
59
  scripts: {
@@ -73,7 +73,7 @@ const N = new R(), p = {
73
73
  dependencies: {
74
74
  "@authhero/drizzle": t,
75
75
  "@authhero/kysely-adapter": t,
76
- ...n && { "@authhero/react-admin": t },
76
+ ...n && { "@authhero/admin": t },
77
77
  "@authhero/widget": t,
78
78
  "@hono/swagger-ui": "^0.5.0",
79
79
  "@hono/zod-openapi": "^0.19.0",
@@ -99,10 +99,10 @@ const N = 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: (a, e, o, r, n) => {
102
+ packageJson: (s, e, o, r, n) => {
103
103
  const t = r ? "workspace:*" : "latest";
104
104
  return {
105
- name: a,
105
+ name: s,
106
106
  version: "1.0.0",
107
107
  type: "module",
108
108
  scripts: {
@@ -114,7 +114,7 @@ const N = new R(), p = {
114
114
  },
115
115
  dependencies: {
116
116
  "@authhero/aws": t,
117
- ...n && { "@authhero/react-admin": t },
117
+ ...n && { "@authhero/admin": t },
118
118
  "@authhero/widget": t,
119
119
  "@aws-sdk/client-dynamodb": "^3.0.0",
120
120
  "@aws-sdk/lib-dynamodb": "^3.0.0",
@@ -137,14 +137,14 @@ const N = new R(), p = {
137
137
  seedFile: "seed.ts"
138
138
  }
139
139
  };
140
- function P(a, e) {
141
- i.readdirSync(a).forEach((r) => {
142
- const n = s.join(a, r), t = s.join(e, r);
140
+ function P(s, e) {
141
+ i.readdirSync(s).forEach((r) => {
142
+ const n = a.join(s, r), t = a.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 $(a, e = !1, o = "authhero-local", r) {
147
- const n = a ? "control_plane" : "main", t = a ? "Control Plane" : "Main", c = [
146
+ function $(s, e = !1, o = "authhero-local", r) {
147
+ const n = s ? "control_plane" : "main", t = s ? "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",
@@ -166,15 +166,19 @@ function $(a, e = !1, o = "authhero-local", r) {
166
166
  // requires either an explicit audience or a tenant default_audience to mint
167
167
  // an access token, so set one here for the conformance setup.
168
168
  // enable_dynamic_client_registration is required by the OIDCC dynamic plan,
169
- // which has the suite register its own client via /oidc/register. Existing
170
- // flags (e.g. inherit_global_permissions_in_organizations set by seed for
171
- // the control-plane tenant) are merged in so the update doesn't clobber.
169
+ // which has the suite register its own client via /oidc/register. The
170
+ // OIDCC dynamic_client variant uses open DCR (no Initial Access Token), so
171
+ // dcr_require_initial_access_token must be flipped off the AuthHero
172
+ // default is to require an IAT. Existing flags (e.g.
173
+ // inherit_global_permissions_in_organizations set by seed for the
174
+ // control-plane tenant) are merged in so the update doesn't clobber.
172
175
  const existingTenant = await adapters.tenants.get("${n}");
173
176
  await adapters.tenants.update("${n}", {
174
177
  default_audience: "urn:authhero:management",
175
178
  flags: {
176
179
  ...(existingTenant?.flags ?? {}),
177
180
  enable_dynamic_client_registration: true,
181
+ dcr_require_initial_access_token: false,
178
182
  },
179
183
  });
180
184
  console.log("✅ Set tenant default_audience and enabled DCR for conformance");
@@ -192,39 +196,44 @@ function $(a, e = !1, o = "authhero-local", r) {
192
196
  "https://localhost.emobix.co.uk:8443",
193
197
  ];
194
198
 
195
- try {
196
- await adapters.clients.create("${n}", {
199
+ // Strict OIDC 5.4: scope-driven claims (profile/email/address/phone) belong
200
+ // in /userinfo whenever an access_token is co-issued at /authorize. The OIDF
201
+ // suite enforces this via EnsureIdTokenDoesNotContainEmailForScopeEmail;
202
+ // running the conformance tenant under Auth0-compatible defaults would WARN.
203
+ const conformanceClientSpecs = [
204
+ {
197
205
  client_id: "conformance-test",
198
206
  client_secret: "conformanceTestSecret123",
199
207
  name: "Conformance Test Client",
200
- callbacks: conformanceCallbacks,
201
- allowed_logout_urls: conformanceLogoutUrls,
202
- web_origins: conformanceWebOrigins,
203
- });
204
- console.log("✅ Created conformance-test client");
205
- } catch (e: any) {
206
- if (e.message?.includes("UNIQUE constraint")) {
207
- console.log("ℹ️ conformance-test client already exists");
208
- } else {
209
- throw e;
210
- }
211
- }
212
-
213
- try {
214
- await adapters.clients.create("${n}", {
208
+ },
209
+ {
215
210
  client_id: "conformance-test2",
216
211
  client_secret: "conformanceTestSecret456",
217
212
  name: "Conformance Test Client 2",
213
+ },
214
+ ];
215
+ for (const spec of conformanceClientSpecs) {
216
+ const desired = {
217
+ name: spec.name,
218
+ client_secret: spec.client_secret,
218
219
  callbacks: conformanceCallbacks,
219
220
  allowed_logout_urls: conformanceLogoutUrls,
220
221
  web_origins: conformanceWebOrigins,
221
- });
222
- console.log("✅ Created conformance-test2 client");
223
- } catch (e: any) {
224
- if (e.message?.includes("UNIQUE constraint")) {
225
- console.log("ℹ️ conformance-test2 client already exists");
222
+ auth0_conformant: false,
223
+ };
224
+ // Idempotent reconcile: prior runs may have created the client with stale
225
+ // callbacks/web_origins after this seed file was changed. Update existing
226
+ // records in place rather than just logging "already exists".
227
+ const existing = await adapters.clients.get("${n}", spec.client_id);
228
+ if (existing) {
229
+ await adapters.clients.update("${n}", spec.client_id, desired);
230
+ console.log(\`🔄 Updated \${spec.client_id} client\`);
226
231
  } else {
227
- throw e;
232
+ await adapters.clients.create("${n}", {
233
+ client_id: spec.client_id,
234
+ ...desired,
235
+ });
236
+ console.log(\`✅ Created \${spec.client_id} client\`);
228
237
  }
229
238
  }
230
239
 
@@ -343,7 +352,7 @@ async function main() {
343
352
  adminPassword,
344
353
  tenantId: "${n}",
345
354
  tenantName: "${t}",
346
- isControlPlane: ${!!a},
355
+ isControlPlane: ${!!s},
347
356
  clientId: "default",
348
357
  callbacks: ${JSON.stringify(f)},
349
358
  allowedLogoutUrls: ${JSON.stringify(y)},
@@ -385,12 +394,12 @@ main().catch((err) => {
385
394
  });
386
395
  `;
387
396
  }
388
- function L(a, e) {
397
+ function L(s, e) {
389
398
  const o = e ? `import fs from "fs";
390
399
  ` : "", r = e ? `
391
400
  const adminDistPath = path.resolve(
392
401
  __dirname,
393
- "../node_modules/@authhero/react-admin/dist",
402
+ "../node_modules/@authhero/admin/dist",
394
403
  );
395
404
  const adminIndexPath = path.join(adminDistPath, "index.html");
396
405
  ` : "", n = e ? `
@@ -403,7 +412,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
403
412
  .replace(/href="\\.\\//g, 'href="/admin/');
404
413
  const configJson = JSON.stringify({
405
414
  domain: issuer.replace(/\\/$/, ""),
406
- clientId: ${a ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
415
+ clientId: ${s ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
407
416
  basePath: "/admin",
408
417
  }).replace(/</g, "\\\\u003c");
409
418
  configWithHandlers.adminIndexHtml = rawHtml.replace(
@@ -416,7 +425,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
416
425
  });
417
426
  }
418
427
  ` : "";
419
- return a ? `import { Context } from "hono";
428
+ return s ? `import { Context } from "hono";
420
429
  import { swaggerUI } from "@hono/swagger-ui";
421
430
  import { AuthHeroConfig, DataAdapters } from "authhero";
422
431
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -521,7 +530,7 @@ ${n}
521
530
  }
522
531
  `;
523
532
  }
524
- function j(a) {
533
+ function j(s) {
525
534
  return `import { D1Dialect } from "kysely-d1";
526
535
  import { Kysely } from "kysely";
527
536
  import createAdapters from "@authhero/kysely-adapter";
@@ -548,9 +557,9 @@ export default {
548
557
  adminUsername,
549
558
  adminPassword,
550
559
  issuer,
551
- tenantId: "${a ? "control_plane" : "main"}",
552
- tenantName: "${a ? "Control Plane" : "Main"}",
553
- isControlPlane: ${!!a},
560
+ tenantId: "${s ? "control_plane" : "main"}",
561
+ tenantName: "${s ? "Control Plane" : "Main"}",
562
+ isControlPlane: ${!!s},
554
563
  clientId: "default",
555
564
  });
556
565
 
@@ -582,11 +591,11 @@ export default {
582
591
  };
583
592
  `;
584
593
  }
585
- function H(a, e) {
594
+ function H(s, e) {
586
595
  const o = e ? `import adminIndexHtml from "./admin-index-html";
587
596
  ` : "", r = e ? ` adminIndexHtml,
588
597
  ` : "";
589
- return a ? `import { Context } from "hono";
598
+ return s ? `import { Context } from "hono";
590
599
  import { swaggerUI } from "@hono/swagger-ui";
591
600
  import { AuthHeroConfig, DataAdapters } from "authhero";
592
601
  import { initMultiTenant } from "@authhero/multi-tenancy";
@@ -667,8 +676,8 @@ ${r} });
667
676
  }
668
677
  `;
669
678
  }
670
- function M(a) {
671
- return a ? `import { Context } from "hono";
679
+ function F(s) {
680
+ return s ? `import { Context } from "hono";
672
681
  import { swaggerUI } from "@hono/swagger-ui";
673
682
  import { AuthHeroConfig, DataAdapters } from "authhero";
674
683
  import { initMultiTenant } from "@authhero/multi-tenancy";
@@ -773,7 +782,7 @@ export default function createApp(config: AppConfig) {
773
782
  }
774
783
  `;
775
784
  }
776
- function F(a) {
785
+ function M(s) {
777
786
  return `import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
778
787
  import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
779
788
  import createAdapters from "@authhero/aws";
@@ -802,9 +811,9 @@ async function main() {
802
811
  await seed(adapters, {
803
812
  adminUsername,
804
813
  adminPassword,
805
- tenantId: "${a ? "control_plane" : "main"}",
806
- tenantName: "${a ? "Control Plane" : "Main"}",
807
- isControlPlane: ${!!a},
814
+ tenantId: "${s ? "control_plane" : "main"}",
815
+ tenantName: "${s ? "Control Plane" : "Main"}",
816
+ isControlPlane: ${!!s},
808
817
  });
809
818
 
810
819
  console.log("✅ Database seeded successfully!");
@@ -813,21 +822,21 @@ async function main() {
813
822
  main().catch(console.error);
814
823
  `;
815
824
  }
816
- function W(a, e) {
817
- const o = s.join(a, "src");
825
+ function W(s, e) {
826
+ const o = a.join(s, "src");
818
827
  i.writeFileSync(
819
- s.join(o, "app.ts"),
820
- M(e)
821
- ), i.writeFileSync(
822
- s.join(o, "seed.ts"),
828
+ a.join(o, "app.ts"),
823
829
  F(e)
830
+ ), i.writeFileSync(
831
+ a.join(o, "seed.ts"),
832
+ M(e)
824
833
  );
825
834
  }
826
835
  function k() {
827
836
  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");
828
837
  }
829
- function q(a) {
830
- const e = s.join(a, ".github", "workflows");
838
+ function q(s) {
839
+ const e = a.join(s, ".github", "workflows");
831
840
  i.mkdirSync(e, { recursive: !0 });
832
841
  const o = `name: Unit tests
833
842
 
@@ -915,9 +924,9 @@ jobs:
915
924
  apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
916
925
  command: deploy --env production
917
926
  `;
918
- 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!");
927
+ 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!");
919
928
  }
920
- function J(a) {
929
+ function J(s) {
921
930
  const e = {
922
931
  branches: ["main"],
923
932
  plugins: [
@@ -927,10 +936,10 @@ function J(a) {
927
936
  ]
928
937
  };
929
938
  i.writeFileSync(
930
- s.join(a, ".releaserc.json"),
939
+ a.join(s, ".releaserc.json"),
931
940
  JSON.stringify(e, null, 2)
932
941
  );
933
- const o = s.join(a, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
942
+ const o = a.join(s, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
934
943
  r.devDependencies = {
935
944
  ...r.devDependencies,
936
945
  "semantic-release": "^24.0.0"
@@ -940,9 +949,9 @@ function J(a) {
940
949
  "type-check": "tsc --noEmit"
941
950
  }, i.writeFileSync(o, JSON.stringify(r, null, 2));
942
951
  }
943
- function A(a, e) {
952
+ function A(s, e) {
944
953
  return new Promise((o, r) => {
945
- const n = U(a, [], {
954
+ const n = U(s, [], {
946
955
  cwd: e,
947
956
  shell: !0,
948
957
  stdio: "inherit"
@@ -952,13 +961,13 @@ function A(a, e) {
952
961
  }), n.on("error", r);
953
962
  });
954
963
  }
955
- function z(a, e, o) {
956
- const r = s.join(a, "src");
964
+ function z(s, e, o) {
965
+ const r = a.join(s, "src");
957
966
  i.writeFileSync(
958
- s.join(r, "app.ts"),
967
+ a.join(r, "app.ts"),
959
968
  H(e, o)
960
969
  ), i.writeFileSync(
961
- s.join(r, "seed.ts"),
970
+ a.join(r, "seed.ts"),
962
971
  j(e)
963
972
  );
964
973
  }
@@ -981,12 +990,12 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
981
990
  ).option(
982
991
  "--workspace",
983
992
  "use workspace:* dependencies for local monorepo development"
984
- ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (a, e) => {
993
+ ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (s, e) => {
985
994
  const o = e.yes === !0;
986
995
  console.log(`
987
996
  🔐 Welcome to AuthHero!
988
997
  `);
989
- let r = a;
998
+ let r = s;
990
999
  r || (o ? (r = "auth-server", console.log(`Using default project name: ${r}`)) : r = (await m.prompt([
991
1000
  {
992
1001
  type: "input",
@@ -996,7 +1005,7 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
996
1005
  validate: (u) => u !== "" || "Project name cannot be empty"
997
1006
  }
998
1007
  ])).projectName);
999
- const n = s.join(process.cwd(), r);
1008
+ const n = a.join(process.cwd(), r);
1000
1009
  i.existsSync(n) && (console.error(`❌ Project "${r}" already exists.`), process.exit(1));
1001
1010
  let t;
1002
1011
  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([
@@ -1052,7 +1061,7 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1052
1061
  C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
1053
1062
  const y = p[t];
1054
1063
  i.mkdirSync(n, { recursive: !0 }), i.writeFileSync(
1055
- s.join(n, "package.json"),
1064
+ a.join(n, "package.json"),
1056
1065
  JSON.stringify(
1057
1066
  y.packageJson(
1058
1067
  r,
@@ -1065,18 +1074,18 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1065
1074
  2
1066
1075
  )
1067
1076
  );
1068
- const _ = y.templateDir, S = s.dirname(O(import.meta.url)), x = [
1069
- s.join(S, _),
1070
- s.join(S, "..", "templates", _)
1077
+ const _ = y.templateDir, S = a.dirname(O(import.meta.url)), x = [
1078
+ a.join(S, _),
1079
+ a.join(S, "..", "templates", _)
1071
1080
  ], I = x.find((l) => i.existsSync(l));
1072
1081
  if (I ? P(I, n) : (console.error(
1073
1082
  `❌ Template directory not found. Looked in:
1074
1083
  ${x.join(`
1075
1084
  `)}`
1076
1085
  ), process.exit(1)), t === "cloudflare" && z(n, c, d), t === "cloudflare") {
1077
- const l = s.join(n, "wrangler.toml"), u = s.join(n, "wrangler.local.toml");
1086
+ const l = a.join(n, "wrangler.toml"), u = a.join(n, "wrangler.local.toml");
1078
1087
  i.existsSync(l) && i.copyFileSync(l, u);
1079
- const g = s.join(n, ".dev.vars.example"), w = s.join(n, ".dev.vars");
1088
+ const g = a.join(n, ".dev.vars.example"), w = a.join(n, ".dev.vars");
1080
1089
  i.existsSync(g) && i.copyFileSync(g, w), console.log(
1081
1090
  "📁 Created wrangler.local.toml and .dev.vars for local development"
1082
1091
  );
@@ -1096,9 +1105,9 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1096
1105
  h,
1097
1106
  d
1098
1107
  );
1099
- i.writeFileSync(s.join(n, "src/seed.ts"), l);
1108
+ i.writeFileSync(a.join(n, "src/seed.ts"), l);
1100
1109
  const u = L(c, d);
1101
- i.writeFileSync(s.join(n, "src/app.ts"), u);
1110
+ i.writeFileSync(a.join(n, "src/app.ts"), u);
1102
1111
  }
1103
1112
  if (t === "aws-sst" && W(n, c), f) {
1104
1113
  const l = {
@@ -1120,7 +1129,7 @@ N.version("1.0.0").description("Create a new AuthHero project").argument("[proje
1120
1129
  }
1121
1130
  };
1122
1131
  i.writeFileSync(
1123
- s.join(n, "conformance-config.json"),
1132
+ a.join(n, "conformance-config.json"),
1124
1133
  JSON.stringify(l, null, 2)
1125
1134
  ), console.log(
1126
1135
  "📝 Created conformance-config.json for OpenID Conformance Suite"
@@ -16,7 +16,7 @@ const widgetPath = path.resolve(
16
16
 
17
17
  const adminDistPath = path.resolve(
18
18
  __dirname,
19
- "../node_modules/@authhero/react-admin/dist",
19
+ "../node_modules/@authhero/admin/dist",
20
20
  );
21
21
  const adminIndexPath = path.join(adminDistPath, "index.html");
22
22
 
@@ -27,7 +27,14 @@ function ensureCertificates() {
27
27
  execFileSync("which", ["mkcert"], { stdio: "ignore" });
28
28
  execFileSync(
29
29
  "mkcert",
30
- ["-key-file", keyPath, "-cert-file", certPath, "localhost", "127.0.0.1"],
30
+ [
31
+ "-key-file",
32
+ keyPath,
33
+ "-cert-file",
34
+ certPath,
35
+ "localhost",
36
+ "127.0.0.1",
37
+ ],
31
38
  { stdio: "inherit" },
32
39
  );
33
40
  console.log("✅ Certificates generated with mkcert");
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.2",
8
+ "version": "0.42.0",
9
9
  "type": "module",
10
10
  "main": "dist/create-authhero.js",
11
11
  "bin": {