@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.
- package/dist/auth-realms.d.ts +50 -0
- package/dist/auth-realms.js +13 -0
- package/dist/auth-realms.js.map +1 -0
- package/dist/edge.d.ts +6 -1
- package/dist/edge.js +41 -19
- package/dist/edge.js.map +1 -1
- package/dist/node-runtime.d.ts +44 -2
- package/dist/node-runtime.js +294 -30
- package/dist/node-runtime.js.map +1 -1
- package/dist/runtime-contributor.js +8 -4
- package/dist/runtime-contributor.js.map +1 -1
- package/dist/server.d.ts +30 -1
- package/dist/server.js +127 -81
- package/dist/server.js.map +1 -1
- package/package.json +9 -4
package/dist/node-runtime.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
617
|
-
|
|
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 =
|
|
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 (
|
|
644
|
-
|
|
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 =
|
|
650
|
-
return await betterAuth.handler(
|
|
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 =
|
|
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,
|