@voyant-travel/auth 0.133.1 → 0.133.2

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.
@@ -17,7 +17,7 @@ import { eq, sql } from "drizzle-orm";
17
17
  import { Hono } from "hono";
18
18
  import { createVoyantCloudAdminAuthPlugin, revalidateVoyantCloudAdminAuthSession, revalidateVoyantCloudAdminAuthUser, } from "./cloud-admin-session.js";
19
19
  import { buildClearCloudAdminAuthStateCookie, createCloudAdminAuthStart } from "./cloud-broker.js";
20
- import { createBetterAuth, handleApiTokenManagementRequest, handleOrganizationMembersRequest, } from "./server.js";
20
+ import { createAdminBetterAuth, createCustomerBetterAuth, handleApiTokenManagementRequest, handleOrganizationMembersRequest, } from "./server.js";
21
21
  import { ensureCurrentUserProfile } from "./workspace.js";
22
22
  /** Resolve Better Auth's standard cross-subdomain cookie policy for Node hosts. */
23
23
  export function buildBetterAuthCookieAdvancedOptions(env) {
@@ -29,6 +29,48 @@ export function buildBetterAuthCookieAdvancedOptions(env) {
29
29
  defaultCookieAttributes: { domain },
30
30
  };
31
31
  }
32
+ /**
33
+ * Better Auth is dispatched internally without the hosting `/api` prefix, but
34
+ * OAuth providers must return to the browser-visible route. Keep explicit
35
+ * merchant overrides and supply the canonical public callback otherwise.
36
+ */
37
+ export function withCustomerSocialRedirectUris(methods, publicApiBaseUrl) {
38
+ const providers = methods.socialProviders;
39
+ if (!providers)
40
+ return methods;
41
+ const normalizedPublicApiBaseUrl = publicApiBaseUrl.replace(/\/+$/, "");
42
+ const callback = (provider) => `${normalizedPublicApiBaseUrl}/auth/customer/callback/${provider}`;
43
+ const withRedirectUri = (provider, redirectURI) => {
44
+ if (!provider || typeof provider !== "object")
45
+ return provider;
46
+ const configured = provider;
47
+ return {
48
+ ...configured,
49
+ redirectURI: configured.redirectURI?.trim() || redirectURI,
50
+ };
51
+ };
52
+ return {
53
+ ...methods,
54
+ socialProviders: {
55
+ ...providers,
56
+ ...(providers.google
57
+ ? {
58
+ google: withRedirectUri(providers.google, callback("google")),
59
+ }
60
+ : {}),
61
+ ...(providers.facebook
62
+ ? {
63
+ facebook: withRedirectUri(providers.facebook, callback("facebook")),
64
+ }
65
+ : {}),
66
+ ...(providers.apple
67
+ ? {
68
+ apple: withRedirectUri(providers.apple, callback("apple")),
69
+ }
70
+ : {}),
71
+ },
72
+ };
73
+ }
32
74
  export function createOperatorAuthNodeRuntime(runtimeOptions) {
33
75
  const openDatabase = runtimeOptions.openDatabase ??
34
76
  ((env) => {
@@ -77,7 +119,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
77
119
  }
78
120
  function isCloudAllowedBetterAuthRoute(request) {
79
121
  const url = new URL(request.url);
80
- const pathname = url.pathname.replace(/\/+$/, "") || "/";
122
+ const pathname = url.pathname.replace("/auth/admin", "/auth").replace(/\/+$/, "") || "/";
81
123
  return CLOUD_BETTER_AUTH_ALLOWLIST.has(pathname);
82
124
  }
83
125
  function localAuthDisabledResponse(c) {
@@ -143,7 +185,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
143
185
  return {
144
186
  cloudAuthStartUrl,
145
187
  deploymentId,
146
- adminCallbackUrl: `${getPublicApiBaseUrl(env)}/auth/cloud/callback`,
188
+ adminCallbackUrl: `${getPublicApiBaseUrl(env)}/auth/admin/cloud/callback`,
147
189
  cookieSecret: env.SESSION_CLAIMS_SECRET,
148
190
  appId: env.VOYANT_CLOUD_APP_ID?.trim() || undefined,
149
191
  environment: env.VOYANT_CLOUD_ENVIRONMENT?.trim() || undefined,
@@ -186,12 +228,151 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
186
228
  function allowAuthSecretLogging(env) {
187
229
  return env.VOYANT_AUTH_LOG_SECRET_FALLBACKS === "1";
188
230
  }
189
- function buildBetterAuth(env, db, options = {}) {
231
+ /**
232
+ * Build a Better Auth instance backed by a caller-provided drizzle
233
+ * client. The caller owns the Pool lifecycle (open before, dispose
234
+ * after) so the same Pool can serve both the auth lookup and any
235
+ * subsequent queries the route does — without spinning up a second
236
+ * connection per request.
237
+ *
238
+ * The deployment decides whether this is a request-scoped or resident Node
239
+ * pool. The Better Auth instance is not cached independently of that owner.
240
+ */
241
+ function requireAdminAuthSecret(env) {
242
+ const secret = env.BETTER_AUTH_ADMIN_SECRET?.trim() || env.BETTER_AUTH_SECRET?.trim();
243
+ if (!secret)
244
+ throw new Error("Admin auth requires BETTER_AUTH_ADMIN_SECRET");
245
+ return secret;
246
+ }
247
+ function requireCustomerAuthSecret(env) {
248
+ const secret = env.BETTER_AUTH_CUSTOMER_SECRET?.trim();
249
+ if (secret)
250
+ return secret;
251
+ // Compatibility bridge for deployments created before realm-specific
252
+ // secrets. The suffix gives the customer cookie an independent signature.
253
+ return `${requireAdminAuthSecret(env)}:voyant-customer-realm`;
254
+ }
255
+ function envEnabled(value, fallback) {
256
+ const normalized = value?.trim().toLowerCase();
257
+ if (!normalized)
258
+ return fallback;
259
+ return normalized === "1" || normalized === "true" || normalized === "yes";
260
+ }
261
+ function parseCustomerAuthPolicy(env) {
262
+ const fallback = {
263
+ methods: {
264
+ emailCode: envEnabled(env.VOYANT_CUSTOMER_AUTH_EMAIL_CODE, true),
265
+ emailPassword: envEnabled(env.VOYANT_CUSTOMER_AUTH_EMAIL_PASSWORD, true),
266
+ google: { enabled: false, credentialRef: null },
267
+ facebook: { enabled: false, credentialRef: null },
268
+ apple: { enabled: false, credentialRef: null },
269
+ },
270
+ };
271
+ const raw = env.VOYANT_CUSTOMER_AUTH_CONFIG_JSON?.trim();
272
+ if (!raw)
273
+ return fallback;
274
+ try {
275
+ const parsed = JSON.parse(raw);
276
+ const methods = parsed.methods ?? {};
277
+ const social = (value) => {
278
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
279
+ return { enabled: false, credentialRef: null };
280
+ }
281
+ const record = value;
282
+ return {
283
+ enabled: record.enabled === true,
284
+ credentialRef: typeof record.credentialRef === "string" && record.credentialRef.trim()
285
+ ? record.credentialRef.trim()
286
+ : null,
287
+ };
288
+ };
289
+ return {
290
+ methods: {
291
+ emailCode: typeof methods.emailCode === "boolean" ? methods.emailCode : fallback.methods.emailCode,
292
+ emailPassword: typeof methods.emailPassword === "boolean"
293
+ ? methods.emailPassword
294
+ : fallback.methods.emailPassword,
295
+ google: social(methods.google),
296
+ facebook: social(methods.facebook),
297
+ apple: social(methods.apple),
298
+ },
299
+ };
300
+ }
301
+ catch {
302
+ console.error("[auth/customer] Invalid VOYANT_CUSTOMER_AUTH_CONFIG_JSON; failing social auth closed");
303
+ return fallback;
304
+ }
305
+ }
306
+ function defaultCustomerAuthContext(env, policy) {
307
+ const hasCanonicalPolicy = Boolean(env.VOYANT_CUSTOMER_AUTH_CONFIG_JSON?.trim());
308
+ const google = Boolean(env.CUSTOMER_AUTH_GOOGLE_CLIENT_ID?.trim() &&
309
+ env.CUSTOMER_AUTH_GOOGLE_CLIENT_SECRET?.trim() &&
310
+ (!hasCanonicalPolicy || policy.methods.google.enabled));
311
+ const facebook = Boolean(env.CUSTOMER_AUTH_FACEBOOK_CLIENT_ID?.trim() &&
312
+ env.CUSTOMER_AUTH_FACEBOOK_CLIENT_SECRET?.trim() &&
313
+ (!hasCanonicalPolicy || policy.methods.facebook.enabled));
314
+ const apple = Boolean(env.CUSTOMER_AUTH_APPLE_CLIENT_ID?.trim() &&
315
+ env.CUSTOMER_AUTH_APPLE_CLIENT_SECRET?.trim() &&
316
+ (!hasCanonicalPolicy || policy.methods.apple.enabled));
317
+ return {
318
+ baseURL: getAuthBaseUrl(env),
319
+ trustedOrigins: getTrustedOrigins(env),
320
+ methods: {
321
+ emailCode: policy.methods.emailCode,
322
+ emailPassword: policy.methods.emailPassword,
323
+ socialProviders: {
324
+ ...(google
325
+ ? {
326
+ google: {
327
+ clientId: env.CUSTOMER_AUTH_GOOGLE_CLIENT_ID.trim(),
328
+ clientSecret: env.CUSTOMER_AUTH_GOOGLE_CLIENT_SECRET.trim(),
329
+ },
330
+ }
331
+ : {}),
332
+ ...(facebook
333
+ ? {
334
+ facebook: {
335
+ clientId: env.CUSTOMER_AUTH_FACEBOOK_CLIENT_ID.trim(),
336
+ clientSecret: env.CUSTOMER_AUTH_FACEBOOK_CLIENT_SECRET.trim(),
337
+ },
338
+ }
339
+ : {}),
340
+ ...(apple
341
+ ? {
342
+ apple: {
343
+ clientId: env.CUSTOMER_AUTH_APPLE_CLIENT_ID.trim(),
344
+ clientSecret: env.CUSTOMER_AUTH_APPLE_CLIENT_SECRET.trim(),
345
+ },
346
+ }
347
+ : {}),
348
+ },
349
+ },
350
+ };
351
+ }
352
+ function resolveCustomerAuthContext(env, request) {
353
+ const policy = parseCustomerAuthPolicy(env);
354
+ return (runtimeOptions.resolveCustomerAuthContext?.(env, policy, request) ??
355
+ defaultCustomerAuthContext(env, policy));
356
+ }
357
+ function publicCustomerAuthConfiguration(env, request) {
358
+ const methods = resolveCustomerAuthContext(env, request).methods;
359
+ const providers = methods.socialProviders ?? {};
360
+ return {
361
+ methods: {
362
+ emailCode: methods.emailCode ?? true,
363
+ emailPassword: methods.emailPassword ?? true,
364
+ google: Boolean(providers.google),
365
+ facebook: Boolean(providers.facebook),
366
+ apple: Boolean(providers.apple),
367
+ },
368
+ };
369
+ }
370
+ function buildAdminBetterAuth(env, db) {
190
371
  const emailSender = runtimeOptions.resolveEmailSender?.(env);
191
372
  const cloudAuthExchange = isVoyantCloudAuthMode(env) ? getCloudAuthExchangeConfig(env) : null;
192
373
  const authDb = db;
193
374
  const cloudAuthDb = db;
194
- return createBetterAuth({
375
+ return createAdminBetterAuth({
195
376
  // `db` is a `NeonDatabase` (neon-serverless WebSocket); the
196
377
  // `CreateBetterAuthOptions.db` type still references the older
197
378
  // `getDb` return union (postgres-js + neon-http). Drizzle's
@@ -199,12 +380,11 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
199
380
  // the cast is structurally safe — better-auth's drizzleAdapter
200
381
  // works on any PgDatabase. See #500 for context.
201
382
  db: authDb,
202
- secret: env.BETTER_AUTH_SECRET,
383
+ secret: requireAdminAuthSecret(env),
203
384
  baseURL: getAuthBaseUrl(env),
204
- basePath: "/auth",
385
+ basePath: "/auth/admin",
205
386
  trustedOrigins: getTrustedOrigins(env),
206
387
  advanced: (runtimeOptions.cookieAdvanced ?? buildBetterAuthCookieAdvancedOptions)(env),
207
- customerSignupSurfaces: options.customerSignup ? ["customer"] : undefined,
208
388
  plugins: cloudAuthExchange
209
389
  ? [
210
390
  createVoyantCloudAdminAuthPlugin({
@@ -242,9 +422,42 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
242
422
  },
243
423
  });
244
424
  }
245
- function rewriteCustomerAuthRequest(request) {
425
+ function buildCustomerBetterAuth(env, db, request) {
426
+ const emailSender = runtimeOptions.resolveEmailSender?.(env);
427
+ const context = resolveCustomerAuthContext(env, request);
428
+ const authDb = db;
429
+ return createCustomerBetterAuth({
430
+ db: authDb,
431
+ secret: requireCustomerAuthSecret(env),
432
+ baseURL: context.baseURL,
433
+ basePath: "/auth/customer",
434
+ trustedOrigins: context.trustedOrigins,
435
+ methods: withCustomerSocialRedirectUris(context.methods, getPublicApiBaseUrl(env)),
436
+ sendResetPassword: async ({ user, url }) => {
437
+ if (!emailSender) {
438
+ if (allowAuthSecretLogging(env)) {
439
+ console.info(`[auth/customer] reset-password (debug fallback) -> ${user.email}: ${url}`);
440
+ return;
441
+ }
442
+ throw new Error("Password reset email provider is not configured");
443
+ }
444
+ await emailSender.sendResetPassword({ user, url });
445
+ },
446
+ sendVerificationOTP: async ({ email, otp, type }) => {
447
+ if (!emailSender) {
448
+ if (allowAuthSecretLogging(env)) {
449
+ console.info(`[auth/customer] verification-otp (debug fallback) [${type}] -> ${email}: ${otp}`);
450
+ return;
451
+ }
452
+ throw new Error("Verification OTP email provider is not configured");
453
+ }
454
+ await emailSender.sendVerificationOtp({ email, otp, type });
455
+ },
456
+ });
457
+ }
458
+ function rewriteLegacyAdminAuthRequest(request) {
246
459
  const url = new URL(request.url);
247
- url.pathname = url.pathname.replace("/auth/customer", "/auth");
460
+ url.pathname = url.pathname.replace("/auth", "/auth/admin");
248
461
  return new Request(url, request);
249
462
  }
250
463
  const FULL_ACCESS_SCOPES = ["*"];
@@ -294,12 +507,15 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
294
507
  async function resolveAuthRequest(request, env) {
295
508
  const { db, dispose } = openDatabase(env);
296
509
  try {
297
- const auth = buildBetterAuth(env, db);
510
+ const customerSurface = new URL(request.url).pathname.includes("/v1/public/");
511
+ const auth = customerSurface
512
+ ? buildCustomerBetterAuth(env, db, request)
513
+ : buildAdminBetterAuth(env, db);
298
514
  const session = await auth.api.getSession({ headers: request.headers });
299
515
  if (!session) {
300
516
  return null;
301
517
  }
302
- if (isVoyantCloudAuthMode(env)) {
518
+ if (!customerSurface && isVoyantCloudAuthMode(env)) {
303
519
  const revalidateConfig = getCloudAuthRevalidateConfig(env);
304
520
  if (!revalidateConfig) {
305
521
  return null;
@@ -319,12 +535,25 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
319
535
  return null;
320
536
  }
321
537
  }
538
+ if (customerSurface) {
539
+ return {
540
+ userId: session.user.id,
541
+ sessionId: session.session.id,
542
+ organizationId: null,
543
+ callerType: "session",
544
+ actor: "customer",
545
+ realm: "customer",
546
+ scopes: [],
547
+ email: session.user.email ?? null,
548
+ };
549
+ }
322
550
  return {
323
551
  userId: session.user.id,
324
552
  sessionId: session.session.id,
325
553
  organizationId: null,
326
554
  callerType: "session",
327
555
  actor: "staff",
556
+ realm: "admin",
328
557
  // Member RBAC scope set (RFC voyant#2085). Defaults to full access until
329
558
  // an admin assigns permissions, so existing deployments are unchanged.
330
559
  // `actor: "staff"` is retained, so actor-gated paths (incl. the
@@ -362,7 +591,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
362
591
  }
363
592
  const { db, dispose } = openDatabase(env);
364
593
  try {
365
- const betterAuth = buildBetterAuth(env, db);
594
+ const betterAuth = buildAdminBetterAuth(env, db);
366
595
  const session = await betterAuth.api.getSession({ headers: request.headers });
367
596
  if (!session) {
368
597
  return null;
@@ -472,7 +701,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
472
701
  // middleware, so own the Pool here.
473
702
  const { db, dispose } = openDatabase(c.env);
474
703
  try {
475
- const betterAuth = buildBetterAuth(c.env, db);
704
+ const betterAuth = buildAdminBetterAuth(c.env, db);
476
705
  const session = await betterAuth.api.getSession({ headers: c.req.raw.headers });
477
706
  if (!session) {
478
707
  return c.json({ userExists: false, authenticated: false });
@@ -500,7 +729,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
500
729
  async function handleApiTokensFacade(c) {
501
730
  const { db, dispose } = openDatabase(c.env);
502
731
  try {
503
- const betterAuth = buildBetterAuth(c.env, db);
732
+ const betterAuth = buildAdminBetterAuth(c.env, db);
504
733
  const cloudAuthDb = db;
505
734
  if (isVoyantCloudAuthMode(c.env)) {
506
735
  const revalidateConfig = getCloudAuthRevalidateConfig(c.env);
@@ -538,7 +767,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
538
767
  async function handleOrganizationMembersFacade(c) {
539
768
  const { db, dispose } = openDatabase(c.env);
540
769
  try {
541
- const betterAuth = buildBetterAuth(c.env, db);
770
+ const betterAuth = buildAdminBetterAuth(c.env, db);
542
771
  const cloudAuthDb = db;
543
772
  if (isVoyantCloudAuthMode(c.env)) {
544
773
  const revalidateConfig = getCloudAuthRevalidateConfig(c.env);
@@ -573,7 +802,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
573
802
  c.executionCtx.waitUntil(dispose());
574
803
  }
575
804
  }
576
- auth.get("/auth/cloud/start", async (c) => {
805
+ async function handleCloudAuthStart(c) {
577
806
  if (!isVoyantCloudAuthMode(c.env)) {
578
807
  return c.json({ error: "Not found" }, 404);
579
808
  }
@@ -599,8 +828,8 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
599
828
  console.error("[auth/cloud/start] Error:", error);
600
829
  return c.json({ error: "Voyant Cloud auth broker is misconfigured" }, 500);
601
830
  }
602
- });
603
- auth.get("/auth/cloud/callback", async (c) => {
831
+ }
832
+ async function handleCloudAuthCallback(c) {
604
833
  if (!isVoyantCloudAuthMode(c.env)) {
605
834
  return c.json({ error: "Not found" }, 404);
606
835
  }
@@ -613,8 +842,11 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
613
842
  }
614
843
  const { db, dispose } = openDatabase(c.env);
615
844
  try {
616
- const betterAuth = buildBetterAuth(c.env, db);
617
- return await betterAuth.handler(c.req.raw);
845
+ const betterAuth = buildAdminBetterAuth(c.env, db);
846
+ const request = c.req.path.startsWith("/auth/admin/")
847
+ ? c.req.raw
848
+ : rewriteLegacyAdminAuthRequest(c.req.raw);
849
+ return await betterAuth.handler(request);
618
850
  }
619
851
  catch (error) {
620
852
  console.error("[auth/cloud/callback] Error:", error);
@@ -623,7 +855,12 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
623
855
  finally {
624
856
  c.executionCtx.waitUntil(dispose());
625
857
  }
626
- });
858
+ }
859
+ auth.get("/auth/admin/cloud/start", handleCloudAuthStart);
860
+ auth.get("/auth/admin/cloud/callback", handleCloudAuthCallback);
861
+ /** @deprecated Use /auth/admin/cloud/*. */
862
+ auth.get("/auth/cloud/start", handleCloudAuthStart);
863
+ auth.get("/auth/cloud/callback", handleCloudAuthCallback);
627
864
  auth.all("/auth/api-tokens", handleApiTokensFacade);
628
865
  auth.all("/auth/api-tokens/:keyId", handleApiTokensFacade);
629
866
  auth.all("/auth/api-tokens/:keyId/rotate", handleApiTokensFacade);
@@ -631,7 +868,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
631
868
  auth.get("/auth/customer/status", async (c) => {
632
869
  const { db, dispose } = openDatabase(c.env);
633
870
  try {
634
- const betterAuth = buildBetterAuth(c.env, db, { customerSignup: true });
871
+ const betterAuth = buildCustomerBetterAuth(c.env, db, c.req.raw);
635
872
  const session = await betterAuth.api.getSession({ headers: c.req.raw.headers });
636
873
  return c.json({ authenticated: Boolean(session) });
637
874
  }
@@ -639,15 +876,28 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
639
876
  c.executionCtx.waitUntil(dispose());
640
877
  }
641
878
  });
879
+ auth.get("/auth/customer/config", (c) => {
880
+ if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
881
+ return c.json({
882
+ methods: {
883
+ emailCode: false,
884
+ emailPassword: false,
885
+ google: false,
886
+ facebook: false,
887
+ apple: false,
888
+ },
889
+ });
890
+ }
891
+ return c.json(publicCustomerAuthConfiguration(c.env, c.req.raw));
892
+ });
642
893
  auth.all("/auth/customer/*", async (c) => {
643
- if (isVoyantCloudAuthMode(c.env) &&
644
- !isCloudAllowedBetterAuthRoute(rewriteCustomerAuthRequest(c.req.raw))) {
645
- return localAuthDisabledResponse(c);
894
+ if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
895
+ return c.json({ error: "Customer auth is disabled" }, 404);
646
896
  }
647
897
  const { db, dispose } = openDatabase(c.env);
648
898
  try {
649
- const betterAuth = buildBetterAuth(c.env, db, { customerSignup: true });
650
- return await betterAuth.handler(rewriteCustomerAuthRequest(c.req.raw));
899
+ const betterAuth = buildCustomerBetterAuth(c.env, db, c.req.raw);
900
+ return await betterAuth.handler(c.req.raw);
651
901
  }
652
902
  finally {
653
903
  c.executionCtx.waitUntil(dispose());
@@ -660,19 +910,33 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
660
910
  * Sign-up is gated by a `user.create.before` hook in @voyant-travel/auth — once a
661
911
  * user exists, the hook throws and BA returns an error to the client.
662
912
  */
663
- auth.all("/auth/*", async (c) => {
913
+ auth.all("/auth/admin/*", async (c) => {
664
914
  if (isVoyantCloudAuthMode(c.env) && !isCloudAllowedBetterAuthRoute(c.req.raw)) {
665
915
  return localAuthDisabledResponse(c);
666
916
  }
667
917
  const { db, dispose } = openDatabase(c.env);
668
918
  try {
669
- const betterAuth = buildBetterAuth(c.env, db);
919
+ const betterAuth = buildAdminBetterAuth(c.env, db);
670
920
  return await betterAuth.handler(c.req.raw);
671
921
  }
672
922
  finally {
673
923
  c.executionCtx.waitUntil(dispose());
674
924
  }
675
925
  });
926
+ /** @deprecated Admin Better Auth routes moved to /auth/admin/*. */
927
+ auth.all("/auth/*", async (c) => {
928
+ const rewritten = rewriteLegacyAdminAuthRequest(c.req.raw);
929
+ if (isVoyantCloudAuthMode(c.env) && !isCloudAllowedBetterAuthRoute(rewritten)) {
930
+ return localAuthDisabledResponse(c);
931
+ }
932
+ const { db, dispose } = openDatabase(c.env);
933
+ try {
934
+ return await buildAdminBetterAuth(c.env, db).handler(rewritten);
935
+ }
936
+ finally {
937
+ c.executionCtx.waitUntil(dispose());
938
+ }
939
+ });
676
940
  return {
677
941
  handler: auth,
678
942
  getBootstrapStatusForRequest,