@voyant-travel/auth 0.135.0 → 0.136.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-realms.d.ts +31 -1
- package/dist/auth-realms.js.map +1 -1
- package/dist/customer-buyer-accounts.d.ts +134 -0
- package/dist/customer-buyer-accounts.js +177 -0
- package/dist/customer-buyer-accounts.js.map +1 -0
- package/dist/local-member-access.d.ts +2 -0
- package/dist/local-member-access.js +11 -2
- package/dist/local-member-access.js.map +1 -1
- package/dist/node-runtime.d.ts +5 -1
- package/dist/node-runtime.js +135 -8
- package/dist/node-runtime.js.map +1 -1
- package/dist/server.d.ts +97 -1
- package/dist/server.js +66 -12
- package/dist/server.js.map +1 -1
- package/package.json +5 -5
package/dist/node-runtime.js
CHANGED
|
@@ -10,13 +10,16 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { openNodeDatabase } from "@voyant-travel/db/runtime";
|
|
12
12
|
import { authUser, cloudAuthUserLinks, userProfilesTable, } from "@voyant-travel/db/schema/iam";
|
|
13
|
+
import { parseJsonBody, } from "@voyant-travel/hono";
|
|
13
14
|
import { handleApiError, reportException, requestId, } from "@voyant-travel/hono/middleware/error-boundary";
|
|
14
15
|
import { getRequestId } from "@voyant-travel/hono/observability";
|
|
15
16
|
import { accessCatalogScopesForRole, isFullAccessRole } from "@voyant-travel/types/member-roles";
|
|
16
17
|
import { eq, sql } from "drizzle-orm";
|
|
17
18
|
import { Hono } from "hono";
|
|
19
|
+
import { z } from "zod";
|
|
18
20
|
import { createVoyantCloudAdminAuthPlugin, revalidateVoyantCloudAdminAuthSession, revalidateVoyantCloudAdminAuthUser, } from "./cloud-admin-session.js";
|
|
19
21
|
import { buildClearCloudAdminAuthStateCookie, createCloudAdminAuthStart } from "./cloud-broker.js";
|
|
22
|
+
import { createDrizzleCustomerBuyerAccountStore, listCustomerBuyerAccounts, normalizeCustomerBuyerAccountPolicy, repairCustomerPersonalBuyerAccountEntitlement, resolveActiveCustomerBuyerContext, selectCustomerBuyerAccount, } from "./customer-buyer-accounts.js";
|
|
20
23
|
import { createAdminBetterAuth, createCustomerBetterAuth, handleApiTokenManagementRequest, handleOrganizationMembersRequest, } from "./server.js";
|
|
21
24
|
import { ensureCurrentUserProfile } from "./workspace.js";
|
|
22
25
|
function classifyOperatorApiAuthSurface(requestUrl) {
|
|
@@ -292,6 +295,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
292
295
|
facebook: { enabled: false, credentialRef: null },
|
|
293
296
|
apple: { enabled: false, credentialRef: null },
|
|
294
297
|
},
|
|
298
|
+
accountPolicy: normalizeCustomerBuyerAccountPolicy(),
|
|
295
299
|
};
|
|
296
300
|
const raw = env.VOYANT_CUSTOMER_AUTH_CONFIG_JSON?.trim();
|
|
297
301
|
if (!raw)
|
|
@@ -321,11 +325,11 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
321
325
|
facebook: social(methods.facebook),
|
|
322
326
|
apple: social(methods.apple),
|
|
323
327
|
},
|
|
328
|
+
accountPolicy: normalizeCustomerBuyerAccountPolicy(parsed.accountPolicy),
|
|
324
329
|
};
|
|
325
330
|
}
|
|
326
|
-
catch {
|
|
327
|
-
|
|
328
|
-
return fallback;
|
|
331
|
+
catch (error) {
|
|
332
|
+
throw new Error("Invalid VOYANT_CUSTOMER_AUTH_CONFIG_JSON", { cause: error });
|
|
329
333
|
}
|
|
330
334
|
}
|
|
331
335
|
function defaultCustomerAuthContext(env, policy) {
|
|
@@ -373,6 +377,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
373
377
|
: {}),
|
|
374
378
|
},
|
|
375
379
|
},
|
|
380
|
+
accountPolicy: policy.accountPolicy,
|
|
376
381
|
};
|
|
377
382
|
}
|
|
378
383
|
async function resolveCustomerAuthContext(env, request) {
|
|
@@ -423,7 +428,8 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
423
428
|
return parsed.toString().replace(/\/$/, "");
|
|
424
429
|
}
|
|
425
430
|
async function publicCustomerAuthConfiguration(env, request) {
|
|
426
|
-
const
|
|
431
|
+
const context = await resolveCustomerAuthContext(env, request);
|
|
432
|
+
const methods = context.methods;
|
|
427
433
|
const providers = methods.socialProviders ?? {};
|
|
428
434
|
return {
|
|
429
435
|
methods: {
|
|
@@ -433,6 +439,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
433
439
|
facebook: Boolean(providers.facebook),
|
|
434
440
|
apple: Boolean(providers.apple),
|
|
435
441
|
},
|
|
442
|
+
accountPolicy: normalizeCustomerBuyerAccountPolicy(context.accountPolicy),
|
|
436
443
|
};
|
|
437
444
|
}
|
|
438
445
|
function buildAdminBetterAuth(env, db) {
|
|
@@ -492,9 +499,9 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
492
499
|
},
|
|
493
500
|
});
|
|
494
501
|
}
|
|
495
|
-
async function buildCustomerBetterAuth(env, db, request) {
|
|
502
|
+
async function buildCustomerBetterAuth(env, db, request, resolvedContext) {
|
|
496
503
|
const emailSender = runtimeOptions.resolveEmailSender?.(env);
|
|
497
|
-
const context = await resolveCustomerAuthContext(env, request);
|
|
504
|
+
const context = resolvedContext ?? (await resolveCustomerAuthContext(env, request));
|
|
498
505
|
const publicApiBaseURL = context.publicApiBaseURL ?? getPublicApiBaseUrl(env);
|
|
499
506
|
const authDb = db;
|
|
500
507
|
return createCustomerBetterAuth({
|
|
@@ -504,6 +511,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
504
511
|
basePath: "/auth/customer",
|
|
505
512
|
trustedOrigins: context.trustedOrigins,
|
|
506
513
|
methods: withCustomerSocialRedirectUris(context.methods, publicApiBaseURL),
|
|
514
|
+
accountPolicy: context.accountPolicy,
|
|
507
515
|
sendResetPassword: async ({ user, url }) => {
|
|
508
516
|
const publicUrl = withCustomerPublicResetPasswordUrl(url, publicApiBaseURL);
|
|
509
517
|
if (!emailSender) {
|
|
@@ -581,8 +589,11 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
581
589
|
}
|
|
582
590
|
const { db, dispose } = openDatabase(env);
|
|
583
591
|
try {
|
|
592
|
+
const customerContext = customerSurface
|
|
593
|
+
? await resolveCustomerAuthContext(env, request)
|
|
594
|
+
: null;
|
|
584
595
|
const auth = customerSurface
|
|
585
|
-
? await buildCustomerBetterAuth(env, db, request)
|
|
596
|
+
? await buildCustomerBetterAuth(env, db, request, customerContext ?? undefined)
|
|
586
597
|
: buildAdminBetterAuth(env, db);
|
|
587
598
|
const session = await auth.api.getSession({ headers: request.headers });
|
|
588
599
|
if (!session) {
|
|
@@ -609,9 +620,25 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
609
620
|
}
|
|
610
621
|
}
|
|
611
622
|
if (customerSurface) {
|
|
612
|
-
|
|
623
|
+
await repairCustomerPersonalBuyerAccountEntitlement(db, session.user.id);
|
|
624
|
+
const buyerStore = createDrizzleCustomerBuyerAccountStore(db);
|
|
625
|
+
const relationshipPersonId = await buyerStore.getRelationshipPersonId(session.user.id);
|
|
626
|
+
const activeAuthOrganizationId = session.session.activeOrganizationId ?? null;
|
|
627
|
+
const buyer = await resolveActiveCustomerBuyerContext({
|
|
628
|
+
identity: {
|
|
629
|
+
userId: session.user.id,
|
|
630
|
+
name: session.user.name ?? null,
|
|
631
|
+
email: session.user.email ?? null,
|
|
632
|
+
},
|
|
633
|
+
activeAuthOrganizationId,
|
|
634
|
+
policy: customerContext?.accountPolicy,
|
|
635
|
+
store: buyerStore,
|
|
636
|
+
});
|
|
637
|
+
const customerIdentity = {
|
|
613
638
|
userId: session.user.id,
|
|
614
639
|
sessionId: session.session.id,
|
|
640
|
+
// Existing organizationId is staff/workspace context. Customer buyer
|
|
641
|
+
// scoping uses the explicit discriminated buyer fields below.
|
|
615
642
|
organizationId: null,
|
|
616
643
|
callerType: "session",
|
|
617
644
|
actor: "customer",
|
|
@@ -619,6 +646,22 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
619
646
|
scopes: [],
|
|
620
647
|
email: session.user.email ?? null,
|
|
621
648
|
};
|
|
649
|
+
if (!buyer) {
|
|
650
|
+
return {
|
|
651
|
+
...customerIdentity,
|
|
652
|
+
relationshipPersonId,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
...customerIdentity,
|
|
657
|
+
buyerAccountId: buyer.id,
|
|
658
|
+
buyerAccountKind: buyer.kind,
|
|
659
|
+
authOrganizationId: buyer.authOrganizationId,
|
|
660
|
+
relationshipOrganizationId: buyer.relationshipOrganizationId,
|
|
661
|
+
relationshipPersonId,
|
|
662
|
+
buyerMembershipId: buyer.membershipId,
|
|
663
|
+
buyerMembershipRole: buyer.membershipRole,
|
|
664
|
+
};
|
|
622
665
|
}
|
|
623
666
|
return {
|
|
624
667
|
userId: session.user.id,
|
|
@@ -951,8 +994,10 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
951
994
|
}
|
|
952
995
|
});
|
|
953
996
|
auth.get("/auth/customer/config", async (c) => {
|
|
997
|
+
c.header("Cache-Control", "no-store");
|
|
954
998
|
if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
955
999
|
return c.json({
|
|
1000
|
+
disabled: true,
|
|
956
1001
|
methods: {
|
|
957
1002
|
emailCode: false,
|
|
958
1003
|
emailPassword: false,
|
|
@@ -964,6 +1009,88 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
964
1009
|
}
|
|
965
1010
|
return c.json(await publicCustomerAuthConfiguration(c.env, c.req.raw));
|
|
966
1011
|
});
|
|
1012
|
+
const selectBuyerAccountSchema = z.object({ accountId: z.string().min(1) });
|
|
1013
|
+
auth.get("/auth/customer/buyer-accounts", async (c) => {
|
|
1014
|
+
c.header("Cache-Control", "no-store");
|
|
1015
|
+
if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
1016
|
+
return c.json({ error: "Customer auth is disabled" }, 404);
|
|
1017
|
+
}
|
|
1018
|
+
const { db, dispose } = openDatabase(c.env);
|
|
1019
|
+
try {
|
|
1020
|
+
const context = await resolveCustomerAuthContext(c.env, c.req.raw);
|
|
1021
|
+
const betterAuth = await buildCustomerBetterAuth(c.env, db, c.req.raw, context);
|
|
1022
|
+
const session = await betterAuth.api.getSession({ headers: c.req.raw.headers });
|
|
1023
|
+
if (!session)
|
|
1024
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
1025
|
+
await repairCustomerPersonalBuyerAccountEntitlement(db, session.user.id);
|
|
1026
|
+
return c.json(await listCustomerBuyerAccounts({
|
|
1027
|
+
identity: {
|
|
1028
|
+
userId: session.user.id,
|
|
1029
|
+
name: session.user.name ?? null,
|
|
1030
|
+
email: session.user.email ?? null,
|
|
1031
|
+
},
|
|
1032
|
+
activeAuthOrganizationId: session.session.activeOrganizationId ??
|
|
1033
|
+
null,
|
|
1034
|
+
policy: context.accountPolicy,
|
|
1035
|
+
store: createDrizzleCustomerBuyerAccountStore(db),
|
|
1036
|
+
}));
|
|
1037
|
+
}
|
|
1038
|
+
finally {
|
|
1039
|
+
c.executionCtx.waitUntil(dispose());
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
auth.post("/auth/customer/buyer-accounts/active", async (c) => {
|
|
1043
|
+
c.header("Cache-Control", "no-store");
|
|
1044
|
+
if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
1045
|
+
return c.json({ error: "Customer auth is disabled" }, 404);
|
|
1046
|
+
}
|
|
1047
|
+
const body = await parseJsonBody(c, selectBuyerAccountSchema);
|
|
1048
|
+
const { db, dispose } = openDatabase(c.env);
|
|
1049
|
+
try {
|
|
1050
|
+
const context = await resolveCustomerAuthContext(c.env, c.req.raw);
|
|
1051
|
+
const betterAuth = await buildCustomerBetterAuth(c.env, db, c.req.raw, context);
|
|
1052
|
+
const session = await betterAuth.api.getSession({ headers: c.req.raw.headers });
|
|
1053
|
+
if (!session)
|
|
1054
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
1055
|
+
await repairCustomerPersonalBuyerAccountEntitlement(db, session.user.id);
|
|
1056
|
+
const selected = await selectCustomerBuyerAccount({
|
|
1057
|
+
accountId: body.accountId,
|
|
1058
|
+
identity: {
|
|
1059
|
+
userId: session.user.id,
|
|
1060
|
+
name: session.user.name ?? null,
|
|
1061
|
+
email: session.user.email ?? null,
|
|
1062
|
+
},
|
|
1063
|
+
activeAuthOrganizationId: session.session.activeOrganizationId ??
|
|
1064
|
+
null,
|
|
1065
|
+
policy: context.accountPolicy,
|
|
1066
|
+
store: createDrizzleCustomerBuyerAccountStore(db),
|
|
1067
|
+
});
|
|
1068
|
+
if (!selected) {
|
|
1069
|
+
return c.json({ error: "Buyer account is unavailable" }, 403);
|
|
1070
|
+
}
|
|
1071
|
+
const activationUrl = new URL(c.req.url);
|
|
1072
|
+
activationUrl.pathname = "/auth/customer/organization/set-active";
|
|
1073
|
+
activationUrl.search = "";
|
|
1074
|
+
const activationHeaders = new Headers(c.req.raw.headers);
|
|
1075
|
+
activationHeaders.delete("content-length");
|
|
1076
|
+
activationHeaders.set("content-type", "application/json");
|
|
1077
|
+
const activationResponse = await betterAuth.handler(new Request(activationUrl, {
|
|
1078
|
+
method: "POST",
|
|
1079
|
+
headers: activationHeaders,
|
|
1080
|
+
body: JSON.stringify({ organizationId: selected.authOrganizationId }),
|
|
1081
|
+
}));
|
|
1082
|
+
if (!activationResponse.ok)
|
|
1083
|
+
return activationResponse;
|
|
1084
|
+
const headers = new Headers(activationResponse.headers);
|
|
1085
|
+
headers.delete("content-length");
|
|
1086
|
+
headers.set("cache-control", "no-store");
|
|
1087
|
+
headers.set("content-type", "application/json; charset=UTF-8");
|
|
1088
|
+
return new Response(JSON.stringify({ activeAccount: selected }), { status: 200, headers });
|
|
1089
|
+
}
|
|
1090
|
+
finally {
|
|
1091
|
+
c.executionCtx.waitUntil(dispose());
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
967
1094
|
auth.all("/auth/customer/*", async (c) => {
|
|
968
1095
|
if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
969
1096
|
return c.json({ error: "Customer auth is disabled" }, 404);
|