@objectstack/plugin-auth 4.0.4 → 4.0.5

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.
Files changed (40) hide show
  1. package/README.md +4 -1
  2. package/dist/index.d.mts +332 -19942
  3. package/dist/index.d.ts +332 -19942
  4. package/dist/index.js +351 -882
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +355 -862
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +35 -12
  9. package/.turbo/turbo-build.log +0 -78
  10. package/ARCHITECTURE.md +0 -176
  11. package/CHANGELOG.md +0 -333
  12. package/IMPLEMENTATION_SUMMARY.md +0 -192
  13. package/examples/basic-usage.ts +0 -107
  14. package/objectstack.config.ts +0 -24
  15. package/src/auth-manager.test.ts +0 -883
  16. package/src/auth-manager.ts +0 -419
  17. package/src/auth-plugin.test.ts +0 -446
  18. package/src/auth-plugin.ts +0 -314
  19. package/src/auth-schema-config.ts +0 -339
  20. package/src/index.ts +0 -16
  21. package/src/objectql-adapter.test.ts +0 -281
  22. package/src/objectql-adapter.ts +0 -279
  23. package/src/objects/auth-account.object.ts +0 -7
  24. package/src/objects/auth-session.object.ts +0 -7
  25. package/src/objects/auth-user.object.ts +0 -7
  26. package/src/objects/auth-verification.object.ts +0 -7
  27. package/src/objects/index.ts +0 -40
  28. package/src/objects/sys-account.object.ts +0 -111
  29. package/src/objects/sys-api-key.object.ts +0 -104
  30. package/src/objects/sys-invitation.object.ts +0 -93
  31. package/src/objects/sys-member.object.ts +0 -68
  32. package/src/objects/sys-organization.object.ts +0 -82
  33. package/src/objects/sys-session.object.ts +0 -84
  34. package/src/objects/sys-team-member.object.ts +0 -61
  35. package/src/objects/sys-team.object.ts +0 -69
  36. package/src/objects/sys-two-factor.object.ts +0 -73
  37. package/src/objects/sys-user-preference.object.ts +0 -82
  38. package/src/objects/sys-user.object.ts +0 -91
  39. package/src/objects/sys-verification.object.ts +0 -75
  40. package/tsconfig.json +0 -18
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,15 +17,30 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
23
33
  AUTH_ACCOUNT_CONFIG: () => AUTH_ACCOUNT_CONFIG,
34
+ AUTH_DEVICE_CODE_SCHEMA: () => AUTH_DEVICE_CODE_SCHEMA,
24
35
  AUTH_INVITATION_SCHEMA: () => AUTH_INVITATION_SCHEMA,
36
+ AUTH_JWKS_SCHEMA: () => AUTH_JWKS_SCHEMA,
25
37
  AUTH_MEMBER_SCHEMA: () => AUTH_MEMBER_SCHEMA,
26
38
  AUTH_MODEL_TO_PROTOCOL: () => AUTH_MODEL_TO_PROTOCOL,
39
+ AUTH_OAUTH_ACCESS_TOKEN_SCHEMA: () => AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
40
+ AUTH_OAUTH_APPLICATION_SCHEMA: () => AUTH_OAUTH_APPLICATION_SCHEMA,
41
+ AUTH_OAUTH_CLIENT_SCHEMA: () => AUTH_OAUTH_CLIENT_SCHEMA,
42
+ AUTH_OAUTH_CONSENT_SCHEMA: () => AUTH_OAUTH_CONSENT_SCHEMA,
43
+ AUTH_OAUTH_REFRESH_TOKEN_SCHEMA: () => AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
27
44
  AUTH_ORGANIZATION_SCHEMA: () => AUTH_ORGANIZATION_SCHEMA,
28
45
  AUTH_ORG_SESSION_FIELDS: () => AUTH_ORG_SESSION_FIELDS,
29
46
  AUTH_SESSION_CONFIG: () => AUTH_SESSION_CONFIG,
@@ -33,24 +50,12 @@ __export(index_exports, {
33
50
  AUTH_TWO_FACTOR_USER_FIELDS: () => AUTH_TWO_FACTOR_USER_FIELDS,
34
51
  AUTH_USER_CONFIG: () => AUTH_USER_CONFIG,
35
52
  AUTH_VERIFICATION_CONFIG: () => AUTH_VERIFICATION_CONFIG,
36
- AuthAccount: () => SysAccount,
37
53
  AuthManager: () => AuthManager,
38
54
  AuthPlugin: () => AuthPlugin,
39
- AuthSession: () => SysSession,
40
- AuthUser: () => SysUser,
41
- AuthVerification: () => SysVerification,
42
- SysAccount: () => SysAccount,
43
- SysApiKey: () => SysApiKey,
44
- SysInvitation: () => SysInvitation,
45
- SysMember: () => SysMember,
46
- SysOrganization: () => SysOrganization,
47
- SysSession: () => SysSession,
48
- SysTeam: () => SysTeam,
49
- SysTeamMember: () => SysTeamMember,
50
- SysTwoFactor: () => SysTwoFactor,
51
- SysUser: () => SysUser,
52
- SysUserPreference: () => SysUserPreference,
53
- SysVerification: () => SysVerification,
55
+ buildDeviceAuthorizationPluginSchema: () => buildDeviceAuthorizationPluginSchema,
56
+ buildJwtPluginSchema: () => buildJwtPluginSchema,
57
+ buildOauthProviderPluginSchema: () => buildOauthProviderPluginSchema,
58
+ buildOidcProviderPluginSchema: () => buildOidcProviderPluginSchema,
54
59
  buildOrganizationPluginSchema: () => buildOrganizationPluginSchema,
55
60
  buildTwoFactorPluginSchema: () => buildTwoFactorPluginSchema,
56
61
  createObjectQLAdapter: () => createObjectQLAdapter,
@@ -59,11 +64,8 @@ __export(index_exports, {
59
64
  });
60
65
  module.exports = __toCommonJS(index_exports);
61
66
 
62
- // src/auth-manager.ts
63
- var import_better_auth = require("better-auth");
64
- var import_organization = require("better-auth/plugins/organization");
65
- var import_two_factor = require("better-auth/plugins/two-factor");
66
- var import_magic_link = require("better-auth/plugins/magic-link");
67
+ // src/auth-plugin.ts
68
+ var import_apps = require("@objectstack/platform-objects/apps");
67
69
 
68
70
  // src/objectql-adapter.ts
69
71
  var import_adapters = require("better-auth/adapters");
@@ -337,6 +339,81 @@ var AUTH_TWO_FACTOR_SCHEMA = {
337
339
  var AUTH_TWO_FACTOR_USER_FIELDS = {
338
340
  twoFactorEnabled: "two_factor_enabled"
339
341
  };
342
+ var AUTH_OAUTH_CLIENT_SCHEMA = {
343
+ modelName: import_system2.SystemObjectName.OAUTH_APPLICATION,
344
+ // 'sys_oauth_application'
345
+ fields: {
346
+ clientId: "client_id",
347
+ clientSecret: "client_secret",
348
+ skipConsent: "skip_consent",
349
+ enableEndSession: "enable_end_session",
350
+ subjectType: "subject_type",
351
+ userId: "user_id",
352
+ createdAt: "created_at",
353
+ updatedAt: "updated_at",
354
+ redirectUris: "redirect_uris",
355
+ postLogoutRedirectUris: "post_logout_redirect_uris",
356
+ tokenEndpointAuthMethod: "token_endpoint_auth_method",
357
+ grantTypes: "grant_types",
358
+ responseTypes: "response_types",
359
+ requirePKCE: "require_pkce",
360
+ softwareId: "software_id",
361
+ softwareVersion: "software_version",
362
+ softwareStatement: "software_statement",
363
+ referenceId: "reference_id"
364
+ }
365
+ };
366
+ var AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;
367
+ var AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {
368
+ modelName: import_system2.SystemObjectName.OAUTH_ACCESS_TOKEN,
369
+ // 'sys_oauth_access_token'
370
+ fields: {
371
+ clientId: "client_id",
372
+ sessionId: "session_id",
373
+ userId: "user_id",
374
+ referenceId: "reference_id",
375
+ refreshId: "refresh_id",
376
+ expiresAt: "expires_at",
377
+ createdAt: "created_at"
378
+ }
379
+ };
380
+ var AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {
381
+ modelName: import_system2.SystemObjectName.OAUTH_REFRESH_TOKEN,
382
+ // 'sys_oauth_refresh_token'
383
+ fields: {
384
+ clientId: "client_id",
385
+ sessionId: "session_id",
386
+ userId: "user_id",
387
+ referenceId: "reference_id",
388
+ expiresAt: "expires_at",
389
+ createdAt: "created_at",
390
+ authTime: "auth_time"
391
+ }
392
+ };
393
+ var AUTH_OAUTH_CONSENT_SCHEMA = {
394
+ modelName: import_system2.SystemObjectName.OAUTH_CONSENT,
395
+ // 'sys_oauth_consent'
396
+ fields: {
397
+ clientId: "client_id",
398
+ userId: "user_id",
399
+ referenceId: "reference_id",
400
+ createdAt: "created_at",
401
+ updatedAt: "updated_at"
402
+ }
403
+ };
404
+ var AUTH_DEVICE_CODE_SCHEMA = {
405
+ modelName: import_system2.SystemObjectName.DEVICE_CODE,
406
+ // 'sys_device_code'
407
+ fields: {
408
+ deviceCode: "device_code",
409
+ userCode: "user_code",
410
+ userId: "user_id",
411
+ expiresAt: "expires_at",
412
+ lastPolledAt: "last_polled_at",
413
+ pollingInterval: "polling_interval",
414
+ clientId: "client_id"
415
+ }
416
+ };
340
417
  function buildTwoFactorPluginSchema() {
341
418
  return {
342
419
  twoFactor: AUTH_TWO_FACTOR_SCHEMA,
@@ -357,6 +434,35 @@ function buildOrganizationPluginSchema() {
357
434
  }
358
435
  };
359
436
  }
437
+ var AUTH_JWKS_SCHEMA = {
438
+ modelName: import_system2.SystemObjectName.JWKS,
439
+ // 'sys_jwks'
440
+ fields: {
441
+ publicKey: "public_key",
442
+ privateKey: "private_key",
443
+ createdAt: "created_at",
444
+ expiresAt: "expires_at"
445
+ }
446
+ };
447
+ function buildJwtPluginSchema() {
448
+ return {
449
+ jwks: AUTH_JWKS_SCHEMA
450
+ };
451
+ }
452
+ function buildOauthProviderPluginSchema() {
453
+ return {
454
+ oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,
455
+ oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
456
+ oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
457
+ oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA
458
+ };
459
+ }
460
+ var buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;
461
+ function buildDeviceAuthorizationPluginSchema() {
462
+ return {
463
+ deviceCode: AUTH_DEVICE_CODE_SCHEMA
464
+ };
465
+ }
360
466
 
361
467
  // src/auth-manager.ts
362
468
  var AuthManager = class {
@@ -370,16 +476,18 @@ var AuthManager = class {
370
476
  /**
371
477
  * Get or create the better-auth instance (lazy initialization)
372
478
  */
373
- getOrCreateAuth() {
479
+ async getOrCreateAuth() {
374
480
  if (!this.auth) {
375
- this.auth = this.createAuthInstance();
481
+ this.auth = await this.createAuthInstance();
376
482
  }
377
483
  return this.auth;
378
484
  }
379
485
  /**
380
486
  * Create a better-auth instance from configuration
381
487
  */
382
- createAuthInstance() {
488
+ async createAuthInstance() {
489
+ const { betterAuth } = await import("better-auth");
490
+ const plugins = await this.buildPluginList();
383
491
  const betterAuthConfig = {
384
492
  // Base configuration
385
493
  secret: this.config.secret || this.generateSecret(),
@@ -431,7 +539,7 @@ var AuthManager = class {
431
539
  // 1 day default
432
540
  },
433
541
  // better-auth plugins — registered based on AuthPluginConfig flags
434
- plugins: this.buildPluginList(),
542
+ plugins,
435
543
  // Trusted origins for CSRF protection (supports wildcards like "https://*.example.com")
436
544
  // Auto-includes origins from CORS_ORIGIN env var so CORS and CSRF stay in sync.
437
545
  ...(() => {
@@ -457,7 +565,7 @@ var AuthManager = class {
457
565
  }
458
566
  } : {}
459
567
  };
460
- return (0, import_better_auth.betterAuth)(betterAuthConfig);
568
+ return betterAuth(betterAuthConfig);
461
569
  }
462
570
  /**
463
571
  * Build the list of better-auth plugins based on AuthPluginConfig flags.
@@ -466,21 +574,52 @@ var AuthManager = class {
466
574
  * a `schema` option containing the appropriate snake_case field mappings,
467
575
  * so that `createAdapterFactory` transforms them automatically.
468
576
  */
469
- buildPluginList() {
470
- const pluginConfig = this.config.plugins;
577
+ async buildPluginList() {
578
+ const pluginConfig = this.config.plugins ?? {};
471
579
  const plugins = [];
472
- if (pluginConfig?.organization) {
473
- plugins.push((0, import_organization.organization)({
474
- schema: buildOrganizationPluginSchema()
580
+ const enabled = {
581
+ organization: pluginConfig.organization ?? true,
582
+ twoFactor: pluginConfig.twoFactor ?? false,
583
+ passkeys: pluginConfig.passkeys ?? false,
584
+ magicLink: pluginConfig.magicLink ?? false,
585
+ oidcProvider: pluginConfig.oidcProvider ?? false,
586
+ deviceAuthorization: pluginConfig.deviceAuthorization ?? false
587
+ };
588
+ const { bearer } = await import("better-auth/plugins/bearer");
589
+ plugins.push(bearer());
590
+ if (enabled.organization) {
591
+ const { organization } = await import("better-auth/plugins/organization");
592
+ plugins.push(organization({
593
+ schema: buildOrganizationPluginSchema(),
594
+ // Enable the team sub-feature so the framework's `sys_team` /
595
+ // `sys_team_member` tables (already declared in platform-objects)
596
+ // are actually wired up to better-auth's CRUD endpoints
597
+ // (`/organization/{create,update,remove,list}-team[s]` and
598
+ // `/organization/{add,remove,list}-team-member[s]`). The Account
599
+ // portal exposes a Teams page; without this flag those endpoints
600
+ // 404 and the section silently breaks.
601
+ teams: { enabled: true },
602
+ // No mailer is wired in framework yet — log the accept URL so
603
+ // operators / UI can fall back to copy-paste flows. Replace this
604
+ // with a real mail integration when available.
605
+ sendInvitationEmail: async ({ email, invitation, organization: org, inviter }) => {
606
+ const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
607
+ const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;
608
+ console.warn(
609
+ `[AuthManager] Invitation email not configured. To: ${email} (org: ${org?.name ?? invitation.organizationId}, role: ${invitation.role}, inviter: ${inviter?.user?.email ?? "unknown"}) URL: ${acceptUrl}`
610
+ );
611
+ }
475
612
  }));
476
613
  }
477
- if (pluginConfig?.twoFactor) {
478
- plugins.push((0, import_two_factor.twoFactor)({
614
+ if (enabled.twoFactor) {
615
+ const { twoFactor } = await import("better-auth/plugins/two-factor");
616
+ plugins.push(twoFactor({
479
617
  schema: buildTwoFactorPluginSchema()
480
618
  }));
481
619
  }
482
- if (pluginConfig?.magicLink) {
483
- plugins.push((0, import_magic_link.magicLink)({
620
+ if (enabled.magicLink) {
621
+ const { magicLink } = await import("better-auth/plugins/magic-link");
622
+ plugins.push(magicLink({
484
623
  sendMagicLink: async ({ email, url }) => {
485
624
  console.warn(
486
625
  `[AuthManager] Magic-link requested for ${email} but no sendMagicLink handler configured. URL: ${url}`
@@ -488,6 +627,43 @@ var AuthManager = class {
488
627
  }
489
628
  }));
490
629
  }
630
+ if (this.config.oidcProviders?.length) {
631
+ const { genericOAuth } = await import("better-auth/plugins/generic-oauth");
632
+ plugins.push(genericOAuth({
633
+ config: this.config.oidcProviders.map((p) => ({
634
+ providerId: p.providerId,
635
+ ...p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {},
636
+ ...p.issuer ? { issuer: p.issuer } : {},
637
+ ...p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {},
638
+ ...p.tokenUrl ? { tokenUrl: p.tokenUrl } : {},
639
+ ...p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {},
640
+ clientId: p.clientId,
641
+ clientSecret: p.clientSecret,
642
+ ...p.scopes ? { scopes: p.scopes } : {},
643
+ ...p.pkce != null ? { pkce: p.pkce } : {}
644
+ }))
645
+ }));
646
+ }
647
+ if (enabled.oidcProvider) {
648
+ const { jwt } = await import("better-auth/plugins");
649
+ plugins.push(jwt({ schema: buildJwtPluginSchema() }));
650
+ const { oauthProvider } = await import("@better-auth/oauth-provider");
651
+ const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
652
+ plugins.push(oauthProvider({
653
+ // Account SPA renders both pages — see apps/account.
654
+ loginPage: `${baseUrl}/_account/login`,
655
+ consentPage: `${baseUrl}/_account/oauth/consent`,
656
+ schema: buildOauthProviderPluginSchema()
657
+ }));
658
+ }
659
+ if (enabled.deviceAuthorization) {
660
+ const { deviceAuthorization } = await import("better-auth/plugins/device-authorization");
661
+ const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
662
+ plugins.push(deviceAuthorization({
663
+ verificationUri: `${baseUrl}/_account/auth/device`,
664
+ schema: buildDeviceAuthorizationPluginSchema()
665
+ }));
666
+ }
491
667
  return plugins;
492
668
  }
493
669
  /**
@@ -563,7 +739,7 @@ var AuthManager = class {
563
739
  * @returns Web standard Response object
564
740
  */
565
741
  async handleRequest(request) {
566
- const auth = this.getOrCreateAuth();
742
+ const auth = await this.getOrCreateAuth();
567
743
  const response = await auth.handler(request);
568
744
  if (response.status >= 500) {
569
745
  try {
@@ -579,18 +755,19 @@ var AuthManager = class {
579
755
  * Get the better-auth API for programmatic access
580
756
  * Use this for server-side operations (e.g., creating users, checking sessions)
581
757
  */
582
- get api() {
583
- return this.getOrCreateAuth().api;
758
+ async getApi() {
759
+ const auth = await this.getOrCreateAuth();
760
+ return auth.api;
584
761
  }
585
- /**
586
- * Get public authentication configuration
587
- * Returns safe, non-sensitive configuration that can be exposed to the frontend
588
- *
589
- * This allows the frontend to discover:
590
- * - Which social/OAuth providers are available
591
- * - Whether email/password login is enabled
592
- * - Which advanced features are enabled (2FA, magic links, etc.)
593
- */
762
+ // ---------------------------------------------------------------------------
763
+ // Device Flow (CLI browser-based login)
764
+ //
765
+ // The device authorization flow (RFC 8628) is now handled entirely by
766
+ // better-auth's `device-authorization` plugin. Endpoints are exposed at
767
+ // `${basePath}/device/{code,token,approve,deny}` and persisted in
768
+ // `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in
769
+ // AuthPluginConfig.
770
+ // ---------------------------------------------------------------------------
594
771
  getPublicConfig() {
595
772
  const socialProviders = [];
596
773
  if (this.config.socialProviders) {
@@ -610,11 +787,22 @@ var AuthManager = class {
610
787
  socialProviders.push({
611
788
  id,
612
789
  name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),
613
- enabled: true
790
+ enabled: true,
791
+ type: "social"
614
792
  });
615
793
  }
616
794
  }
617
795
  }
796
+ if (this.config.oidcProviders?.length) {
797
+ for (const p of this.config.oidcProviders) {
798
+ socialProviders.push({
799
+ id: p.providerId,
800
+ name: p.name ?? p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1),
801
+ enabled: true,
802
+ type: "oidc"
803
+ });
804
+ }
805
+ }
618
806
  const emailPasswordConfig = this.config.emailAndPassword ?? {};
619
807
  const emailPassword = {
620
808
  enabled: emailPasswordConfig.enabled !== false,
@@ -627,7 +815,9 @@ var AuthManager = class {
627
815
  twoFactor: pluginConfig.twoFactor ?? false,
628
816
  passkeys: pluginConfig.passkeys ?? false,
629
817
  magicLink: pluginConfig.magicLink ?? false,
630
- organization: pluginConfig.organization ?? false
818
+ organization: pluginConfig.organization ?? true,
819
+ oidcProvider: pluginConfig.oidcProvider ?? false,
820
+ deviceAuthorization: pluginConfig.deviceAuthorization ?? false
631
821
  };
632
822
  return {
633
823
  emailPassword,
@@ -635,776 +825,50 @@ var AuthManager = class {
635
825
  features
636
826
  };
637
827
  }
638
- };
639
-
640
- // src/objects/sys-user.object.ts
641
- var import_data = require("@objectstack/spec/data");
642
- var SysUser = import_data.ObjectSchema.create({
643
- namespace: "sys",
644
- name: "user",
645
- label: "User",
646
- pluralLabel: "Users",
647
- icon: "user",
648
- isSystem: true,
649
- description: "User accounts for authentication",
650
- titleFormat: "{name} ({email})",
651
- compactLayout: ["name", "email", "email_verified"],
652
- fields: {
653
- id: import_data.Field.text({
654
- label: "User ID",
655
- required: true,
656
- readonly: true
657
- }),
658
- created_at: import_data.Field.datetime({
659
- label: "Created At",
660
- defaultValue: "NOW()",
661
- readonly: true
662
- }),
663
- updated_at: import_data.Field.datetime({
664
- label: "Updated At",
665
- defaultValue: "NOW()",
666
- readonly: true
667
- }),
668
- email: import_data.Field.email({
669
- label: "Email",
670
- required: true,
671
- searchable: true
672
- }),
673
- email_verified: import_data.Field.boolean({
674
- label: "Email Verified",
675
- defaultValue: false
676
- }),
677
- name: import_data.Field.text({
678
- label: "Name",
679
- required: true,
680
- searchable: true,
681
- maxLength: 255
682
- }),
683
- image: import_data.Field.url({
684
- label: "Profile Image",
685
- required: false
686
- })
687
- },
688
- indexes: [
689
- { fields: ["email"], unique: true },
690
- { fields: ["created_at"], unique: false }
691
- ],
692
- enable: {
693
- trackHistory: true,
694
- searchable: true,
695
- apiEnabled: true,
696
- apiMethods: ["get", "list", "create", "update", "delete"],
697
- trash: true,
698
- mru: true
699
- },
700
- validations: [
701
- {
702
- name: "email_unique",
703
- type: "unique",
704
- severity: "error",
705
- message: "Email must be unique",
706
- fields: ["email"],
707
- caseSensitive: false
708
- }
709
- ]
710
- });
711
-
712
- // src/objects/sys-session.object.ts
713
- var import_data2 = require("@objectstack/spec/data");
714
- var SysSession = import_data2.ObjectSchema.create({
715
- namespace: "sys",
716
- name: "session",
717
- label: "Session",
718
- pluralLabel: "Sessions",
719
- icon: "key",
720
- isSystem: true,
721
- description: "Active user sessions",
722
- titleFormat: "Session {token}",
723
- compactLayout: ["user_id", "expires_at", "ip_address"],
724
- fields: {
725
- id: import_data2.Field.text({
726
- label: "Session ID",
727
- required: true,
728
- readonly: true
729
- }),
730
- created_at: import_data2.Field.datetime({
731
- label: "Created At",
732
- defaultValue: "NOW()",
733
- readonly: true
734
- }),
735
- updated_at: import_data2.Field.datetime({
736
- label: "Updated At",
737
- defaultValue: "NOW()",
738
- readonly: true
739
- }),
740
- user_id: import_data2.Field.text({
741
- label: "User ID",
742
- required: true
743
- }),
744
- expires_at: import_data2.Field.datetime({
745
- label: "Expires At",
746
- required: true
747
- }),
748
- token: import_data2.Field.text({
749
- label: "Session Token",
750
- required: true
751
- }),
752
- ip_address: import_data2.Field.text({
753
- label: "IP Address",
754
- required: false,
755
- maxLength: 45
756
- // Support IPv6
757
- }),
758
- user_agent: import_data2.Field.textarea({
759
- label: "User Agent",
760
- required: false
761
- })
762
- },
763
- indexes: [
764
- { fields: ["token"], unique: true },
765
- { fields: ["user_id"], unique: false },
766
- { fields: ["expires_at"], unique: false }
767
- ],
768
- enable: {
769
- trackHistory: false,
770
- searchable: false,
771
- apiEnabled: true,
772
- apiMethods: ["get", "list", "create", "delete"],
773
- trash: false,
774
- mru: false
775
- }
776
- });
777
-
778
- // src/objects/sys-account.object.ts
779
- var import_data3 = require("@objectstack/spec/data");
780
- var SysAccount = import_data3.ObjectSchema.create({
781
- namespace: "sys",
782
- name: "account",
783
- label: "Account",
784
- pluralLabel: "Accounts",
785
- icon: "link",
786
- isSystem: true,
787
- description: "OAuth and authentication provider accounts",
788
- titleFormat: "{provider_id} - {account_id}",
789
- compactLayout: ["provider_id", "user_id", "account_id"],
790
- fields: {
791
- id: import_data3.Field.text({
792
- label: "Account ID",
793
- required: true,
794
- readonly: true
795
- }),
796
- created_at: import_data3.Field.datetime({
797
- label: "Created At",
798
- defaultValue: "NOW()",
799
- readonly: true
800
- }),
801
- updated_at: import_data3.Field.datetime({
802
- label: "Updated At",
803
- defaultValue: "NOW()",
804
- readonly: true
805
- }),
806
- provider_id: import_data3.Field.text({
807
- label: "Provider ID",
808
- required: true,
809
- description: "OAuth provider identifier (google, github, etc.)"
810
- }),
811
- account_id: import_data3.Field.text({
812
- label: "Provider Account ID",
813
- required: true,
814
- description: "User's ID in the provider's system"
815
- }),
816
- user_id: import_data3.Field.text({
817
- label: "User ID",
818
- required: true,
819
- description: "Link to user table"
820
- }),
821
- access_token: import_data3.Field.textarea({
822
- label: "Access Token",
823
- required: false
824
- }),
825
- refresh_token: import_data3.Field.textarea({
826
- label: "Refresh Token",
827
- required: false
828
- }),
829
- id_token: import_data3.Field.textarea({
830
- label: "ID Token",
831
- required: false
832
- }),
833
- access_token_expires_at: import_data3.Field.datetime({
834
- label: "Access Token Expires At",
835
- required: false
836
- }),
837
- refresh_token_expires_at: import_data3.Field.datetime({
838
- label: "Refresh Token Expires At",
839
- required: false
840
- }),
841
- scope: import_data3.Field.text({
842
- label: "OAuth Scope",
843
- required: false
844
- }),
845
- password: import_data3.Field.text({
846
- label: "Password Hash",
847
- required: false,
848
- description: "Hashed password for email/password provider"
849
- })
850
- },
851
- indexes: [
852
- { fields: ["user_id"], unique: false },
853
- { fields: ["provider_id", "account_id"], unique: true }
854
- ],
855
- enable: {
856
- trackHistory: false,
857
- searchable: false,
858
- apiEnabled: true,
859
- apiMethods: ["get", "list", "create", "update", "delete"],
860
- trash: true,
861
- mru: false
862
- }
863
- });
864
-
865
- // src/objects/sys-verification.object.ts
866
- var import_data4 = require("@objectstack/spec/data");
867
- var SysVerification = import_data4.ObjectSchema.create({
868
- namespace: "sys",
869
- name: "verification",
870
- label: "Verification",
871
- pluralLabel: "Verifications",
872
- icon: "shield-check",
873
- isSystem: true,
874
- description: "Email and phone verification tokens",
875
- titleFormat: "Verification for {identifier}",
876
- compactLayout: ["identifier", "expires_at", "created_at"],
877
- fields: {
878
- id: import_data4.Field.text({
879
- label: "Verification ID",
880
- required: true,
881
- readonly: true
882
- }),
883
- created_at: import_data4.Field.datetime({
884
- label: "Created At",
885
- defaultValue: "NOW()",
886
- readonly: true
887
- }),
888
- updated_at: import_data4.Field.datetime({
889
- label: "Updated At",
890
- defaultValue: "NOW()",
891
- readonly: true
892
- }),
893
- value: import_data4.Field.text({
894
- label: "Verification Token",
895
- required: true,
896
- description: "Token or code for verification"
897
- }),
898
- expires_at: import_data4.Field.datetime({
899
- label: "Expires At",
900
- required: true
901
- }),
902
- identifier: import_data4.Field.text({
903
- label: "Identifier",
904
- required: true,
905
- description: "Email address or phone number"
906
- })
907
- },
908
- indexes: [
909
- { fields: ["value"], unique: true },
910
- { fields: ["identifier"], unique: false },
911
- { fields: ["expires_at"], unique: false }
912
- ],
913
- enable: {
914
- trackHistory: false,
915
- searchable: false,
916
- apiEnabled: true,
917
- apiMethods: ["get", "create", "delete"],
918
- trash: false,
919
- mru: false
920
- }
921
- });
922
-
923
- // src/objects/sys-organization.object.ts
924
- var import_data5 = require("@objectstack/spec/data");
925
- var SysOrganization = import_data5.ObjectSchema.create({
926
- namespace: "sys",
927
- name: "organization",
928
- label: "Organization",
929
- pluralLabel: "Organizations",
930
- icon: "building-2",
931
- isSystem: true,
932
- description: "Organizations for multi-tenant grouping",
933
- titleFormat: "{name}",
934
- compactLayout: ["name", "slug", "created_at"],
935
- fields: {
936
- id: import_data5.Field.text({
937
- label: "Organization ID",
938
- required: true,
939
- readonly: true
940
- }),
941
- created_at: import_data5.Field.datetime({
942
- label: "Created At",
943
- defaultValue: "NOW()",
944
- readonly: true
945
- }),
946
- updated_at: import_data5.Field.datetime({
947
- label: "Updated At",
948
- defaultValue: "NOW()",
949
- readonly: true
950
- }),
951
- name: import_data5.Field.text({
952
- label: "Name",
953
- required: true,
954
- searchable: true,
955
- maxLength: 255
956
- }),
957
- slug: import_data5.Field.text({
958
- label: "Slug",
959
- required: false,
960
- maxLength: 255,
961
- description: "URL-friendly identifier"
962
- }),
963
- logo: import_data5.Field.url({
964
- label: "Logo",
965
- required: false
966
- }),
967
- metadata: import_data5.Field.textarea({
968
- label: "Metadata",
969
- required: false,
970
- description: "JSON-serialized organization metadata"
971
- })
972
- },
973
- indexes: [
974
- { fields: ["slug"], unique: true },
975
- { fields: ["name"] }
976
- ],
977
- enable: {
978
- trackHistory: true,
979
- searchable: true,
980
- apiEnabled: true,
981
- apiMethods: ["get", "list", "create", "update", "delete"],
982
- trash: true,
983
- mru: true
984
- }
985
- });
986
-
987
- // src/objects/sys-member.object.ts
988
- var import_data6 = require("@objectstack/spec/data");
989
- var SysMember = import_data6.ObjectSchema.create({
990
- namespace: "sys",
991
- name: "member",
992
- label: "Member",
993
- pluralLabel: "Members",
994
- icon: "user-check",
995
- isSystem: true,
996
- description: "Organization membership records",
997
- titleFormat: "{user_id} in {organization_id}",
998
- compactLayout: ["user_id", "organization_id", "role"],
999
- fields: {
1000
- id: import_data6.Field.text({
1001
- label: "Member ID",
1002
- required: true,
1003
- readonly: true
1004
- }),
1005
- created_at: import_data6.Field.datetime({
1006
- label: "Created At",
1007
- defaultValue: "NOW()",
1008
- readonly: true
1009
- }),
1010
- organization_id: import_data6.Field.text({
1011
- label: "Organization ID",
1012
- required: true
1013
- }),
1014
- user_id: import_data6.Field.text({
1015
- label: "User ID",
1016
- required: true
1017
- }),
1018
- role: import_data6.Field.text({
1019
- label: "Role",
1020
- required: false,
1021
- description: "Member role within the organization (e.g. admin, member)",
1022
- maxLength: 100
1023
- })
1024
- },
1025
- indexes: [
1026
- { fields: ["organization_id", "user_id"], unique: true },
1027
- { fields: ["user_id"] }
1028
- ],
1029
- enable: {
1030
- trackHistory: true,
1031
- searchable: false,
1032
- apiEnabled: true,
1033
- apiMethods: ["get", "list", "create", "update", "delete"],
1034
- trash: false,
1035
- mru: false
1036
- }
1037
- });
1038
-
1039
- // src/objects/sys-invitation.object.ts
1040
- var import_data7 = require("@objectstack/spec/data");
1041
- var SysInvitation = import_data7.ObjectSchema.create({
1042
- namespace: "sys",
1043
- name: "invitation",
1044
- label: "Invitation",
1045
- pluralLabel: "Invitations",
1046
- icon: "mail",
1047
- isSystem: true,
1048
- description: "Organization invitations for user onboarding",
1049
- titleFormat: "Invitation to {organization_id}",
1050
- compactLayout: ["email", "organization_id", "status"],
1051
- fields: {
1052
- id: import_data7.Field.text({
1053
- label: "Invitation ID",
1054
- required: true,
1055
- readonly: true
1056
- }),
1057
- created_at: import_data7.Field.datetime({
1058
- label: "Created At",
1059
- defaultValue: "NOW()",
1060
- readonly: true
1061
- }),
1062
- organization_id: import_data7.Field.text({
1063
- label: "Organization ID",
1064
- required: true
1065
- }),
1066
- email: import_data7.Field.email({
1067
- label: "Email",
1068
- required: true,
1069
- description: "Email address of the invited user"
1070
- }),
1071
- role: import_data7.Field.text({
1072
- label: "Role",
1073
- required: false,
1074
- maxLength: 100,
1075
- description: "Role to assign upon acceptance"
1076
- }),
1077
- status: import_data7.Field.select(["pending", "accepted", "rejected", "expired", "canceled"], {
1078
- label: "Status",
1079
- required: true,
1080
- defaultValue: "pending"
1081
- }),
1082
- inviter_id: import_data7.Field.text({
1083
- label: "Inviter ID",
1084
- required: true,
1085
- description: "User ID of the person who sent the invitation"
1086
- }),
1087
- expires_at: import_data7.Field.datetime({
1088
- label: "Expires At",
1089
- required: true
1090
- }),
1091
- team_id: import_data7.Field.text({
1092
- label: "Team ID",
1093
- required: false,
1094
- description: "Optional team to assign upon acceptance"
1095
- })
1096
- },
1097
- indexes: [
1098
- { fields: ["organization_id"] },
1099
- { fields: ["email"] },
1100
- { fields: ["expires_at"] }
1101
- ],
1102
- enable: {
1103
- trackHistory: true,
1104
- searchable: false,
1105
- apiEnabled: true,
1106
- apiMethods: ["get", "list", "create", "update", "delete"],
1107
- trash: false,
1108
- mru: false
1109
- }
1110
- });
1111
-
1112
- // src/objects/sys-team.object.ts
1113
- var import_data8 = require("@objectstack/spec/data");
1114
- var SysTeam = import_data8.ObjectSchema.create({
1115
- namespace: "sys",
1116
- name: "team",
1117
- label: "Team",
1118
- pluralLabel: "Teams",
1119
- icon: "users",
1120
- isSystem: true,
1121
- description: "Teams within organizations for fine-grained grouping",
1122
- titleFormat: "{name}",
1123
- compactLayout: ["name", "organization_id", "created_at"],
1124
- fields: {
1125
- id: import_data8.Field.text({
1126
- label: "Team ID",
1127
- required: true,
1128
- readonly: true
1129
- }),
1130
- created_at: import_data8.Field.datetime({
1131
- label: "Created At",
1132
- defaultValue: "NOW()",
1133
- readonly: true
1134
- }),
1135
- updated_at: import_data8.Field.datetime({
1136
- label: "Updated At",
1137
- defaultValue: "NOW()",
1138
- readonly: true
1139
- }),
1140
- name: import_data8.Field.text({
1141
- label: "Name",
1142
- required: true,
1143
- searchable: true,
1144
- maxLength: 255
1145
- }),
1146
- organization_id: import_data8.Field.text({
1147
- label: "Organization ID",
1148
- required: true
1149
- })
1150
- },
1151
- indexes: [
1152
- { fields: ["organization_id"] },
1153
- { fields: ["name", "organization_id"], unique: true }
1154
- ],
1155
- enable: {
1156
- trackHistory: true,
1157
- searchable: true,
1158
- apiEnabled: true,
1159
- apiMethods: ["get", "list", "create", "update", "delete"],
1160
- trash: true,
1161
- mru: false
1162
- }
1163
- });
1164
-
1165
- // src/objects/sys-team-member.object.ts
1166
- var import_data9 = require("@objectstack/spec/data");
1167
- var SysTeamMember = import_data9.ObjectSchema.create({
1168
- namespace: "sys",
1169
- name: "team_member",
1170
- label: "Team Member",
1171
- pluralLabel: "Team Members",
1172
- icon: "user-plus",
1173
- isSystem: true,
1174
- description: "Team membership records linking users to teams",
1175
- titleFormat: "{user_id} in {team_id}",
1176
- compactLayout: ["user_id", "team_id", "created_at"],
1177
- fields: {
1178
- id: import_data9.Field.text({
1179
- label: "Team Member ID",
1180
- required: true,
1181
- readonly: true
1182
- }),
1183
- created_at: import_data9.Field.datetime({
1184
- label: "Created At",
1185
- defaultValue: "NOW()",
1186
- readonly: true
1187
- }),
1188
- team_id: import_data9.Field.text({
1189
- label: "Team ID",
1190
- required: true
1191
- }),
1192
- user_id: import_data9.Field.text({
1193
- label: "User ID",
1194
- required: true
1195
- })
1196
- },
1197
- indexes: [
1198
- { fields: ["team_id", "user_id"], unique: true },
1199
- { fields: ["user_id"] }
1200
- ],
1201
- enable: {
1202
- trackHistory: true,
1203
- searchable: false,
1204
- apiEnabled: true,
1205
- apiMethods: ["get", "list", "create", "delete"],
1206
- trash: false,
1207
- mru: false
1208
- }
1209
- });
1210
-
1211
- // src/objects/sys-api-key.object.ts
1212
- var import_data10 = require("@objectstack/spec/data");
1213
- var SysApiKey = import_data10.ObjectSchema.create({
1214
- namespace: "sys",
1215
- name: "api_key",
1216
- label: "API Key",
1217
- pluralLabel: "API Keys",
1218
- icon: "key-round",
1219
- isSystem: true,
1220
- description: "API keys for programmatic access",
1221
- titleFormat: "{name}",
1222
- compactLayout: ["name", "user_id", "expires_at"],
1223
- fields: {
1224
- id: import_data10.Field.text({
1225
- label: "API Key ID",
1226
- required: true,
1227
- readonly: true
1228
- }),
1229
- created_at: import_data10.Field.datetime({
1230
- label: "Created At",
1231
- defaultValue: "NOW()",
1232
- readonly: true
1233
- }),
1234
- updated_at: import_data10.Field.datetime({
1235
- label: "Updated At",
1236
- defaultValue: "NOW()",
1237
- readonly: true
1238
- }),
1239
- name: import_data10.Field.text({
1240
- label: "Name",
1241
- required: true,
1242
- maxLength: 255,
1243
- description: "Human-readable label for the API key"
1244
- }),
1245
- key: import_data10.Field.text({
1246
- label: "Key",
1247
- required: true,
1248
- description: "Hashed API key value"
1249
- }),
1250
- prefix: import_data10.Field.text({
1251
- label: "Prefix",
1252
- required: false,
1253
- maxLength: 16,
1254
- description: 'Visible prefix for identifying the key (e.g., "osk_")'
1255
- }),
1256
- user_id: import_data10.Field.text({
1257
- label: "User ID",
1258
- required: true,
1259
- description: "Owner user of this API key"
1260
- }),
1261
- scopes: import_data10.Field.textarea({
1262
- label: "Scopes",
1263
- required: false,
1264
- description: "JSON array of permission scopes"
1265
- }),
1266
- expires_at: import_data10.Field.datetime({
1267
- label: "Expires At",
1268
- required: false
1269
- }),
1270
- last_used_at: import_data10.Field.datetime({
1271
- label: "Last Used At",
1272
- required: false
1273
- }),
1274
- revoked: import_data10.Field.boolean({
1275
- label: "Revoked",
1276
- defaultValue: false
1277
- })
1278
- },
1279
- indexes: [
1280
- { fields: ["key"], unique: true },
1281
- { fields: ["user_id"] },
1282
- { fields: ["prefix"] }
1283
- ],
1284
- enable: {
1285
- trackHistory: true,
1286
- searchable: false,
1287
- apiEnabled: true,
1288
- apiMethods: ["get", "list", "create", "update", "delete"],
1289
- trash: false,
1290
- mru: false
1291
- }
1292
- });
1293
-
1294
- // src/objects/sys-two-factor.object.ts
1295
- var import_data11 = require("@objectstack/spec/data");
1296
- var SysTwoFactor = import_data11.ObjectSchema.create({
1297
- namespace: "sys",
1298
- name: "two_factor",
1299
- label: "Two Factor",
1300
- pluralLabel: "Two Factor Credentials",
1301
- icon: "smartphone",
1302
- isSystem: true,
1303
- description: "Two-factor authentication credentials",
1304
- titleFormat: "Two-factor for {user_id}",
1305
- compactLayout: ["user_id", "created_at"],
1306
- fields: {
1307
- id: import_data11.Field.text({
1308
- label: "Two Factor ID",
1309
- required: true,
1310
- readonly: true
1311
- }),
1312
- created_at: import_data11.Field.datetime({
1313
- label: "Created At",
1314
- defaultValue: "NOW()",
1315
- readonly: true
1316
- }),
1317
- updated_at: import_data11.Field.datetime({
1318
- label: "Updated At",
1319
- defaultValue: "NOW()",
1320
- readonly: true
1321
- }),
1322
- user_id: import_data11.Field.text({
1323
- label: "User ID",
1324
- required: true
1325
- }),
1326
- secret: import_data11.Field.text({
1327
- label: "Secret",
1328
- required: true,
1329
- description: "TOTP secret key"
1330
- }),
1331
- backup_codes: import_data11.Field.textarea({
1332
- label: "Backup Codes",
1333
- required: false,
1334
- description: "JSON-serialized backup recovery codes"
1335
- })
1336
- },
1337
- indexes: [
1338
- { fields: ["user_id"], unique: true }
1339
- ],
1340
- enable: {
1341
- trackHistory: false,
1342
- searchable: false,
1343
- apiEnabled: true,
1344
- apiMethods: ["get", "create", "update", "delete"],
1345
- trash: false,
1346
- mru: false
828
+ /**
829
+ * Returns the data engine wired into this auth manager. Used by route
830
+ * handlers (e.g. bootstrap-status) that need to query identity tables
831
+ * directly without going through better-auth.
832
+ */
833
+ getDataEngine() {
834
+ return this.config.dataEngine;
1347
835
  }
1348
- });
836
+ };
1349
837
 
1350
- // src/objects/sys-user-preference.object.ts
1351
- var import_data12 = require("@objectstack/spec/data");
1352
- var SysUserPreference = import_data12.ObjectSchema.create({
838
+ // src/manifest.ts
839
+ var import_identity = require("@objectstack/platform-objects/identity");
840
+ var AUTH_PLUGIN_ID = "com.objectstack.plugin-auth";
841
+ var AUTH_PLUGIN_VERSION = "3.0.1";
842
+ var authIdentityObjects = [
843
+ import_identity.SysUser,
844
+ import_identity.SysSession,
845
+ import_identity.SysAccount,
846
+ import_identity.SysVerification,
847
+ import_identity.SysOrganization,
848
+ import_identity.SysMember,
849
+ import_identity.SysInvitation,
850
+ import_identity.SysTeam,
851
+ import_identity.SysTeamMember,
852
+ import_identity.SysApiKey,
853
+ import_identity.SysTwoFactor,
854
+ import_identity.SysUserPreference,
855
+ import_identity.SysOauthApplication,
856
+ import_identity.SysOauthAccessToken,
857
+ import_identity.SysOauthRefreshToken,
858
+ import_identity.SysOauthConsent,
859
+ import_identity.SysJwks,
860
+ import_identity.SysDeviceCode
861
+ ];
862
+ var authPluginManifestHeader = {
863
+ id: AUTH_PLUGIN_ID,
1353
864
  namespace: "sys",
1354
- name: "user_preference",
1355
- label: "User Preference",
1356
- pluralLabel: "User Preferences",
1357
- icon: "settings",
1358
- isSystem: true,
1359
- description: "Per-user key-value preferences (theme, locale, etc.)",
1360
- titleFormat: "{key}",
1361
- compactLayout: ["user_id", "key"],
1362
- fields: {
1363
- id: import_data12.Field.text({
1364
- label: "Preference ID",
1365
- required: true,
1366
- readonly: true
1367
- }),
1368
- created_at: import_data12.Field.datetime({
1369
- label: "Created At",
1370
- defaultValue: "NOW()",
1371
- readonly: true
1372
- }),
1373
- updated_at: import_data12.Field.datetime({
1374
- label: "Updated At",
1375
- defaultValue: "NOW()",
1376
- readonly: true
1377
- }),
1378
- user_id: import_data12.Field.text({
1379
- label: "User ID",
1380
- required: true,
1381
- maxLength: 255,
1382
- description: "Owner user of this preference"
1383
- }),
1384
- key: import_data12.Field.text({
1385
- label: "Key",
1386
- required: true,
1387
- maxLength: 255,
1388
- description: "Preference key (e.g., theme, locale, plugin.ai.auto_save)"
1389
- }),
1390
- value: import_data12.Field.json({
1391
- label: "Value",
1392
- description: "Preference value (any JSON-serializable type)"
1393
- })
1394
- },
1395
- indexes: [
1396
- { fields: ["user_id", "key"], unique: true },
1397
- { fields: ["user_id"], unique: false }
1398
- ],
1399
- enable: {
1400
- trackHistory: false,
1401
- searchable: false,
1402
- apiEnabled: true,
1403
- apiMethods: ["get", "list", "create", "update", "delete"],
1404
- trash: false,
1405
- mru: false
1406
- }
1407
- });
865
+ version: AUTH_PLUGIN_VERSION,
866
+ type: "plugin",
867
+ scope: "system",
868
+ defaultDatasource: "cloud",
869
+ name: "Authentication & Identity Plugin",
870
+ description: "Core authentication objects for ObjectStack (User, Session, Account, Verification)"
871
+ };
1408
872
 
1409
873
  // src/auth-plugin.ts
1410
874
  var AuthPlugin = class {
@@ -1435,43 +899,27 @@ var AuthPlugin = class {
1435
899
  });
1436
900
  ctx.registerService("auth", this.authManager);
1437
901
  ctx.getService("manifest").register({
1438
- id: "com.objectstack.system",
1439
- name: "System",
1440
- version: "1.0.0",
1441
- type: "plugin",
1442
- namespace: "sys",
1443
- objects: [
1444
- SysUser,
1445
- SysSession,
1446
- SysAccount,
1447
- SysVerification,
1448
- SysOrganization,
1449
- SysMember,
1450
- SysInvitation,
1451
- SysTeam,
1452
- SysTeamMember,
1453
- SysApiKey,
1454
- SysTwoFactor,
1455
- SysUserPreference
1456
- ]
902
+ ...authPluginManifestHeader,
903
+ objects: authIdentityObjects,
904
+ // The platform Setup App is a static metadata artifact (lives in
905
+ // @objectstack/platform-objects/apps). plugin-auth is the natural
906
+ // owner of its registration since it loads first among the trio
907
+ // (auth + security + audit) that supplies the underlying objects.
908
+ apps: [import_apps.SETUP_APP],
909
+ // Curated list views and dashboards consumed by the Setup App's
910
+ // navigation entries. The manifest service does NOT auto-discover
911
+ // these from the app definition — they must be registered as
912
+ // explicit top-level arrays per ObjectStackDefinitionSchema.
913
+ views: [
914
+ import_apps.UsersView,
915
+ import_apps.OrganizationsView,
916
+ import_apps.RolesView,
917
+ import_apps.SessionsView,
918
+ import_apps.AuditLogsView,
919
+ import_apps.PackageInstallationsView
920
+ ],
921
+ dashboards: [import_apps.SystemOverviewDashboard, import_apps.SecurityOverviewDashboard]
1457
922
  });
1458
- try {
1459
- const setupNav = ctx.getService("setupNav");
1460
- if (setupNav) {
1461
- setupNav.contribute({
1462
- areaId: "area_administration",
1463
- items: [
1464
- { id: "nav_users", type: "object", label: "Users", objectName: "user", icon: "users", order: 10 },
1465
- { id: "nav_organizations", type: "object", label: "Organizations", objectName: "organization", icon: "building-2", order: 20 },
1466
- { id: "nav_teams", type: "object", label: "Teams", objectName: "team", icon: "users-round", order: 30 },
1467
- { id: "nav_api_keys", type: "object", label: "API Keys", objectName: "api_key", icon: "key", order: 40 },
1468
- { id: "nav_sessions", type: "object", label: "Sessions", objectName: "session", icon: "monitor", order: 50 }
1469
- ]
1470
- });
1471
- ctx.logger.info("Auth navigation items contributed to Setup App");
1472
- }
1473
- } catch {
1474
- }
1475
923
  ctx.logger.info("Auth Plugin initialized successfully");
1476
924
  }
1477
925
  async start(ctx) {
@@ -1494,7 +942,8 @@ var AuthPlugin = class {
1494
942
  const configuredUrl = this.options.baseUrl || "http://localhost:3000";
1495
943
  const configuredOrigin = new URL(configuredUrl).origin;
1496
944
  const actualUrl = `http://localhost:${actualPort}`;
1497
- if (configuredOrigin !== actualUrl) {
945
+ const configuredIsLocalhost = configuredOrigin.startsWith("http://localhost");
946
+ if (configuredIsLocalhost && configuredOrigin !== actualUrl) {
1498
947
  this.authManager.setRuntimeBaseUrl(actualUrl);
1499
948
  ctx.logger.info(
1500
949
  `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`
@@ -1552,23 +1001,26 @@ var AuthPlugin = class {
1552
1001
  );
1553
1002
  }
1554
1003
  const rawApp = httpServer.getRawApp();
1555
- rawApp.get(`${basePath}/config`, async (c) => {
1004
+ rawApp.get(`${basePath}/config`, (c) => {
1556
1005
  try {
1557
1006
  const config = this.authManager.getPublicConfig();
1558
- return c.json({
1559
- success: true,
1560
- data: config
1561
- });
1007
+ return c.json({ success: true, data: config });
1562
1008
  } catch (error) {
1563
1009
  const err = error instanceof Error ? error : new Error(String(error));
1564
- ctx.logger.error("Auth config error:", err);
1565
- return c.json({
1566
- success: false,
1567
- error: {
1568
- code: "auth_config_error",
1569
- message: err.message
1570
- }
1571
- }, 500);
1010
+ return c.json({ success: false, error: { code: "auth_config_error", message: err.message } }, 500);
1011
+ }
1012
+ });
1013
+ rawApp.get(`${basePath}/bootstrap-status`, async (c) => {
1014
+ try {
1015
+ const dataEngine = this.authManager.getDataEngine();
1016
+ if (!dataEngine) {
1017
+ return c.json({ hasOwner: true });
1018
+ }
1019
+ const count = await dataEngine.count("sys_user", {});
1020
+ return c.json({ hasOwner: (count ?? 0) > 0 });
1021
+ } catch (error) {
1022
+ ctx.logger.warn("[AuthPlugin] bootstrap-status check failed; assuming bootstrapped", error);
1023
+ return c.json({ hasOwner: true });
1572
1024
  }
1573
1025
  });
1574
1026
  rawApp.all(`${basePath}/*`, async (c) => {
@@ -1598,15 +1050,44 @@ var AuthPlugin = class {
1598
1050
  );
1599
1051
  }
1600
1052
  });
1053
+ if (this.options.plugins?.oidcProvider) {
1054
+ void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {
1055
+ ctx.logger.error("Failed to register OIDC discovery routes", error);
1056
+ });
1057
+ }
1601
1058
  ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);
1602
1059
  }
1060
+ /**
1061
+ * Mount the OIDC / OAuth 2.0 well-known discovery documents at the root
1062
+ * URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the
1063
+ * documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`
1064
+ * relative to the issuer, not under the auth basePath.
1065
+ */
1066
+ async registerOidcDiscoveryRoutes(rawApp, ctx) {
1067
+ const auth = await this.authManager.getAuthInstance();
1068
+ const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import("@better-auth/oauth-provider");
1069
+ const authServerHandler = oauthProviderAuthServerMetadata(auth);
1070
+ const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth);
1071
+ rawApp.get("/.well-known/oauth-authorization-server", (c) => authServerHandler(c.req.raw));
1072
+ rawApp.get("/.well-known/openid-configuration", (c) => openidConfigHandler(c.req.raw));
1073
+ ctx.logger.info(
1074
+ "OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}"
1075
+ );
1076
+ }
1603
1077
  };
1604
1078
  // Annotate the CommonJS export names for ESM import in node:
1605
1079
  0 && (module.exports = {
1606
1080
  AUTH_ACCOUNT_CONFIG,
1081
+ AUTH_DEVICE_CODE_SCHEMA,
1607
1082
  AUTH_INVITATION_SCHEMA,
1083
+ AUTH_JWKS_SCHEMA,
1608
1084
  AUTH_MEMBER_SCHEMA,
1609
1085
  AUTH_MODEL_TO_PROTOCOL,
1086
+ AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
1087
+ AUTH_OAUTH_APPLICATION_SCHEMA,
1088
+ AUTH_OAUTH_CLIENT_SCHEMA,
1089
+ AUTH_OAUTH_CONSENT_SCHEMA,
1090
+ AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
1610
1091
  AUTH_ORGANIZATION_SCHEMA,
1611
1092
  AUTH_ORG_SESSION_FIELDS,
1612
1093
  AUTH_SESSION_CONFIG,
@@ -1616,24 +1097,12 @@ var AuthPlugin = class {
1616
1097
  AUTH_TWO_FACTOR_USER_FIELDS,
1617
1098
  AUTH_USER_CONFIG,
1618
1099
  AUTH_VERIFICATION_CONFIG,
1619
- AuthAccount,
1620
1100
  AuthManager,
1621
1101
  AuthPlugin,
1622
- AuthSession,
1623
- AuthUser,
1624
- AuthVerification,
1625
- SysAccount,
1626
- SysApiKey,
1627
- SysInvitation,
1628
- SysMember,
1629
- SysOrganization,
1630
- SysSession,
1631
- SysTeam,
1632
- SysTeamMember,
1633
- SysTwoFactor,
1634
- SysUser,
1635
- SysUserPreference,
1636
- SysVerification,
1102
+ buildDeviceAuthorizationPluginSchema,
1103
+ buildJwtPluginSchema,
1104
+ buildOauthProviderPluginSchema,
1105
+ buildOidcProviderPluginSchema,
1637
1106
  buildOrganizationPluginSchema,
1638
1107
  buildTwoFactorPluginSchema,
1639
1108
  createObjectQLAdapter,