@objectstack/plugin-auth 7.5.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -89,6 +89,34 @@ declare class AuthPlugin implements Plugin {
89
89
  init(ctx: PluginContext): Promise<void>;
90
90
  start(ctx: PluginContext): Promise<void>;
91
91
  destroy(): Promise<void>;
92
+ /**
93
+ * Dev-only admin bootstrap.
94
+ *
95
+ * On an EMPTY database (zero users), provision a well-known, loginable
96
+ * admin (admin@objectos.ai / admin123 by default) so backend debugging
97
+ * never blocks on a first-run sign-up wizard. The account is created
98
+ * through better-auth's real server-side `signUpEmail` pipeline (hashed
99
+ * credential + the same hooks the HTTP endpoint runs), so it is fully
100
+ * loginable; plugin-security's first-user middleware then promotes it to
101
+ * platform admin automatically.
102
+ *
103
+ * This replaces two earlier, divergent seeds:
104
+ * • the CLI-side HTTP seed (`os dev`), which POSTed the public sign-up
105
+ * endpoint from the parent process — racing server readiness and
106
+ * targeting a hard-coded port that broke under dev port auto-shift; and
107
+ * • plugin-dev's raw `sys_user` insert, which produced a credential-less,
108
+ * un-loginable row.
109
+ * Running it in-process needs no port and no readiness polling.
110
+ *
111
+ * Idempotent and non-destructive: it only ever acts on a zero-user DB and
112
+ * never touches an existing account, so a custom password is never
113
+ * overwritten.
114
+ *
115
+ * HARD-GATED to development (NODE_ENV==='development'): a known-credential
116
+ * admin can never be provisioned in production. Opt out within dev via
117
+ * OS_SEED_ADMIN=0 (or false/off/no).
118
+ */
119
+ private maybeSeedDevAdmin;
92
120
  /**
93
121
  * Register authentication routes with HTTP server
94
122
  *
@@ -203,6 +231,17 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
203
231
  declare class AuthManager {
204
232
  private auth;
205
233
  private config;
234
+ /**
235
+ * Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`
236
+ * when it provisions the well-known admin on an empty DB). The `serve`
237
+ * command reads this after boot to surface the credentials in the startup
238
+ * banner. Undefined when no seed ran (production, opt-out, or a DB that
239
+ * already had a user).
240
+ */
241
+ devSeedResult?: {
242
+ email: string;
243
+ password: string;
244
+ };
206
245
  constructor(config: AuthManagerOptions);
207
246
  /**
208
247
  * Get or create the better-auth instance (lazy initialization)
package/dist/index.d.ts CHANGED
@@ -89,6 +89,34 @@ declare class AuthPlugin implements Plugin {
89
89
  init(ctx: PluginContext): Promise<void>;
90
90
  start(ctx: PluginContext): Promise<void>;
91
91
  destroy(): Promise<void>;
92
+ /**
93
+ * Dev-only admin bootstrap.
94
+ *
95
+ * On an EMPTY database (zero users), provision a well-known, loginable
96
+ * admin (admin@objectos.ai / admin123 by default) so backend debugging
97
+ * never blocks on a first-run sign-up wizard. The account is created
98
+ * through better-auth's real server-side `signUpEmail` pipeline (hashed
99
+ * credential + the same hooks the HTTP endpoint runs), so it is fully
100
+ * loginable; plugin-security's first-user middleware then promotes it to
101
+ * platform admin automatically.
102
+ *
103
+ * This replaces two earlier, divergent seeds:
104
+ * • the CLI-side HTTP seed (`os dev`), which POSTed the public sign-up
105
+ * endpoint from the parent process — racing server readiness and
106
+ * targeting a hard-coded port that broke under dev port auto-shift; and
107
+ * • plugin-dev's raw `sys_user` insert, which produced a credential-less,
108
+ * un-loginable row.
109
+ * Running it in-process needs no port and no readiness polling.
110
+ *
111
+ * Idempotent and non-destructive: it only ever acts on a zero-user DB and
112
+ * never touches an existing account, so a custom password is never
113
+ * overwritten.
114
+ *
115
+ * HARD-GATED to development (NODE_ENV==='development'): a known-credential
116
+ * admin can never be provisioned in production. Opt out within dev via
117
+ * OS_SEED_ADMIN=0 (or false/off/no).
118
+ */
119
+ private maybeSeedDevAdmin;
92
120
  /**
93
121
  * Register authentication routes with HTTP server
94
122
  *
@@ -203,6 +231,17 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
203
231
  declare class AuthManager {
204
232
  private auth;
205
233
  private config;
234
+ /**
235
+ * Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`
236
+ * when it provisions the well-known admin on an empty DB). The `serve`
237
+ * command reads this after boot to surface the credentials in the startup
238
+ * banner. Undefined when no seed ran (production, opt-out, or a DB that
239
+ * already had a user).
240
+ */
241
+ devSeedResult?: {
242
+ email: string;
243
+ password: string;
244
+ };
206
245
  constructor(config: AuthManagerOptions);
207
246
  /**
208
247
  * Get or create the better-auth instance (lazy initialization)
package/dist/index.js CHANGED
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  module.exports = __toCommonJS(index_exports);
69
69
 
70
70
  // src/auth-plugin.ts
71
+ var import_system3 = require("@objectstack/spec/system");
71
72
  var import_apps = require("@objectstack/platform-objects/apps");
72
73
  var import_pages = require("@objectstack/platform-objects/pages");
73
74
 
@@ -1572,6 +1573,9 @@ var AuthPlugin = class {
1572
1573
  }
1573
1574
  });
1574
1575
  }
1576
+ ctx.hook("kernel:ready", async () => {
1577
+ await this.maybeSeedDevAdmin(ctx);
1578
+ });
1575
1579
  try {
1576
1580
  const ql = ctx.getService("objectql");
1577
1581
  if (ql && typeof ql.registerMiddleware === "function") {
@@ -1591,6 +1595,66 @@ var AuthPlugin = class {
1591
1595
  async destroy() {
1592
1596
  this.authManager = null;
1593
1597
  }
1598
+ /**
1599
+ * Dev-only admin bootstrap.
1600
+ *
1601
+ * On an EMPTY database (zero users), provision a well-known, loginable
1602
+ * admin (admin@objectos.ai / admin123 by default) so backend debugging
1603
+ * never blocks on a first-run sign-up wizard. The account is created
1604
+ * through better-auth's real server-side `signUpEmail` pipeline (hashed
1605
+ * credential + the same hooks the HTTP endpoint runs), so it is fully
1606
+ * loginable; plugin-security's first-user middleware then promotes it to
1607
+ * platform admin automatically.
1608
+ *
1609
+ * This replaces two earlier, divergent seeds:
1610
+ * • the CLI-side HTTP seed (`os dev`), which POSTed the public sign-up
1611
+ * endpoint from the parent process — racing server readiness and
1612
+ * targeting a hard-coded port that broke under dev port auto-shift; and
1613
+ * • plugin-dev's raw `sys_user` insert, which produced a credential-less,
1614
+ * un-loginable row.
1615
+ * Running it in-process needs no port and no readiness polling.
1616
+ *
1617
+ * Idempotent and non-destructive: it only ever acts on a zero-user DB and
1618
+ * never touches an existing account, so a custom password is never
1619
+ * overwritten.
1620
+ *
1621
+ * HARD-GATED to development (NODE_ENV==='development'): a known-credential
1622
+ * admin can never be provisioned in production. Opt out within dev via
1623
+ * OS_SEED_ADMIN=0 (or false/off/no).
1624
+ */
1625
+ async maybeSeedDevAdmin(ctx) {
1626
+ if (process.env.NODE_ENV !== "development") return;
1627
+ const flag = String(process.env.OS_SEED_ADMIN ?? "").trim().toLowerCase();
1628
+ if (["0", "false", "off", "no"].includes(flag)) return;
1629
+ const email = process.env.OS_SEED_ADMIN_EMAIL?.trim() || "admin@objectos.ai";
1630
+ const password = process.env.OS_SEED_ADMIN_PASSWORD?.trim() || "admin123";
1631
+ const name = process.env.OS_SEED_ADMIN_NAME?.trim() || "Dev Admin";
1632
+ let ql;
1633
+ try {
1634
+ ql = ctx.getService("objectql");
1635
+ } catch {
1636
+ }
1637
+ if (!ql || typeof ql.find !== "function") return;
1638
+ try {
1639
+ const rows = await ql.find(import_system3.SystemObjectName.USER, { where: {}, limit: 50 }, { context: { isSystem: true } }).catch(() => []);
1640
+ const humans = (Array.isArray(rows) ? rows : []).filter((u) => u && u.id !== import_system3.SystemUserId.SYSTEM && u.role !== "system");
1641
+ if (humans.length > 0) {
1642
+ ctx.logger.debug("[auth] dev admin seed skipped \u2014 a user already exists");
1643
+ return;
1644
+ }
1645
+ if (!this.authManager) return;
1646
+ const api = await this.authManager.getApi();
1647
+ if (typeof api?.signUpEmail !== "function") {
1648
+ ctx.logger.warn("[auth] dev admin seed skipped \u2014 signUpEmail unavailable");
1649
+ return;
1650
+ }
1651
+ await api.signUpEmail({ body: { email, password, name } });
1652
+ ctx.logger.info(`\u{1F511} Dev admin seeded: ${email} / ${password}`);
1653
+ this.authManager.devSeedResult = { email, password };
1654
+ } catch (err) {
1655
+ ctx.logger.warn(`[auth] dev admin seed skipped: ${err?.message ?? err}`);
1656
+ }
1657
+ }
1594
1658
  /**
1595
1659
  * Register authentication routes with HTTP server
1596
1660
  *