@objectstack/plugin-auth 3.2.5 → 3.2.7

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.
@@ -410,4 +410,349 @@ describe('AuthManager', () => {
410
410
  );
411
411
  });
412
412
  });
413
+
414
+ describe('trustedOrigins passthrough', () => {
415
+ it('should forward trustedOrigins to betterAuth when provided', () => {
416
+ let capturedConfig: any;
417
+ (betterAuth as any).mockImplementation((config: any) => {
418
+ capturedConfig = config;
419
+ return { handler: vi.fn(), api: {} };
420
+ });
421
+
422
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
423
+ const manager = new AuthManager({
424
+ secret: 'test-secret-at-least-32-chars-long',
425
+ baseUrl: 'http://localhost:3000',
426
+ trustedOrigins: ['https://*.objectos.app', 'http://localhost:*'],
427
+ });
428
+ manager.getAuthInstance();
429
+ warnSpy.mockRestore();
430
+
431
+ expect(capturedConfig.trustedOrigins).toEqual([
432
+ 'https://*.objectos.app',
433
+ 'http://localhost:*',
434
+ ]);
435
+ });
436
+
437
+ it('should NOT include trustedOrigins key when not provided', () => {
438
+ let capturedConfig: any;
439
+ (betterAuth as any).mockImplementation((config: any) => {
440
+ capturedConfig = config;
441
+ return { handler: vi.fn(), api: {} };
442
+ });
443
+
444
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
445
+ const manager = new AuthManager({
446
+ secret: 'test-secret-at-least-32-chars-long',
447
+ baseUrl: 'http://localhost:3000',
448
+ });
449
+ manager.getAuthInstance();
450
+ warnSpy.mockRestore();
451
+
452
+ expect(capturedConfig).not.toHaveProperty('trustedOrigins');
453
+ });
454
+
455
+ it('should NOT include trustedOrigins key when array is empty', () => {
456
+ let capturedConfig: any;
457
+ (betterAuth as any).mockImplementation((config: any) => {
458
+ capturedConfig = config;
459
+ return { handler: vi.fn(), api: {} };
460
+ });
461
+
462
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
463
+ const manager = new AuthManager({
464
+ secret: 'test-secret-at-least-32-chars-long',
465
+ baseUrl: 'http://localhost:3000',
466
+ trustedOrigins: [],
467
+ });
468
+ manager.getAuthInstance();
469
+ warnSpy.mockRestore();
470
+
471
+ expect(capturedConfig).not.toHaveProperty('trustedOrigins');
472
+ });
473
+ });
474
+
475
+ describe('setRuntimeBaseUrl', () => {
476
+ it('should update baseURL before auth instance is created', () => {
477
+ let capturedConfig: any;
478
+ (betterAuth as any).mockImplementation((config: any) => {
479
+ capturedConfig = config;
480
+ return { handler: vi.fn(), api: {} };
481
+ });
482
+
483
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
484
+ const manager = new AuthManager({
485
+ secret: 'test-secret-at-least-32-chars-long',
486
+ baseUrl: 'http://localhost:3000',
487
+ });
488
+
489
+ manager.setRuntimeBaseUrl('http://localhost:3002');
490
+ manager.getAuthInstance();
491
+ warnSpy.mockRestore();
492
+
493
+ expect(capturedConfig.baseURL).toBe('http://localhost:3002');
494
+ });
495
+
496
+ it('should be a no-op and warn when called after auth instance is created', () => {
497
+ let capturedConfig: any;
498
+ (betterAuth as any).mockImplementation((config: any) => {
499
+ capturedConfig = config;
500
+ return { handler: vi.fn(), api: {} };
501
+ });
502
+
503
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
504
+ const manager = new AuthManager({
505
+ secret: 'test-secret-at-least-32-chars-long',
506
+ baseUrl: 'http://localhost:3000',
507
+ });
508
+
509
+ // Force auth instance creation
510
+ manager.getAuthInstance();
511
+ expect(capturedConfig.baseURL).toBe('http://localhost:3000');
512
+
513
+ // Now try to change — should warn and not affect the already-created instance
514
+ manager.setRuntimeBaseUrl('http://localhost:4000');
515
+
516
+ expect(warnSpy).toHaveBeenCalledWith(
517
+ expect.stringContaining('setRuntimeBaseUrl() called after the auth instance was already created'),
518
+ );
519
+ warnSpy.mockRestore();
520
+ });
521
+
522
+ it('should override the default fallback (localhost:3000) when no baseUrl was configured', () => {
523
+ let capturedConfig: any;
524
+ (betterAuth as any).mockImplementation((config: any) => {
525
+ capturedConfig = config;
526
+ return { handler: vi.fn(), api: {} };
527
+ });
528
+
529
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
530
+ const manager = new AuthManager({
531
+ secret: 'test-secret-at-least-32-chars-long',
532
+ });
533
+
534
+ manager.setRuntimeBaseUrl('http://localhost:3002');
535
+ manager.getAuthInstance();
536
+ warnSpy.mockRestore();
537
+
538
+ expect(capturedConfig.baseURL).toBe('http://localhost:3002');
539
+ });
540
+ });
541
+
542
+ describe('socialProviders passthrough', () => {
543
+ it('should forward socialProviders to betterAuth when provided', () => {
544
+ let capturedConfig: any;
545
+ (betterAuth as any).mockImplementation((config: any) => {
546
+ capturedConfig = config;
547
+ return { handler: vi.fn(), api: {} };
548
+ });
549
+
550
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
551
+ const manager = new AuthManager({
552
+ secret: 'test-secret-at-least-32-chars-long',
553
+ baseUrl: 'http://localhost:3000',
554
+ socialProviders: {
555
+ google: { clientId: 'gid', clientSecret: 'gsecret' },
556
+ github: { clientId: 'ghid', clientSecret: 'ghsecret' },
557
+ },
558
+ });
559
+ manager.getAuthInstance();
560
+ warnSpy.mockRestore();
561
+
562
+ expect(capturedConfig.socialProviders).toEqual({
563
+ google: { clientId: 'gid', clientSecret: 'gsecret' },
564
+ github: { clientId: 'ghid', clientSecret: 'ghsecret' },
565
+ });
566
+ });
567
+
568
+ it('should NOT include socialProviders when not provided', () => {
569
+ let capturedConfig: any;
570
+ (betterAuth as any).mockImplementation((config: any) => {
571
+ capturedConfig = config;
572
+ return { handler: vi.fn(), api: {} };
573
+ });
574
+
575
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
576
+ const manager = new AuthManager({
577
+ secret: 'test-secret-at-least-32-chars-long',
578
+ baseUrl: 'http://localhost:3000',
579
+ });
580
+ manager.getAuthInstance();
581
+ warnSpy.mockRestore();
582
+
583
+ expect(capturedConfig).not.toHaveProperty('socialProviders');
584
+ });
585
+ });
586
+
587
+ describe('emailAndPassword passthrough', () => {
588
+ it('should default emailAndPassword to enabled: true', () => {
589
+ let capturedConfig: any;
590
+ (betterAuth as any).mockImplementation((config: any) => {
591
+ capturedConfig = config;
592
+ return { handler: vi.fn(), api: {} };
593
+ });
594
+
595
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
596
+ const manager = new AuthManager({
597
+ secret: 'test-secret-at-least-32-chars-long',
598
+ baseUrl: 'http://localhost:3000',
599
+ });
600
+ manager.getAuthInstance();
601
+ warnSpy.mockRestore();
602
+
603
+ expect(capturedConfig.emailAndPassword.enabled).toBe(true);
604
+ });
605
+
606
+ it('should forward extended emailAndPassword options', () => {
607
+ let capturedConfig: any;
608
+ (betterAuth as any).mockImplementation((config: any) => {
609
+ capturedConfig = config;
610
+ return { handler: vi.fn(), api: {} };
611
+ });
612
+
613
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
614
+ const manager = new AuthManager({
615
+ secret: 'test-secret-at-least-32-chars-long',
616
+ baseUrl: 'http://localhost:3000',
617
+ emailAndPassword: {
618
+ enabled: true,
619
+ minPasswordLength: 12,
620
+ maxPasswordLength: 64,
621
+ requireEmailVerification: true,
622
+ autoSignIn: false,
623
+ revokeSessionsOnPasswordReset: true,
624
+ },
625
+ });
626
+ manager.getAuthInstance();
627
+ warnSpy.mockRestore();
628
+
629
+ expect(capturedConfig.emailAndPassword).toEqual({
630
+ enabled: true,
631
+ minPasswordLength: 12,
632
+ maxPasswordLength: 64,
633
+ requireEmailVerification: true,
634
+ autoSignIn: false,
635
+ revokeSessionsOnPasswordReset: true,
636
+ });
637
+ });
638
+ });
639
+
640
+ describe('emailVerification passthrough', () => {
641
+ it('should forward emailVerification when provided', () => {
642
+ let capturedConfig: any;
643
+ (betterAuth as any).mockImplementation((config: any) => {
644
+ capturedConfig = config;
645
+ return { handler: vi.fn(), api: {} };
646
+ });
647
+
648
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
649
+ const manager = new AuthManager({
650
+ secret: 'test-secret-at-least-32-chars-long',
651
+ baseUrl: 'http://localhost:3000',
652
+ emailVerification: {
653
+ sendOnSignUp: true,
654
+ expiresIn: 1800,
655
+ },
656
+ });
657
+ manager.getAuthInstance();
658
+ warnSpy.mockRestore();
659
+
660
+ expect(capturedConfig.emailVerification).toEqual({
661
+ sendOnSignUp: true,
662
+ expiresIn: 1800,
663
+ });
664
+ });
665
+
666
+ it('should NOT include emailVerification when not provided', () => {
667
+ let capturedConfig: any;
668
+ (betterAuth as any).mockImplementation((config: any) => {
669
+ capturedConfig = config;
670
+ return { handler: vi.fn(), api: {} };
671
+ });
672
+
673
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
674
+ const manager = new AuthManager({
675
+ secret: 'test-secret-at-least-32-chars-long',
676
+ baseUrl: 'http://localhost:3000',
677
+ });
678
+ manager.getAuthInstance();
679
+ warnSpy.mockRestore();
680
+
681
+ expect(capturedConfig).not.toHaveProperty('emailVerification');
682
+ });
683
+ });
684
+
685
+ describe('advanced options passthrough', () => {
686
+ it('should forward crossSubDomainCookies when provided', () => {
687
+ let capturedConfig: any;
688
+ (betterAuth as any).mockImplementation((config: any) => {
689
+ capturedConfig = config;
690
+ return { handler: vi.fn(), api: {} };
691
+ });
692
+
693
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
694
+ const manager = new AuthManager({
695
+ secret: 'test-secret-at-least-32-chars-long',
696
+ baseUrl: 'http://localhost:3000',
697
+ advanced: {
698
+ crossSubDomainCookies: {
699
+ enabled: true,
700
+ domain: '.objectos.app',
701
+ },
702
+ useSecureCookies: true,
703
+ },
704
+ });
705
+ manager.getAuthInstance();
706
+ warnSpy.mockRestore();
707
+
708
+ expect(capturedConfig.advanced).toEqual({
709
+ crossSubDomainCookies: {
710
+ enabled: true,
711
+ domain: '.objectos.app',
712
+ },
713
+ useSecureCookies: true,
714
+ });
715
+ });
716
+
717
+ it('should forward cookiePrefix and disableCSRFCheck', () => {
718
+ let capturedConfig: any;
719
+ (betterAuth as any).mockImplementation((config: any) => {
720
+ capturedConfig = config;
721
+ return { handler: vi.fn(), api: {} };
722
+ });
723
+
724
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
725
+ const manager = new AuthManager({
726
+ secret: 'test-secret-at-least-32-chars-long',
727
+ baseUrl: 'http://localhost:3000',
728
+ advanced: {
729
+ disableCSRFCheck: true,
730
+ cookiePrefix: 'objectos',
731
+ },
732
+ });
733
+ manager.getAuthInstance();
734
+ warnSpy.mockRestore();
735
+
736
+ expect(capturedConfig.advanced.disableCSRFCheck).toBe(true);
737
+ expect(capturedConfig.advanced.cookiePrefix).toBe('objectos');
738
+ });
739
+
740
+ it('should NOT include advanced when not provided', () => {
741
+ let capturedConfig: any;
742
+ (betterAuth as any).mockImplementation((config: any) => {
743
+ capturedConfig = config;
744
+ return { handler: vi.fn(), api: {} };
745
+ });
746
+
747
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
748
+ const manager = new AuthManager({
749
+ secret: 'test-secret-at-least-32-chars-long',
750
+ baseUrl: 'http://localhost:3000',
751
+ });
752
+ manager.getAuthInstance();
753
+ warnSpy.mockRestore();
754
+
755
+ expect(capturedConfig).not.toHaveProperty('advanced');
756
+ });
757
+ });
413
758
  });
@@ -106,11 +106,42 @@ export class AuthManager {
106
106
  ...AUTH_VERIFICATION_CONFIG,
107
107
  },
108
108
 
109
- // Email configuration
109
+ // Social / OAuth providers
110
+ ...(this.config.socialProviders ? { socialProviders: this.config.socialProviders as any } : {}),
111
+
112
+ // Email and password configuration
110
113
  emailAndPassword: {
111
- enabled: true,
114
+ enabled: this.config.emailAndPassword?.enabled ?? true,
115
+ ...(this.config.emailAndPassword?.disableSignUp != null
116
+ ? { disableSignUp: this.config.emailAndPassword.disableSignUp } : {}),
117
+ ...(this.config.emailAndPassword?.requireEmailVerification != null
118
+ ? { requireEmailVerification: this.config.emailAndPassword.requireEmailVerification } : {}),
119
+ ...(this.config.emailAndPassword?.minPasswordLength != null
120
+ ? { minPasswordLength: this.config.emailAndPassword.minPasswordLength } : {}),
121
+ ...(this.config.emailAndPassword?.maxPasswordLength != null
122
+ ? { maxPasswordLength: this.config.emailAndPassword.maxPasswordLength } : {}),
123
+ ...(this.config.emailAndPassword?.resetPasswordTokenExpiresIn != null
124
+ ? { resetPasswordTokenExpiresIn: this.config.emailAndPassword.resetPasswordTokenExpiresIn } : {}),
125
+ ...(this.config.emailAndPassword?.autoSignIn != null
126
+ ? { autoSignIn: this.config.emailAndPassword.autoSignIn } : {}),
127
+ ...(this.config.emailAndPassword?.revokeSessionsOnPasswordReset != null
128
+ ? { revokeSessionsOnPasswordReset: this.config.emailAndPassword.revokeSessionsOnPasswordReset } : {}),
112
129
  },
113
130
 
131
+ // Email verification
132
+ ...(this.config.emailVerification ? {
133
+ emailVerification: {
134
+ ...(this.config.emailVerification.sendOnSignUp != null
135
+ ? { sendOnSignUp: this.config.emailVerification.sendOnSignUp } : {}),
136
+ ...(this.config.emailVerification.sendOnSignIn != null
137
+ ? { sendOnSignIn: this.config.emailVerification.sendOnSignIn } : {}),
138
+ ...(this.config.emailVerification.autoSignInAfterVerification != null
139
+ ? { autoSignInAfterVerification: this.config.emailVerification.autoSignInAfterVerification } : {}),
140
+ ...(this.config.emailVerification.expiresIn != null
141
+ ? { expiresIn: this.config.emailVerification.expiresIn } : {}),
142
+ },
143
+ } : {}),
144
+
114
145
  // Session configuration
115
146
  session: {
116
147
  ...AUTH_SESSION_CONFIG,
@@ -120,6 +151,23 @@ export class AuthManager {
120
151
 
121
152
  // better-auth plugins — registered based on AuthPluginConfig flags
122
153
  plugins: this.buildPluginList(),
154
+
155
+ // Trusted origins for CSRF protection (supports wildcards like "https://*.example.com")
156
+ ...(this.config.trustedOrigins?.length ? { trustedOrigins: this.config.trustedOrigins } : {}),
157
+
158
+ // Advanced options (cross-subdomain cookies, secure cookies, CSRF, etc.)
159
+ ...(this.config.advanced ? {
160
+ advanced: {
161
+ ...(this.config.advanced.crossSubDomainCookies
162
+ ? { crossSubDomainCookies: this.config.advanced.crossSubDomainCookies } : {}),
163
+ ...(this.config.advanced.useSecureCookies != null
164
+ ? { useSecureCookies: this.config.advanced.useSecureCookies } : {}),
165
+ ...(this.config.advanced.disableCSRFCheck != null
166
+ ? { disableCSRFCheck: this.config.advanced.disableCSRFCheck } : {}),
167
+ ...(this.config.advanced.cookiePrefix != null
168
+ ? { cookiePrefix: this.config.advanced.cookiePrefix } : {}),
169
+ },
170
+ } : {}),
123
171
  };
124
172
 
125
173
  return betterAuth(betterAuthConfig);
@@ -224,6 +272,27 @@ export class AuthManager {
224
272
  return envSecret;
225
273
  }
226
274
 
275
+ /**
276
+ * Update the base URL at runtime.
277
+ *
278
+ * This **must** be called before the first request triggers lazy
279
+ * initialisation of the better-auth instance — typically from a
280
+ * `kernel:ready` hook where the actual server port is known.
281
+ *
282
+ * If the auth instance has already been created this is a no-op and
283
+ * a warning is emitted.
284
+ */
285
+ setRuntimeBaseUrl(url: string): void {
286
+ if (this.auth) {
287
+ console.warn(
288
+ '[AuthManager] setRuntimeBaseUrl() called after the auth instance was already created — ignoring. ' +
289
+ 'Ensure this method is called before the first request.',
290
+ );
291
+ return;
292
+ }
293
+ this.config = { ...this.config, baseUrl: url };
294
+ }
295
+
227
296
  /**
228
297
  * Get the underlying better-auth instance
229
298
  * Useful for advanced use cases
@@ -265,6 +265,95 @@ describe('AuthPlugin', () => {
265
265
  // Should NOT throw
266
266
  });
267
267
 
268
+ it('should auto-detect baseUrl from http-server port when port differs', async () => {
269
+ const mockRawApp = { all: vi.fn() };
270
+ const mockHttpServer = {
271
+ post: vi.fn(), get: vi.fn(), put: vi.fn(), delete: vi.fn(),
272
+ patch: vi.fn(), use: vi.fn(),
273
+ getRawApp: vi.fn(() => mockRawApp),
274
+ getPort: vi.fn(() => 3002),
275
+ };
276
+
277
+ mockContext.getService = vi.fn((name: string) => {
278
+ if (name === 'http-server') return mockHttpServer;
279
+ throw new Error(`Service not found: ${name}`);
280
+ });
281
+
282
+ // AuthPlugin configured with default port 3000, but server will be on 3002
283
+ const registeredAuthManager = (mockContext.registerService as any).mock.calls[0][1];
284
+ const setRuntimeSpy = vi.spyOn(registeredAuthManager, 'setRuntimeBaseUrl');
285
+
286
+ await authPlugin.start(mockContext);
287
+ await hookCapture.trigger('kernel:ready');
288
+
289
+ expect(setRuntimeSpy).toHaveBeenCalledWith('http://localhost:3002');
290
+ expect(mockContext.logger.info).toHaveBeenCalledWith(
291
+ expect.stringContaining('Auth baseUrl auto-updated to http://localhost:3002'),
292
+ );
293
+ });
294
+
295
+ it('should NOT update baseUrl when port matches configured value', async () => {
296
+ const localHookCapture = createHookCapture();
297
+ const localPlugin = new AuthPlugin({
298
+ secret: 'test-secret-at-least-32-chars-long',
299
+ baseUrl: 'http://localhost:3000',
300
+ });
301
+ mockContext.hook = localHookCapture.hookFn;
302
+ await localPlugin.init(mockContext);
303
+
304
+ const mockRawApp = { all: vi.fn() };
305
+ const mockHttpServer = {
306
+ post: vi.fn(), get: vi.fn(), put: vi.fn(), delete: vi.fn(),
307
+ patch: vi.fn(), use: vi.fn(),
308
+ getRawApp: vi.fn(() => mockRawApp),
309
+ getPort: vi.fn(() => 3000),
310
+ };
311
+
312
+ mockContext.getService = vi.fn((name: string) => {
313
+ if (name === 'http-server') return mockHttpServer;
314
+ throw new Error(`Service not found: ${name}`);
315
+ });
316
+
317
+ const registeredAuthManager = (mockContext.registerService as any).mock.calls.at(-1)[1];
318
+ const setRuntimeSpy = vi.spyOn(registeredAuthManager, 'setRuntimeBaseUrl');
319
+
320
+ await localPlugin.start(mockContext);
321
+ await localHookCapture.trigger('kernel:ready');
322
+
323
+ expect(setRuntimeSpy).not.toHaveBeenCalled();
324
+ });
325
+
326
+ it('should auto-detect baseUrl when no baseUrl configured (uses default fallback)', async () => {
327
+ const localHookCapture = createHookCapture();
328
+ // No baseUrl — defaults to http://localhost:3000 internally
329
+ const localPlugin = new AuthPlugin({
330
+ secret: 'test-secret-at-least-32-chars-long',
331
+ });
332
+ mockContext.hook = localHookCapture.hookFn;
333
+ await localPlugin.init(mockContext);
334
+
335
+ const mockRawApp = { all: vi.fn() };
336
+ const mockHttpServer = {
337
+ post: vi.fn(), get: vi.fn(), put: vi.fn(), delete: vi.fn(),
338
+ patch: vi.fn(), use: vi.fn(),
339
+ getRawApp: vi.fn(() => mockRawApp),
340
+ getPort: vi.fn(() => 3002),
341
+ };
342
+
343
+ mockContext.getService = vi.fn((name: string) => {
344
+ if (name === 'http-server') return mockHttpServer;
345
+ throw new Error(`Service not found: ${name}`);
346
+ });
347
+
348
+ const registeredAuthManager = (mockContext.registerService as any).mock.calls.at(-1)[1];
349
+ const setRuntimeSpy = vi.spyOn(registeredAuthManager, 'setRuntimeBaseUrl');
350
+
351
+ await localPlugin.start(mockContext);
352
+ await localHookCapture.trigger('kernel:ready');
353
+
354
+ expect(setRuntimeSpy).toHaveBeenCalledWith('http://localhost:3002');
355
+ });
356
+
268
357
  it('should throw error if auth not initialized', async () => {
269
358
  const uninitializedPlugin = new AuthPlugin({
270
359
  secret: 'test-secret',
@@ -114,6 +114,27 @@ export class AuthPlugin implements Plugin {
114
114
  }
115
115
 
116
116
  if (httpServer) {
117
+ // Auto-detect the actual server URL when no explicit baseUrl was
118
+ // configured, or when the configured baseUrl uses a different port
119
+ // than the running server (e.g. port 3000 configured but 3002 bound).
120
+ // getPort() is optional on IHttpServer; duck-type check for it.
121
+ const serverWithPort = httpServer as IHttpServer & { getPort?: () => number };
122
+ if (this.authManager && typeof serverWithPort.getPort === 'function') {
123
+ const actualPort = serverWithPort.getPort();
124
+ if (actualPort) {
125
+ const configuredUrl = this.options.baseUrl || 'http://localhost:3000';
126
+ const configuredOrigin = new URL(configuredUrl).origin;
127
+ const actualUrl = `http://localhost:${actualPort}`;
128
+
129
+ if (configuredOrigin !== actualUrl) {
130
+ this.authManager.setRuntimeBaseUrl(actualUrl);
131
+ ctx.logger.info(
132
+ `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`,
133
+ );
134
+ }
135
+ }
136
+ }
137
+
117
138
  // Route registration errors should propagate (server misconfiguration)
118
139
  this.registerAuthRoutes(httpServer, ctx);
119
140
  ctx.logger.info(`Auth routes registered at ${this.options.basePath}`);
@@ -157,7 +157,7 @@ export const AUTH_VERIFICATION_CONFIG = {
157
157
  * | updatedAt | updated_at |
158
158
  */
159
159
  export const AUTH_ORGANIZATION_SCHEMA = {
160
- modelName: 'sys_organization',
160
+ modelName: SystemObjectName.ORGANIZATION, // 'sys_organization'
161
161
  fields: {
162
162
  createdAt: 'created_at',
163
163
  updatedAt: 'updated_at',
@@ -178,7 +178,7 @@ export const AUTH_ORGANIZATION_SCHEMA = {
178
178
  * | createdAt | created_at |
179
179
  */
180
180
  export const AUTH_MEMBER_SCHEMA = {
181
- modelName: 'sys_member',
181
+ modelName: SystemObjectName.MEMBER, // 'sys_member'
182
182
  fields: {
183
183
  organizationId: 'organization_id',
184
184
  userId: 'user_id',
@@ -202,7 +202,7 @@ export const AUTH_MEMBER_SCHEMA = {
202
202
  * | teamId | team_id |
203
203
  */
204
204
  export const AUTH_INVITATION_SCHEMA = {
205
- modelName: 'sys_invitation',
205
+ modelName: SystemObjectName.INVITATION, // 'sys_invitation'
206
206
  fields: {
207
207
  organizationId: 'organization_id',
208
208
  inviterId: 'inviter_id',
@@ -240,7 +240,7 @@ export const AUTH_ORG_SESSION_FIELDS = {
240
240
  * | updatedAt | updated_at |
241
241
  */
242
242
  export const AUTH_TEAM_SCHEMA = {
243
- modelName: 'sys_team',
243
+ modelName: SystemObjectName.TEAM, // 'sys_team'
244
244
  fields: {
245
245
  organizationId: 'organization_id',
246
246
  createdAt: 'created_at',
@@ -262,7 +262,7 @@ export const AUTH_TEAM_SCHEMA = {
262
262
  * | createdAt | created_at |
263
263
  */
264
264
  export const AUTH_TEAM_MEMBER_SCHEMA = {
265
- modelName: 'sys_team_member',
265
+ modelName: SystemObjectName.TEAM_MEMBER, // 'sys_team_member'
266
266
  fields: {
267
267
  teamId: 'team_id',
268
268
  userId: 'user_id',
@@ -283,7 +283,7 @@ export const AUTH_TEAM_MEMBER_SCHEMA = {
283
283
  * | userId | user_id |
284
284
  */
285
285
  export const AUTH_TWO_FACTOR_SCHEMA = {
286
- modelName: 'sys_two_factor',
286
+ modelName: SystemObjectName.TWO_FACTOR, // 'sys_two_factor'
287
287
  fields: {
288
288
  backupCodes: 'backup_codes',
289
289
  userId: 'user_id',