@voyant-travel/auth 0.136.0 → 0.138.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.
Files changed (50) hide show
  1. package/dist/customer-business-accounts-contracts.d.ts +159 -0
  2. package/dist/customer-business-accounts-contracts.js +112 -0
  3. package/dist/customer-business-accounts-contracts.js.map +1 -0
  4. package/dist/customer-business-onboarding-graph-runtime.d.ts +13 -0
  5. package/dist/customer-business-onboarding-graph-runtime.js +8 -0
  6. package/dist/customer-business-onboarding-graph-runtime.js.map +1 -0
  7. package/dist/customer-business-onboarding-routes.d.ts +13 -0
  8. package/dist/customer-business-onboarding-routes.js +151 -0
  9. package/dist/customer-business-onboarding-routes.js.map +1 -0
  10. package/dist/customer-business-onboarding-runtime-port.d.ts +40 -0
  11. package/dist/customer-business-onboarding-runtime-port.js +25 -0
  12. package/dist/customer-business-onboarding-runtime-port.js.map +1 -0
  13. package/dist/customer-business-onboarding-service.d.ts +32 -0
  14. package/dist/customer-business-onboarding-service.js +237 -0
  15. package/dist/customer-business-onboarding-service.js.map +1 -0
  16. package/dist/customer-buyer-accounts.d.ts +0 -19
  17. package/dist/customer-buyer-accounts.js.map +1 -1
  18. package/dist/node-runtime.d.ts +13 -0
  19. package/dist/node-runtime.js +285 -1
  20. package/dist/node-runtime.js.map +1 -1
  21. package/dist/ports.d.ts +1 -0
  22. package/dist/ports.js +2 -0
  23. package/dist/ports.js.map +1 -0
  24. package/dist/runtime-contributor.js +11 -0
  25. package/dist/runtime-contributor.js.map +1 -1
  26. package/dist/server.d.ts +39 -3
  27. package/dist/server.js.map +1 -1
  28. package/dist/storefront-credentials.d.ts +31 -0
  29. package/dist/storefront-credentials.js +54 -0
  30. package/dist/storefront-credentials.js.map +1 -0
  31. package/dist/storefront-customer-auth-resolver.d.ts +30 -0
  32. package/dist/storefront-customer-auth-resolver.js +72 -0
  33. package/dist/storefront-customer-auth-resolver.js.map +1 -0
  34. package/dist/storefront-keys.d.ts +40 -0
  35. package/dist/storefront-keys.js +50 -0
  36. package/dist/storefront-keys.js.map +1 -0
  37. package/dist/storefront-local-adapter.d.ts +11 -0
  38. package/dist/storefront-local-adapter.js +313 -0
  39. package/dist/storefront-local-adapter.js.map +1 -0
  40. package/dist/storefront-origins.d.ts +34 -0
  41. package/dist/storefront-origins.js +132 -0
  42. package/dist/storefront-origins.js.map +1 -0
  43. package/dist/storefront-runtime-port.d.ts +93 -0
  44. package/dist/storefront-runtime-port.js +35 -0
  45. package/dist/storefront-runtime-port.js.map +1 -0
  46. package/dist/voyant.d.ts +2 -0
  47. package/dist/voyant.js +99 -0
  48. package/dist/voyant.js.map +1 -1
  49. package/openapi/admin/customer-business-accounts.json +44 -0
  50. package/package.json +50 -4
package/dist/server.d.ts CHANGED
@@ -146,20 +146,32 @@ export type CreateCustomerBetterAuthOptions<UserOptions extends BetterAuthOption
146
146
  accountPolicy?: CustomerBuyerAccountPolicy | null;
147
147
  sendOrganizationInvitation?: (data: {
148
148
  id: string;
149
+ role: string;
149
150
  email: string;
150
151
  organization: {
151
152
  id: string;
152
153
  name: string;
153
154
  slug: string;
154
155
  };
156
+ invitation: {
157
+ id: string;
158
+ email: string;
159
+ role: string;
160
+ organizationId: string;
161
+ inviterId: string;
162
+ status: string;
163
+ expiresAt: Date;
164
+ };
155
165
  inviter: {
166
+ id: string;
167
+ role: string;
156
168
  user: {
157
169
  id: string;
158
170
  name: string;
159
171
  email: string;
160
172
  };
161
173
  };
162
- }) => Promise<void>;
174
+ }, request?: Request) => Promise<void>;
163
175
  };
164
176
  /** Storefront realm factory with isolated tables, cookies, secret, and routes. */
165
177
  export declare function createCustomerBetterAuth<const UserOptions extends BetterAuthOptions["user"] = undefined, const Plugins extends BetterAuthPlugin[] | undefined = undefined>(options?: CreateCustomerBetterAuthOptions<UserOptions, Plugins>): import("better-auth").Auth<ResolvedCreateBetterAuthOptions<{
@@ -190,20 +202,32 @@ export declare function createCustomerBetterAuth<const UserOptions extends Bette
190
202
  };
191
203
  sendInvitationEmail?: ((data: {
192
204
  id: string;
205
+ role: string;
193
206
  email: string;
194
207
  organization: {
195
208
  id: string;
196
209
  name: string;
197
210
  slug: string;
198
211
  };
212
+ invitation: {
213
+ id: string;
214
+ email: string;
215
+ role: string;
216
+ organizationId: string;
217
+ inviterId: string;
218
+ status: string;
219
+ expiresAt: Date;
220
+ };
199
221
  inviter: {
222
+ id: string;
223
+ role: string;
200
224
  user: {
201
225
  id: string;
202
226
  name: string;
203
227
  email: string;
204
228
  };
205
229
  };
206
- }) => Promise<void>) | undefined;
230
+ }, request?: Request) => Promise<void>) | undefined;
207
231
  allowUserToCreateOrganization: false;
208
232
  disableOrganizationDeletion: true;
209
233
  requireEmailVerificationOnInvitation: true;
@@ -222,20 +246,32 @@ export declare function createCustomerBetterAuth<const UserOptions extends Bette
222
246
  };
223
247
  sendInvitationEmail?: ((data: {
224
248
  id: string;
249
+ role: string;
225
250
  email: string;
226
251
  organization: {
227
252
  id: string;
228
253
  name: string;
229
254
  slug: string;
230
255
  };
256
+ invitation: {
257
+ id: string;
258
+ email: string;
259
+ role: string;
260
+ organizationId: string;
261
+ inviterId: string;
262
+ status: string;
263
+ expiresAt: Date;
264
+ };
231
265
  inviter: {
266
+ id: string;
267
+ role: string;
232
268
  user: {
233
269
  id: string;
234
270
  name: string;
235
271
  email: string;
236
272
  };
237
273
  };
238
- }) => Promise<void>) | undefined;
274
+ }, request?: Request) => Promise<void>) | undefined;
239
275
  allowUserToCreateOrganization: false;
240
276
  disableOrganizationDeletion: true;
241
277
  requireEmailVerificationOnInvitation: true;
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { apiKey } from \"@better-auth/api-key\"\nimport { getDb } from \"@voyant-travel/db\"\nimport {\n apikeyTable,\n authAccount,\n authSession,\n authUser,\n authVerification,\n customerAuthAccount,\n customerAuthInvitation,\n customerAuthMember,\n customerAuthOrganization,\n customerAuthSession,\n customerAuthUser,\n customerAuthVerification,\n} from \"@voyant-travel/db/schema/iam\"\nimport { type BetterAuthOptions, betterAuth } from \"better-auth\"\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\"\nimport { emailOTP, organization } from \"better-auth/plugins\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\nimport { sql } from \"drizzle-orm\"\nimport type { AnyPgTable } from \"drizzle-orm/pg-core\"\nimport {\n type CustomerBuyerAccountPolicy,\n normalizeCustomerBuyerAccountPolicy,\n} from \"./customer-buyer-accounts.js\"\nimport {\n createLocalMemberAccessPlugin,\n isCustomerSessionActive,\n isLocalMemberDeactivated,\n isLocalMemberEmailDeactivated,\n isLocalMemberSessionActive,\n} from \"./local-member-access.js\"\nimport { expandTrustedOrigins, getTrustedOrigins } from \"./trusted-origins.js\"\nimport { isFirstAuthUser, provisionCurrentUserProfile } from \"./workspace.js\"\n\nexport function getAuthSecret(realm: \"admin\" | \"customer\" = \"admin\"): string {\n const envName = realm === \"customer\" ? \"BETTER_AUTH_CUSTOMER_SECRET\" : \"BETTER_AUTH_ADMIN_SECRET\"\n const secret = process.env[envName] || \"\"\n\n if (!secret || secret.length < 32) {\n throw new Error(`Missing ${envName} with at least 32 characters`)\n }\n\n return secret\n}\n\nexport type {\n AccountProfileUpdateHandler,\n ApiTokenRotationStore,\n BetterAuthApiTokenManagement,\n HandleAccountProfileRequestOptions,\n HandleApiTokenManagementRequestOptions,\n HandleOrganizationMembersRequestOptions,\n OrganizationMemberRecord,\n OrganizationMembersListHandler,\n OrganizationMembersListInput,\n} from \"./auth-facades.js\"\nexport {\n handleAccountProfileRequest,\n handleApiTokenManagementRequest,\n handleOrganizationMembersRequest,\n} from \"./auth-facades.js\"\n\ntype DefinedBetterAuthUserOptions<UserOptions extends BetterAuthOptions[\"user\"]> =\n UserOptions extends undefined ? Record<PropertyKey, never> : NonNullable<UserOptions>\n\ntype ResolvedBetterAuthChangeEmail<UserOptions extends BetterAuthOptions[\"user\"]> =\n DefinedBetterAuthUserOptions<UserOptions> extends { changeEmail?: infer ChangeEmail }\n ? ChangeEmail extends { enabled: infer Enabled }\n ? Omit<ChangeEmail, \"enabled\"> & { enabled: Enabled }\n : NonNullable<ChangeEmail> & { enabled: true }\n : { enabled: true }\n\ntype ResolvedBetterAuthUserOptions<UserOptions extends BetterAuthOptions[\"user\"]> = Omit<\n DefinedBetterAuthUserOptions<UserOptions>,\n \"changeEmail\"\n> & {\n changeEmail: ResolvedBetterAuthChangeEmail<UserOptions>\n}\n\ntype VoyantBetterAuthPlugins = [\n ReturnType<typeof apiKey>,\n ReturnType<typeof emailOTP>,\n ReturnType<typeof createLocalMemberAccessPlugin>,\n]\n\ntype ResolvedBetterAuthPlugins<_Plugins extends BetterAuthPlugin[] | undefined> =\n | VoyantBetterAuthPlugins\n | BetterAuthPlugin[]\n\nconst DEFAULT_SIGNUP_BLOCK_SURFACES = [\"admin\"] as const\nconst CUSTOMER_SIGNUP_ENDPOINT_SUFFIXES = [\n \"/sign-up/email\",\n \"/phone-number/verify\",\n \"/sign-in/email-otp\",\n] as const\n\nexport interface DisableSignupWhenUsersExistOptions {\n /**\n * Set to false when the consuming app owns all signup admission checks.\n *\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * User surfaces that should keep the single-tenant signup guard. New users\n * with only other explicit surfaces can still be created by customer-facing\n * auth plugins such as phone OTP.\n *\n * Defaults to [\"admin\"]. Users without an explicit surfaces array are\n * treated as admin users for backward compatibility.\n */\n surfaces?: readonly string[]\n}\n\ntype BetterAuthCreateBeforeResult =\n | boolean\n | undefined\n | {\n data: Record<string, unknown>\n }\n\ntype SignupBlockUserPayload = {\n surfaces?: unknown\n}\n\ntype BetterAuthHookContext = {\n path?: string\n}\n\nfunction normalizeSignupBlockSurfaces(\n options: DisableSignupWhenUsersExistOptions | undefined,\n): readonly string[] {\n return options?.surfaces ?? DEFAULT_SIGNUP_BLOCK_SURFACES\n}\n\nfunction isSignupBlockEnabled(options: DisableSignupWhenUsersExistOptions | undefined): boolean {\n return options?.enabled !== false\n}\n\nfunction normalizeSurfaceList(surfaces: readonly string[] | undefined): readonly string[] {\n if (!surfaces) return []\n\n const normalized = new Set<string>()\n for (const surface of surfaces) {\n const trimmed = surface.trim()\n if (trimmed.length > 0) {\n normalized.add(trimmed)\n }\n }\n return Array.from(normalized)\n}\n\nfunction signupBlockAppliesToUser(\n user: SignupBlockUserPayload | undefined,\n blockedSurfaces: readonly string[],\n): boolean {\n if (blockedSurfaces.length === 0) return false\n\n const surfaces = user?.surfaces\n if (Array.isArray(surfaces)) {\n const explicitSurfaces = surfaces.filter(\n (surface): surface is string => typeof surface === \"string\" && surface.trim().length > 0,\n )\n if (explicitSurfaces.length > 0) {\n return explicitSurfaces.some((surface) => blockedSurfaces.includes(surface.trim()))\n }\n }\n\n if (typeof surfaces === \"string\" && surfaces.trim().length > 0) {\n return blockedSurfaces.includes(surfaces.trim())\n }\n\n return blockedSurfaces.includes(\"admin\")\n}\n\nfunction isCustomerSignupCreate(context: BetterAuthHookContext | null | undefined): boolean {\n const path = context?.path\n if (!path) return false\n\n return CUSTOMER_SIGNUP_ENDPOINT_SUFFIXES.some((suffix) => path.endsWith(suffix))\n}\n\nfunction stampCustomerSignupSurfaces(\n user: SignupBlockUserPayload | undefined,\n context: BetterAuthHookContext | null | undefined,\n surfaces: readonly string[],\n): SignupBlockUserPayload | undefined {\n if (surfaces.length === 0 || !isCustomerSignupCreate(context)) {\n return user\n }\n\n return {\n ...(user ?? {}),\n surfaces,\n }\n}\n\ntype ResolvedCreateBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined,\n> = Omit<BetterAuthOptions, \"plugins\" | \"user\"> & {\n plugins: ResolvedBetterAuthPlugins<Plugins>\n user: ResolvedBetterAuthUserOptions<UserOptions>\n}\n\nexport type BetterAuthDrizzleSchema = Record<string, AnyPgTable>\n\nexport type VoyantAuthRealm = \"admin\" | \"customer\"\n\nexport interface BetterAuthRealmTables {\n user: AnyPgTable\n session: AnyPgTable\n account: AnyPgTable\n verification: AnyPgTable\n apikey?: AnyPgTable\n}\n\nconst ADMIN_AUTH_TABLES: BetterAuthRealmTables = {\n user: authUser,\n session: authSession,\n account: authAccount,\n verification: authVerification,\n apikey: apikeyTable,\n}\n\nconst CUSTOMER_AUTH_TABLES = {\n user: customerAuthUser,\n session: customerAuthSession,\n account: customerAuthAccount,\n verification: customerAuthVerification,\n organization: customerAuthOrganization,\n member: customerAuthMember,\n invitation: customerAuthInvitation,\n} satisfies BetterAuthRealmTables & BetterAuthDrizzleSchema\n\nexport interface CreateBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> {\n db?: ReturnType<typeof getDb>\n secret?: string\n baseURL?: string\n basePath?: string\n trustedOrigins?: string[]\n /** Security realm. Existing callers default to the isolated admin realm. */\n realm?: VoyantAuthRealm\n /** Override only when an adapter owns a compatible Better Auth schema. */\n realmTables?: BetterAuthRealmTables\n /**\n * Additional Drizzle tables for Better Auth plugins. The consuming app owns\n * matching migrations for every table passed here.\n */\n extraSchema?: BetterAuthDrizzleSchema\n plugins?: Plugins\n user?: UserOptions\n /** Resolved credentials; the auth package never persists provider secrets. */\n socialProviders?: BetterAuthOptions[\"socialProviders\"]\n emailCodeEnabled?: boolean\n emailPasswordEnabled?: boolean\n /**\n * Surfaces stamped on Better Auth customer self-signups before the bundled\n * single-tenant signup guard evaluates the new user. Applies to Better\n * Auth OTP signup endpoints that create a user as part of verification.\n *\n * Leave undefined to preserve Better Auth's raw user payload.\n */\n customerSignupSurfaces?: readonly string[]\n disableSignupWhenUsersExist?: DisableSignupWhenUsersExistOptions\n /** Called when a user requests a password reset. If not provided, logs to console. */\n sendResetPassword?: (data: {\n user: { email: string; name: string }\n url: string\n token: string\n }) => Promise<void>\n /** Called to send a verification OTP. If not provided, logs to console. */\n sendVerificationOTP?: (data: { email: string; otp: string; type: string }) => Promise<void>\n /**\n * Better Auth session cookie cache: session data rides in a short-lived\n * signed cookie. Enabled by default with a 5-minute TTL. Voyant still\n * validates local membership and session existence against Postgres so a\n * deleted session cannot be revived from the cookie cache. Pass `false` to\n * disable payload caching, or tune `maxAge` (seconds).\n */\n sessionCookieCache?: false | { maxAge?: number }\n /** Better Auth secondary storage, typically Redis/KV, used for distributed rate limits. */\n secondaryStorage?: BetterAuthOptions[\"secondaryStorage\"]\n /** Better Auth rate-limit config. Defaults to secondary storage when present. */\n rateLimit?: BetterAuthOptions[\"rateLimit\"]\n /**\n * Additional Better Auth advanced options. Voyant supplies secure cookies\n * by default except in explicit local development. Set\n * `advanced.useSecureCookies` to override that default.\n */\n advanced?: BetterAuthOptions[\"advanced\"]\n}\n\n/**\n * Framework-agnostic Better Auth factory.\n *\n * Accepts optional overrides for db, secret, baseURL, trustedOrigins.\n * Does NOT depend on Next.js — safe to use in Hono workers, TanStack Start, etc.\n */\nexport function createBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateBetterAuthOptions<UserOptions, Plugins> = {}) {\n const realm = options.realm ?? \"admin\"\n const db = options.db ?? getDb(\"edge\")\n const secret = options.secret ?? getAuthSecret(realm)\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n process.env.APP_URL ??\n process.env.NEXT_PUBLIC_APP_URL ??\n \"http://localhost:3000\"\n const trustedOrigins = expandTrustedOrigins(\n options.trustedOrigins ?? getTrustedOrigins(),\n baseURL,\n )\n const extraPlugins = options.plugins ?? []\n const realmTables =\n options.realmTables ?? (realm === \"customer\" ? CUSTOMER_AUTH_TABLES : ADMIN_AUTH_TABLES)\n const signupBlockSurfaces = normalizeSignupBlockSurfaces(options.disableSignupWhenUsersExist)\n const signupBlockEnabled = isSignupBlockEnabled(options.disableSignupWhenUsersExist)\n const customerSignupSurfaces = normalizeSurfaceList(options.customerSignupSurfaces)\n const reservedSchemaCollision = Object.keys(options.extraSchema ?? {}).find(\n (modelName) => (realmTables as unknown as BetterAuthDrizzleSchema)[modelName] !== undefined,\n )\n if (reservedSchemaCollision) {\n throw new Error(`extraSchema cannot override reserved auth model: ${reservedSchemaCollision}`)\n }\n const schema = {\n ...realmTables,\n ...(options.extraSchema ?? {}),\n } satisfies BetterAuthDrizzleSchema\n const rateLimit =\n options.rateLimit ??\n (options.secondaryStorage\n ? ({\n enabled: true,\n window: 60,\n max: 100,\n storage: \"secondary-storage\",\n } as BetterAuthOptions[\"rateLimit\"])\n : undefined)\n\n const authOptions = {\n appName: \"Voyant\",\n baseURL,\n ...(options.basePath ? { basePath: options.basePath } : {}),\n secret,\n database: drizzleAdapter(db, {\n provider: \"pg\",\n schema,\n }),\n ...(options.secondaryStorage ? { secondaryStorage: options.secondaryStorage } : {}),\n ...(rateLimit ? { rateLimit } : {}),\n ...(options.sessionCookieCache === false\n ? {}\n : {\n session: {\n cookieCache: {\n enabled: true,\n maxAge: options.sessionCookieCache?.maxAge ?? 300,\n },\n },\n }),\n emailAndPassword: {\n enabled: options.emailPasswordEnabled !== false,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n requireEmailVerification: true,\n revokeSessionsOnPasswordReset: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- owner: auth; existing suppression is intentional pending typed cleanup.\n sendResetPassword:\n options.sendResetPassword ??\n (async ({ user, url }) => {\n console.warn(\n `[Auth] No email provider configured — password reset for ${user.email}: ${url}`,\n )\n }),\n },\n emailVerification: {\n sendOnSignUp: false, // OTP plugin handles this\n autoSignInAfterVerification: true,\n },\n user: {\n ...options.user,\n changeEmail: {\n enabled: true,\n ...options.user?.changeEmail,\n },\n } as ResolvedBetterAuthUserOptions<UserOptions>,\n socialProviders: options.socialProviders ?? {},\n trustedOrigins,\n plugins: [\n ...(realm === \"admin\"\n ? [\n apiKey({\n defaultPrefix: \"voy_\",\n apiKeyHeaders: [\"authorization\"],\n requireName: true,\n keyExpiration: {\n defaultExpiresIn: null,\n },\n }),\n ]\n : []),\n ...(options.emailCodeEnabled === false\n ? []\n : [\n emailOTP({\n // eslint-disable-next-line @typescript-eslint/require-await -- owner: auth; existing suppression is intentional pending typed cleanup.\n sendVerificationOTP:\n options.sendVerificationOTP ??\n (async ({ email, otp, type }) => {\n console.warn(`[Auth] OTP for ${email}: ${otp} (${type})`)\n }),\n otpLength: 6,\n expiresIn: 600,\n sendVerificationOnSignUp: true,\n changeEmail: {\n enabled: true,\n },\n }),\n ]),\n createLocalMemberAccessPlugin(\n realm === \"admin\"\n ? {\n isEmailDeactivated: (email) => isLocalMemberEmailDeactivated(db, email),\n isSessionActive: (sessionId, userId) =>\n isLocalMemberSessionActive(db, sessionId, userId),\n isUserDeactivated: (userId) => isLocalMemberDeactivated(db, userId),\n }\n : {\n isEmailDeactivated: async () => false,\n isSessionActive: (sessionId, userId) =>\n isCustomerSessionActive(db, sessionId, userId),\n isUserDeactivated: async () => false,\n },\n ),\n ...extraPlugins,\n ] as ResolvedBetterAuthPlugins<Plugins>,\n ...(realm === \"admin\"\n ? {\n databaseHooks: {\n user: {\n create: {\n // Single-tenant: once a user exists, reject any new-user creation.\n // Covers email sign-up AND social-provider sign-up (Google would\n // otherwise auto-create a user on first OAuth callback). Existing\n // social sign-ins still work because this hook only fires on CREATE.\n // Seed scripts do raw drizzle inserts, so they bypass this hook —\n // which is intentional.\n before: async (user, context): Promise<BetterAuthCreateBeforeResult> => {\n const normalizedUser = stampCustomerSignupSurfaces(\n user as SignupBlockUserPayload,\n context as BetterAuthHookContext | null,\n customerSignupSurfaces,\n )\n\n if (\n !signupBlockEnabled ||\n !signupBlockAppliesToUser(normalizedUser, signupBlockSurfaces)\n ) {\n if (normalizedUser === user) {\n return\n }\n return { data: normalizedUser as Record<string, unknown> }\n }\n\n const [row] = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(realmTables.user)\n if ((row?.count ?? 0) > 0) {\n throw new Error(\"Sign-up is disabled. Ask an admin to invite you.\")\n }\n\n if (normalizedUser !== user) {\n return { data: normalizedUser as Record<string, unknown> }\n }\n },\n after: async (user, context) => {\n if (\n customerSignupSurfaces.length > 0 &&\n isCustomerSignupCreate(context as BetterAuthHookContext | null)\n ) {\n return\n }\n\n // Single-tenant bootstrap: the very first user to register becomes\n // the super-admin. Runs atomically after the `user` row is\n // inserted, so a simple COUNT(*) = 1 check identifies them.\n await provisionCurrentUserProfile(db, {\n userId: user.id,\n name: user.name,\n image: user.image,\n isSuperAdmin: await isFirstAuthUser(db),\n })\n },\n },\n },\n },\n }\n : {}),\n advanced: {\n ...options.advanced,\n cookiePrefix:\n options.advanced?.cookiePrefix ??\n (realm === \"customer\" ? \"voyant-customer\" : \"voyant-admin\"),\n useSecureCookies:\n options.advanced?.useSecureCookies ?? process.env.NODE_ENV !== \"development\",\n },\n } as ResolvedCreateBetterAuthOptions<UserOptions, Plugins>\n\n return betterAuth<ResolvedCreateBetterAuthOptions<UserOptions, Plugins>>(authOptions)\n}\n\nexport type CreateAdminBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> = Omit<CreateBetterAuthOptions<UserOptions, Plugins>, \"realm\" | \"realmTables\">\n\nexport function createAdminBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateAdminBetterAuthOptions<UserOptions, Plugins> = {}) {\n return createBetterAuth({\n ...options,\n realm: \"admin\",\n realmTables: ADMIN_AUTH_TABLES,\n basePath: options.basePath ?? \"/auth/admin\",\n })\n}\n\nexport interface CustomerAuthMethods {\n emailCode?: boolean\n emailPassword?: boolean\n /** Better Auth provider configs such as google, facebook, and apple. */\n socialProviders?: BetterAuthOptions[\"socialProviders\"]\n}\n\nexport type CreateCustomerBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> = Omit<\n CreateBetterAuthOptions<UserOptions, Plugins>,\n | \"realm\"\n | \"realmTables\"\n | \"customerSignupSurfaces\"\n | \"disableSignupWhenUsersExist\"\n | \"socialProviders\"\n | \"emailCodeEnabled\"\n | \"emailPasswordEnabled\"\n> & {\n methods?: CustomerAuthMethods\n accountPolicy?: CustomerBuyerAccountPolicy | null\n sendOrganizationInvitation?: (data: {\n id: string\n email: string\n organization: { id: string; name: string; slug: string }\n inviter: { user: { id: string; name: string; email: string } }\n }) => Promise<void>\n}\n\n/** Storefront realm factory with isolated tables, cookies, secret, and routes. */\nexport function createCustomerBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateCustomerBetterAuthOptions<UserOptions, Plugins> = {}) {\n const accountPolicy = normalizeCustomerBuyerAccountPolicy(options.accountPolicy)\n const customerOrganizationPlugin = organization({\n // A Better Auth organization is only the membership container. Public\n // creation cannot also create/link the canonical Relationships Organization,\n // so onboarding must go through the provider-neutral orchestration seam.\n allowUserToCreateOrganization: false,\n disableOrganizationDeletion: true,\n requireEmailVerificationOnInvitation: true,\n invitationLimit: options.sendOrganizationInvitation ? 100 : 0,\n ...(options.sendOrganizationInvitation\n ? { sendInvitationEmail: options.sendOrganizationInvitation }\n : {}),\n schema: {\n organization: {\n additionalFields: {\n relationshipOrganizationId: {\n type: \"string\",\n required: false,\n input: false,\n },\n },\n },\n },\n })\n\n return createBetterAuth({\n ...options,\n realm: \"customer\",\n realmTables: CUSTOMER_AUTH_TABLES,\n basePath: options.basePath ?? \"/auth/customer\",\n disableSignupWhenUsersExist: { enabled: false },\n emailCodeEnabled: options.methods?.emailCode ?? true,\n emailPasswordEnabled: options.methods?.emailPassword ?? true,\n socialProviders: options.methods?.socialProviders,\n user: {\n ...options.user,\n additionalFields: {\n ...options.user?.additionalFields,\n personalBuyerEntitlementEligible: {\n type: \"boolean\",\n required: true,\n input: false,\n defaultValue:\n accountPolicy.allowedKinds.includes(\"personal\") &&\n accountPolicy.personalSignup === \"open\",\n },\n relationshipPersonId: {\n type: \"string\",\n required: false,\n input: false,\n },\n },\n },\n extraSchema: options.extraSchema,\n plugins: [customerOrganizationPlugin, ...(options.plugins ?? [])],\n })\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { apiKey } from \"@better-auth/api-key\"\nimport { getDb } from \"@voyant-travel/db\"\nimport {\n apikeyTable,\n authAccount,\n authSession,\n authUser,\n authVerification,\n customerAuthAccount,\n customerAuthInvitation,\n customerAuthMember,\n customerAuthOrganization,\n customerAuthSession,\n customerAuthUser,\n customerAuthVerification,\n} from \"@voyant-travel/db/schema/iam\"\nimport { type BetterAuthOptions, betterAuth } from \"better-auth\"\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\"\nimport { emailOTP, organization } from \"better-auth/plugins\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\nimport { sql } from \"drizzle-orm\"\nimport type { AnyPgTable } from \"drizzle-orm/pg-core\"\nimport {\n type CustomerBuyerAccountPolicy,\n normalizeCustomerBuyerAccountPolicy,\n} from \"./customer-buyer-accounts.js\"\nimport {\n createLocalMemberAccessPlugin,\n isCustomerSessionActive,\n isLocalMemberDeactivated,\n isLocalMemberEmailDeactivated,\n isLocalMemberSessionActive,\n} from \"./local-member-access.js\"\nimport { expandTrustedOrigins, getTrustedOrigins } from \"./trusted-origins.js\"\nimport { isFirstAuthUser, provisionCurrentUserProfile } from \"./workspace.js\"\n\nexport function getAuthSecret(realm: \"admin\" | \"customer\" = \"admin\"): string {\n const envName = realm === \"customer\" ? \"BETTER_AUTH_CUSTOMER_SECRET\" : \"BETTER_AUTH_ADMIN_SECRET\"\n const secret = process.env[envName] || \"\"\n\n if (!secret || secret.length < 32) {\n throw new Error(`Missing ${envName} with at least 32 characters`)\n }\n\n return secret\n}\n\nexport type {\n AccountProfileUpdateHandler,\n ApiTokenRotationStore,\n BetterAuthApiTokenManagement,\n HandleAccountProfileRequestOptions,\n HandleApiTokenManagementRequestOptions,\n HandleOrganizationMembersRequestOptions,\n OrganizationMemberRecord,\n OrganizationMembersListHandler,\n OrganizationMembersListInput,\n} from \"./auth-facades.js\"\nexport {\n handleAccountProfileRequest,\n handleApiTokenManagementRequest,\n handleOrganizationMembersRequest,\n} from \"./auth-facades.js\"\n\ntype DefinedBetterAuthUserOptions<UserOptions extends BetterAuthOptions[\"user\"]> =\n UserOptions extends undefined ? Record<PropertyKey, never> : NonNullable<UserOptions>\n\ntype ResolvedBetterAuthChangeEmail<UserOptions extends BetterAuthOptions[\"user\"]> =\n DefinedBetterAuthUserOptions<UserOptions> extends { changeEmail?: infer ChangeEmail }\n ? ChangeEmail extends { enabled: infer Enabled }\n ? Omit<ChangeEmail, \"enabled\"> & { enabled: Enabled }\n : NonNullable<ChangeEmail> & { enabled: true }\n : { enabled: true }\n\ntype ResolvedBetterAuthUserOptions<UserOptions extends BetterAuthOptions[\"user\"]> = Omit<\n DefinedBetterAuthUserOptions<UserOptions>,\n \"changeEmail\"\n> & {\n changeEmail: ResolvedBetterAuthChangeEmail<UserOptions>\n}\n\ntype VoyantBetterAuthPlugins = [\n ReturnType<typeof apiKey>,\n ReturnType<typeof emailOTP>,\n ReturnType<typeof createLocalMemberAccessPlugin>,\n]\n\ntype ResolvedBetterAuthPlugins<_Plugins extends BetterAuthPlugin[] | undefined> =\n | VoyantBetterAuthPlugins\n | BetterAuthPlugin[]\n\nconst DEFAULT_SIGNUP_BLOCK_SURFACES = [\"admin\"] as const\nconst CUSTOMER_SIGNUP_ENDPOINT_SUFFIXES = [\n \"/sign-up/email\",\n \"/phone-number/verify\",\n \"/sign-in/email-otp\",\n] as const\n\nexport interface DisableSignupWhenUsersExistOptions {\n /**\n * Set to false when the consuming app owns all signup admission checks.\n *\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * User surfaces that should keep the single-tenant signup guard. New users\n * with only other explicit surfaces can still be created by customer-facing\n * auth plugins such as phone OTP.\n *\n * Defaults to [\"admin\"]. Users without an explicit surfaces array are\n * treated as admin users for backward compatibility.\n */\n surfaces?: readonly string[]\n}\n\ntype BetterAuthCreateBeforeResult =\n | boolean\n | undefined\n | {\n data: Record<string, unknown>\n }\n\ntype SignupBlockUserPayload = {\n surfaces?: unknown\n}\n\ntype BetterAuthHookContext = {\n path?: string\n}\n\nfunction normalizeSignupBlockSurfaces(\n options: DisableSignupWhenUsersExistOptions | undefined,\n): readonly string[] {\n return options?.surfaces ?? DEFAULT_SIGNUP_BLOCK_SURFACES\n}\n\nfunction isSignupBlockEnabled(options: DisableSignupWhenUsersExistOptions | undefined): boolean {\n return options?.enabled !== false\n}\n\nfunction normalizeSurfaceList(surfaces: readonly string[] | undefined): readonly string[] {\n if (!surfaces) return []\n\n const normalized = new Set<string>()\n for (const surface of surfaces) {\n const trimmed = surface.trim()\n if (trimmed.length > 0) {\n normalized.add(trimmed)\n }\n }\n return Array.from(normalized)\n}\n\nfunction signupBlockAppliesToUser(\n user: SignupBlockUserPayload | undefined,\n blockedSurfaces: readonly string[],\n): boolean {\n if (blockedSurfaces.length === 0) return false\n\n const surfaces = user?.surfaces\n if (Array.isArray(surfaces)) {\n const explicitSurfaces = surfaces.filter(\n (surface): surface is string => typeof surface === \"string\" && surface.trim().length > 0,\n )\n if (explicitSurfaces.length > 0) {\n return explicitSurfaces.some((surface) => blockedSurfaces.includes(surface.trim()))\n }\n }\n\n if (typeof surfaces === \"string\" && surfaces.trim().length > 0) {\n return blockedSurfaces.includes(surfaces.trim())\n }\n\n return blockedSurfaces.includes(\"admin\")\n}\n\nfunction isCustomerSignupCreate(context: BetterAuthHookContext | null | undefined): boolean {\n const path = context?.path\n if (!path) return false\n\n return CUSTOMER_SIGNUP_ENDPOINT_SUFFIXES.some((suffix) => path.endsWith(suffix))\n}\n\nfunction stampCustomerSignupSurfaces(\n user: SignupBlockUserPayload | undefined,\n context: BetterAuthHookContext | null | undefined,\n surfaces: readonly string[],\n): SignupBlockUserPayload | undefined {\n if (surfaces.length === 0 || !isCustomerSignupCreate(context)) {\n return user\n }\n\n return {\n ...(user ?? {}),\n surfaces,\n }\n}\n\ntype ResolvedCreateBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined,\n> = Omit<BetterAuthOptions, \"plugins\" | \"user\"> & {\n plugins: ResolvedBetterAuthPlugins<Plugins>\n user: ResolvedBetterAuthUserOptions<UserOptions>\n}\n\nexport type BetterAuthDrizzleSchema = Record<string, AnyPgTable>\n\nexport type VoyantAuthRealm = \"admin\" | \"customer\"\n\nexport interface BetterAuthRealmTables {\n user: AnyPgTable\n session: AnyPgTable\n account: AnyPgTable\n verification: AnyPgTable\n apikey?: AnyPgTable\n}\n\nconst ADMIN_AUTH_TABLES: BetterAuthRealmTables = {\n user: authUser,\n session: authSession,\n account: authAccount,\n verification: authVerification,\n apikey: apikeyTable,\n}\n\nconst CUSTOMER_AUTH_TABLES = {\n user: customerAuthUser,\n session: customerAuthSession,\n account: customerAuthAccount,\n verification: customerAuthVerification,\n organization: customerAuthOrganization,\n member: customerAuthMember,\n invitation: customerAuthInvitation,\n} satisfies BetterAuthRealmTables & BetterAuthDrizzleSchema\n\nexport interface CreateBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> {\n db?: ReturnType<typeof getDb>\n secret?: string\n baseURL?: string\n basePath?: string\n trustedOrigins?: string[]\n /** Security realm. Existing callers default to the isolated admin realm. */\n realm?: VoyantAuthRealm\n /** Override only when an adapter owns a compatible Better Auth schema. */\n realmTables?: BetterAuthRealmTables\n /**\n * Additional Drizzle tables for Better Auth plugins. The consuming app owns\n * matching migrations for every table passed here.\n */\n extraSchema?: BetterAuthDrizzleSchema\n plugins?: Plugins\n user?: UserOptions\n /** Resolved credentials; the auth package never persists provider secrets. */\n socialProviders?: BetterAuthOptions[\"socialProviders\"]\n emailCodeEnabled?: boolean\n emailPasswordEnabled?: boolean\n /**\n * Surfaces stamped on Better Auth customer self-signups before the bundled\n * single-tenant signup guard evaluates the new user. Applies to Better\n * Auth OTP signup endpoints that create a user as part of verification.\n *\n * Leave undefined to preserve Better Auth's raw user payload.\n */\n customerSignupSurfaces?: readonly string[]\n disableSignupWhenUsersExist?: DisableSignupWhenUsersExistOptions\n /** Called when a user requests a password reset. If not provided, logs to console. */\n sendResetPassword?: (data: {\n user: { email: string; name: string }\n url: string\n token: string\n }) => Promise<void>\n /** Called to send a verification OTP. If not provided, logs to console. */\n sendVerificationOTP?: (data: { email: string; otp: string; type: string }) => Promise<void>\n /**\n * Better Auth session cookie cache: session data rides in a short-lived\n * signed cookie. Enabled by default with a 5-minute TTL. Voyant still\n * validates local membership and session existence against Postgres so a\n * deleted session cannot be revived from the cookie cache. Pass `false` to\n * disable payload caching, or tune `maxAge` (seconds).\n */\n sessionCookieCache?: false | { maxAge?: number }\n /** Better Auth secondary storage, typically Redis/KV, used for distributed rate limits. */\n secondaryStorage?: BetterAuthOptions[\"secondaryStorage\"]\n /** Better Auth rate-limit config. Defaults to secondary storage when present. */\n rateLimit?: BetterAuthOptions[\"rateLimit\"]\n /**\n * Additional Better Auth advanced options. Voyant supplies secure cookies\n * by default except in explicit local development. Set\n * `advanced.useSecureCookies` to override that default.\n */\n advanced?: BetterAuthOptions[\"advanced\"]\n}\n\n/**\n * Framework-agnostic Better Auth factory.\n *\n * Accepts optional overrides for db, secret, baseURL, trustedOrigins.\n * Does NOT depend on Next.js — safe to use in Hono workers, TanStack Start, etc.\n */\nexport function createBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateBetterAuthOptions<UserOptions, Plugins> = {}) {\n const realm = options.realm ?? \"admin\"\n const db = options.db ?? getDb(\"edge\")\n const secret = options.secret ?? getAuthSecret(realm)\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n process.env.APP_URL ??\n process.env.NEXT_PUBLIC_APP_URL ??\n \"http://localhost:3000\"\n const trustedOrigins = expandTrustedOrigins(\n options.trustedOrigins ?? getTrustedOrigins(),\n baseURL,\n )\n const extraPlugins = options.plugins ?? []\n const realmTables =\n options.realmTables ?? (realm === \"customer\" ? CUSTOMER_AUTH_TABLES : ADMIN_AUTH_TABLES)\n const signupBlockSurfaces = normalizeSignupBlockSurfaces(options.disableSignupWhenUsersExist)\n const signupBlockEnabled = isSignupBlockEnabled(options.disableSignupWhenUsersExist)\n const customerSignupSurfaces = normalizeSurfaceList(options.customerSignupSurfaces)\n const reservedSchemaCollision = Object.keys(options.extraSchema ?? {}).find(\n (modelName) => (realmTables as unknown as BetterAuthDrizzleSchema)[modelName] !== undefined,\n )\n if (reservedSchemaCollision) {\n throw new Error(`extraSchema cannot override reserved auth model: ${reservedSchemaCollision}`)\n }\n const schema = {\n ...realmTables,\n ...(options.extraSchema ?? {}),\n } satisfies BetterAuthDrizzleSchema\n const rateLimit =\n options.rateLimit ??\n (options.secondaryStorage\n ? ({\n enabled: true,\n window: 60,\n max: 100,\n storage: \"secondary-storage\",\n } as BetterAuthOptions[\"rateLimit\"])\n : undefined)\n\n const authOptions = {\n appName: \"Voyant\",\n baseURL,\n ...(options.basePath ? { basePath: options.basePath } : {}),\n secret,\n database: drizzleAdapter(db, {\n provider: \"pg\",\n schema,\n }),\n ...(options.secondaryStorage ? { secondaryStorage: options.secondaryStorage } : {}),\n ...(rateLimit ? { rateLimit } : {}),\n ...(options.sessionCookieCache === false\n ? {}\n : {\n session: {\n cookieCache: {\n enabled: true,\n maxAge: options.sessionCookieCache?.maxAge ?? 300,\n },\n },\n }),\n emailAndPassword: {\n enabled: options.emailPasswordEnabled !== false,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n requireEmailVerification: true,\n revokeSessionsOnPasswordReset: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- owner: auth; existing suppression is intentional pending typed cleanup.\n sendResetPassword:\n options.sendResetPassword ??\n (async ({ user, url }) => {\n console.warn(\n `[Auth] No email provider configured — password reset for ${user.email}: ${url}`,\n )\n }),\n },\n emailVerification: {\n sendOnSignUp: false, // OTP plugin handles this\n autoSignInAfterVerification: true,\n },\n user: {\n ...options.user,\n changeEmail: {\n enabled: true,\n ...options.user?.changeEmail,\n },\n } as ResolvedBetterAuthUserOptions<UserOptions>,\n socialProviders: options.socialProviders ?? {},\n trustedOrigins,\n plugins: [\n ...(realm === \"admin\"\n ? [\n apiKey({\n defaultPrefix: \"voy_\",\n apiKeyHeaders: [\"authorization\"],\n requireName: true,\n keyExpiration: {\n defaultExpiresIn: null,\n },\n }),\n ]\n : []),\n ...(options.emailCodeEnabled === false\n ? []\n : [\n emailOTP({\n // eslint-disable-next-line @typescript-eslint/require-await -- owner: auth; existing suppression is intentional pending typed cleanup.\n sendVerificationOTP:\n options.sendVerificationOTP ??\n (async ({ email, otp, type }) => {\n console.warn(`[Auth] OTP for ${email}: ${otp} (${type})`)\n }),\n otpLength: 6,\n expiresIn: 600,\n sendVerificationOnSignUp: true,\n changeEmail: {\n enabled: true,\n },\n }),\n ]),\n createLocalMemberAccessPlugin(\n realm === \"admin\"\n ? {\n isEmailDeactivated: (email) => isLocalMemberEmailDeactivated(db, email),\n isSessionActive: (sessionId, userId) =>\n isLocalMemberSessionActive(db, sessionId, userId),\n isUserDeactivated: (userId) => isLocalMemberDeactivated(db, userId),\n }\n : {\n isEmailDeactivated: async () => false,\n isSessionActive: (sessionId, userId) =>\n isCustomerSessionActive(db, sessionId, userId),\n isUserDeactivated: async () => false,\n },\n ),\n ...extraPlugins,\n ] as ResolvedBetterAuthPlugins<Plugins>,\n ...(realm === \"admin\"\n ? {\n databaseHooks: {\n user: {\n create: {\n // Single-tenant: once a user exists, reject any new-user creation.\n // Covers email sign-up AND social-provider sign-up (Google would\n // otherwise auto-create a user on first OAuth callback). Existing\n // social sign-ins still work because this hook only fires on CREATE.\n // Seed scripts do raw drizzle inserts, so they bypass this hook —\n // which is intentional.\n before: async (user, context): Promise<BetterAuthCreateBeforeResult> => {\n const normalizedUser = stampCustomerSignupSurfaces(\n user as SignupBlockUserPayload,\n context as BetterAuthHookContext | null,\n customerSignupSurfaces,\n )\n\n if (\n !signupBlockEnabled ||\n !signupBlockAppliesToUser(normalizedUser, signupBlockSurfaces)\n ) {\n if (normalizedUser === user) {\n return\n }\n return { data: normalizedUser as Record<string, unknown> }\n }\n\n const [row] = await db\n .select({ count: sql<number>`count(*)::int` })\n .from(realmTables.user)\n if ((row?.count ?? 0) > 0) {\n throw new Error(\"Sign-up is disabled. Ask an admin to invite you.\")\n }\n\n if (normalizedUser !== user) {\n return { data: normalizedUser as Record<string, unknown> }\n }\n },\n after: async (user, context) => {\n if (\n customerSignupSurfaces.length > 0 &&\n isCustomerSignupCreate(context as BetterAuthHookContext | null)\n ) {\n return\n }\n\n // Single-tenant bootstrap: the very first user to register becomes\n // the super-admin. Runs atomically after the `user` row is\n // inserted, so a simple COUNT(*) = 1 check identifies them.\n await provisionCurrentUserProfile(db, {\n userId: user.id,\n name: user.name,\n image: user.image,\n isSuperAdmin: await isFirstAuthUser(db),\n })\n },\n },\n },\n },\n }\n : {}),\n advanced: {\n ...options.advanced,\n cookiePrefix:\n options.advanced?.cookiePrefix ??\n (realm === \"customer\" ? \"voyant-customer\" : \"voyant-admin\"),\n useSecureCookies:\n options.advanced?.useSecureCookies ?? process.env.NODE_ENV !== \"development\",\n },\n } as ResolvedCreateBetterAuthOptions<UserOptions, Plugins>\n\n return betterAuth<ResolvedCreateBetterAuthOptions<UserOptions, Plugins>>(authOptions)\n}\n\nexport type CreateAdminBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> = Omit<CreateBetterAuthOptions<UserOptions, Plugins>, \"realm\" | \"realmTables\">\n\nexport function createAdminBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateAdminBetterAuthOptions<UserOptions, Plugins> = {}) {\n return createBetterAuth({\n ...options,\n realm: \"admin\",\n realmTables: ADMIN_AUTH_TABLES,\n basePath: options.basePath ?? \"/auth/admin\",\n })\n}\n\nexport interface CustomerAuthMethods {\n emailCode?: boolean\n emailPassword?: boolean\n /** Better Auth provider configs such as google, facebook, and apple. */\n socialProviders?: BetterAuthOptions[\"socialProviders\"]\n}\n\nexport type CreateCustomerBetterAuthOptions<\n UserOptions extends BetterAuthOptions[\"user\"] = BetterAuthOptions[\"user\"],\n Plugins extends BetterAuthPlugin[] | undefined = BetterAuthPlugin[] | undefined,\n> = Omit<\n CreateBetterAuthOptions<UserOptions, Plugins>,\n | \"realm\"\n | \"realmTables\"\n | \"customerSignupSurfaces\"\n | \"disableSignupWhenUsersExist\"\n | \"socialProviders\"\n | \"emailCodeEnabled\"\n | \"emailPasswordEnabled\"\n> & {\n methods?: CustomerAuthMethods\n accountPolicy?: CustomerBuyerAccountPolicy | null\n sendOrganizationInvitation?: (\n data: {\n id: string\n role: string\n email: string\n organization: { id: string; name: string; slug: string }\n invitation: {\n id: string\n email: string\n role: string\n organizationId: string\n inviterId: string\n status: string\n expiresAt: Date\n }\n inviter: {\n id: string\n role: string\n user: { id: string; name: string; email: string }\n }\n },\n request?: Request,\n ) => Promise<void>\n}\n\n/** Storefront realm factory with isolated tables, cookies, secret, and routes. */\nexport function createCustomerBetterAuth<\n const UserOptions extends BetterAuthOptions[\"user\"] = undefined,\n const Plugins extends BetterAuthPlugin[] | undefined = undefined,\n>(options: CreateCustomerBetterAuthOptions<UserOptions, Plugins> = {}) {\n const accountPolicy = normalizeCustomerBuyerAccountPolicy(options.accountPolicy)\n const customerOrganizationPlugin = organization({\n // A Better Auth organization is only the membership container. Public\n // creation cannot also create/link the canonical Relationships Organization,\n // so onboarding must go through the provider-neutral orchestration seam.\n allowUserToCreateOrganization: false,\n disableOrganizationDeletion: true,\n requireEmailVerificationOnInvitation: true,\n invitationLimit: options.sendOrganizationInvitation ? 100 : 0,\n ...(options.sendOrganizationInvitation\n ? { sendInvitationEmail: options.sendOrganizationInvitation }\n : {}),\n schema: {\n organization: {\n additionalFields: {\n relationshipOrganizationId: {\n type: \"string\",\n required: false,\n input: false,\n },\n },\n },\n },\n })\n\n return createBetterAuth({\n ...options,\n realm: \"customer\",\n realmTables: CUSTOMER_AUTH_TABLES,\n basePath: options.basePath ?? \"/auth/customer\",\n disableSignupWhenUsersExist: { enabled: false },\n emailCodeEnabled: options.methods?.emailCode ?? true,\n emailPasswordEnabled: options.methods?.emailPassword ?? true,\n socialProviders: options.methods?.socialProviders,\n user: {\n ...options.user,\n additionalFields: {\n ...options.user?.additionalFields,\n personalBuyerEntitlementEligible: {\n type: \"boolean\",\n required: true,\n input: false,\n defaultValue:\n accountPolicy.allowedKinds.includes(\"personal\") &&\n accountPolicy.personalSignup === \"open\",\n },\n relationshipPersonId: {\n type: \"string\",\n required: false,\n input: false,\n },\n },\n },\n extraSchema: options.extraSchema,\n plugins: [customerOrganizationPlugin, ...(options.plugins ?? [])],\n })\n}\n"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Storefront OAuth provider credential bundles + the encryption seam.
3
+ *
4
+ * Plaintext provider secrets never touch persisted config: the local adapter
5
+ * encrypts a validated bundle through a `StorefrontCredentialCipher` (backed by
6
+ * the deployment's KMS) into the opaque `{ enc }` envelope stored on
7
+ * `storefrontCustomerAuthCredentials`, and decrypts it only when resolving a
8
+ * customer-auth context at request time.
9
+ */
10
+ import type { KmsEnvelope, StorefrontCustomerAuthSocialProvider } from "@voyant-travel/db/schema/iam";
11
+ export type StorefrontCredentialBundle = {
12
+ provider: StorefrontCustomerAuthSocialProvider;
13
+ clientId: string;
14
+ clientSecret: string;
15
+ };
16
+ /** Validate a provider-specific secret bundle before encryption. */
17
+ export declare function validateStorefrontCredentialBundle(provider: StorefrontCustomerAuthSocialProvider, credentials: Record<string, unknown>): StorefrontCredentialBundle;
18
+ /**
19
+ * Encryption seam for storefront provider secrets. The self-host runtime binds
20
+ * this to its KMS; tests bind a deterministic in-memory cipher.
21
+ */
22
+ export interface StorefrontCredentialCipher {
23
+ encrypt(plaintext: string): Promise<KmsEnvelope>;
24
+ decrypt(envelope: KmsEnvelope): Promise<string>;
25
+ }
26
+ /**
27
+ * Default cipher backed by the deployment's configured KMS (GCP/AWS/env/local),
28
+ * selected from environment exactly like every other framework secret. The
29
+ * ciphertext is wrapped in the opaque `{ enc }` envelope stored on the row.
30
+ */
31
+ export declare function createKmsStorefrontCredentialCipher(env: Record<string, string | undefined>): StorefrontCredentialCipher;
@@ -0,0 +1,54 @@
1
+ import { createKmsProviderFromEnv } from "@voyant-travel/utils/kms";
2
+ import { StorefrontInputError } from "./storefront-origins.js";
3
+ // Every provider is stored as `{ clientId, clientSecret }`, matching the shape
4
+ // `defaultCustomerAuthContext` feeds Better Auth's `socialProviders`. For Apple
5
+ // the `clientSecret` is the operator's pre-generated Sign-in-with-Apple JWT.
6
+ const REQUIRED_FIELDS = {
7
+ google: ["clientId", "clientSecret"],
8
+ facebook: ["clientId", "clientSecret"],
9
+ apple: ["clientId", "clientSecret"],
10
+ };
11
+ const MAX_CREDENTIAL_FIELD_LENGTH = 16_384;
12
+ function requireCredentialField(credentials, key) {
13
+ const value = credentials[key];
14
+ if (typeof value !== "string" || !value.trim()) {
15
+ throw new StorefrontInputError(`Customer auth credential field ${key} is required.`);
16
+ }
17
+ if (value.length > MAX_CREDENTIAL_FIELD_LENGTH) {
18
+ throw new StorefrontInputError(`Customer auth credential field ${key} is too large.`);
19
+ }
20
+ }
21
+ /** Validate a provider-specific secret bundle before encryption. */
22
+ export function validateStorefrontCredentialBundle(provider, credentials) {
23
+ const required = REQUIRED_FIELDS[provider];
24
+ for (const key of required)
25
+ requireCredentialField(credentials, key);
26
+ const allowed = new Set(["provider", ...required]);
27
+ const unexpected = Object.keys(credentials).filter((key) => !allowed.has(key));
28
+ if (unexpected.length > 0) {
29
+ throw new StorefrontInputError(`Unexpected ${provider} customer auth credential field(s): ${unexpected.join(", ")}.`);
30
+ }
31
+ return { provider, ...credentials };
32
+ }
33
+ // Storefront OAuth secrets are third-party integration credentials.
34
+ const STOREFRONT_CREDENTIAL_KEY = { keyType: "integrations" };
35
+ /**
36
+ * Default cipher backed by the deployment's configured KMS (GCP/AWS/env/local),
37
+ * selected from environment exactly like every other framework secret. The
38
+ * ciphertext is wrapped in the opaque `{ enc }` envelope stored on the row.
39
+ */
40
+ export function createKmsStorefrontCredentialCipher(env) {
41
+ const provider = createKmsProviderFromEnv(env);
42
+ return {
43
+ async encrypt(plaintext) {
44
+ return { enc: await provider.encrypt(plaintext, STOREFRONT_CREDENTIAL_KEY) };
45
+ },
46
+ async decrypt(envelope) {
47
+ if (!envelope) {
48
+ throw new StorefrontInputError("Stored storefront credential is empty.");
49
+ }
50
+ return provider.decrypt(envelope.enc, STOREFRONT_CREDENTIAL_KEY);
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=storefront-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storefront-credentials.js","sourceRoot":"","sources":["../src/storefront-credentials.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,wBAAwB,EAAe,MAAM,0BAA0B,CAAA;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAQ9D,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,MAAM,eAAe,GAAoE;IACvF,MAAM,EAAE,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,EAAE,CAAC,UAAU,EAAE,cAAc,CAAC;IACtC,KAAK,EAAE,CAAC,UAAU,EAAE,cAAc,CAAC;CACpC,CAAA;AAED,MAAM,2BAA2B,GAAG,MAAM,CAAA;AAE1C,SAAS,sBAAsB,CAAC,WAAoC,EAAE,GAAW;IAC/E,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,GAAG,eAAe,CAAC,CAAA;IACtF,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,2BAA2B,EAAE,CAAC;QAC/C,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,GAAG,gBAAgB,CAAC,CAAA;IACvF,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,kCAAkC,CAChD,QAA8C,EAC9C,WAAoC;IAEpC,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC1C,KAAK,MAAM,GAAG,IAAI,QAAQ;QAAE,sBAAsB,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;IACpE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAA;IAClD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9E,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,oBAAoB,CAC5B,cAAc,QAAQ,uCAAuC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACtF,CAAA;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAgC,CAAA;AACnE,CAAC;AAWD,oEAAoE;AACpE,MAAM,yBAAyB,GAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAA;AAErE;;;;GAIG;AACH,MAAM,UAAU,mCAAmC,CACjD,GAAuC;IAEvC,MAAM,QAAQ,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAA;IAC9C,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,SAAS;YACrB,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,yBAAyB,CAAC,EAAE,CAAA;QAC9E,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,QAAQ;YACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,CAAC,CAAA;YAC1E,CAAC;YACD,OAAO,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAA;QAClE,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * Storefront OAuth provider credential bundles + the encryption seam.\n *\n * Plaintext provider secrets never touch persisted config: the local adapter\n * encrypts a validated bundle through a `StorefrontCredentialCipher` (backed by\n * the deployment's KMS) into the opaque `{ enc }` envelope stored on\n * `storefrontCustomerAuthCredentials`, and decrypts it only when resolving a\n * customer-auth context at request time.\n */\nimport type {\n KmsEnvelope,\n StorefrontCustomerAuthSocialProvider,\n} from \"@voyant-travel/db/schema/iam\"\nimport { createKmsProviderFromEnv, type KeyRef } from \"@voyant-travel/utils/kms\"\n\nimport { StorefrontInputError } from \"./storefront-origins.js\"\n\nexport type StorefrontCredentialBundle = {\n provider: StorefrontCustomerAuthSocialProvider\n clientId: string\n clientSecret: string\n}\n\n// Every provider is stored as `{ clientId, clientSecret }`, matching the shape\n// `defaultCustomerAuthContext` feeds Better Auth's `socialProviders`. For Apple\n// the `clientSecret` is the operator's pre-generated Sign-in-with-Apple JWT.\nconst REQUIRED_FIELDS: Record<StorefrontCustomerAuthSocialProvider, readonly string[]> = {\n google: [\"clientId\", \"clientSecret\"],\n facebook: [\"clientId\", \"clientSecret\"],\n apple: [\"clientId\", \"clientSecret\"],\n}\n\nconst MAX_CREDENTIAL_FIELD_LENGTH = 16_384\n\nfunction requireCredentialField(credentials: Record<string, unknown>, key: string): void {\n const value = credentials[key]\n if (typeof value !== \"string\" || !value.trim()) {\n throw new StorefrontInputError(`Customer auth credential field ${key} is required.`)\n }\n if (value.length > MAX_CREDENTIAL_FIELD_LENGTH) {\n throw new StorefrontInputError(`Customer auth credential field ${key} is too large.`)\n }\n}\n\n/** Validate a provider-specific secret bundle before encryption. */\nexport function validateStorefrontCredentialBundle(\n provider: StorefrontCustomerAuthSocialProvider,\n credentials: Record<string, unknown>,\n): StorefrontCredentialBundle {\n const required = REQUIRED_FIELDS[provider]\n for (const key of required) requireCredentialField(credentials, key)\n const allowed = new Set([\"provider\", ...required])\n const unexpected = Object.keys(credentials).filter((key) => !allowed.has(key))\n if (unexpected.length > 0) {\n throw new StorefrontInputError(\n `Unexpected ${provider} customer auth credential field(s): ${unexpected.join(\", \")}.`,\n )\n }\n return { provider, ...credentials } as StorefrontCredentialBundle\n}\n\n/**\n * Encryption seam for storefront provider secrets. The self-host runtime binds\n * this to its KMS; tests bind a deterministic in-memory cipher.\n */\nexport interface StorefrontCredentialCipher {\n encrypt(plaintext: string): Promise<KmsEnvelope>\n decrypt(envelope: KmsEnvelope): Promise<string>\n}\n\n// Storefront OAuth secrets are third-party integration credentials.\nconst STOREFRONT_CREDENTIAL_KEY: KeyRef = { keyType: \"integrations\" }\n\n/**\n * Default cipher backed by the deployment's configured KMS (GCP/AWS/env/local),\n * selected from environment exactly like every other framework secret. The\n * ciphertext is wrapped in the opaque `{ enc }` envelope stored on the row.\n */\nexport function createKmsStorefrontCredentialCipher(\n env: Record<string, string | undefined>,\n): StorefrontCredentialCipher {\n const provider = createKmsProviderFromEnv(env)\n return {\n async encrypt(plaintext) {\n return { enc: await provider.encrypt(plaintext, STOREFRONT_CREDENTIAL_KEY) }\n },\n async decrypt(envelope) {\n if (!envelope) {\n throw new StorefrontInputError(\"Stored storefront credential is empty.\")\n }\n return provider.decrypt(envelope.enc, STOREFRONT_CREDENTIAL_KEY)\n },\n }\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import type { CustomerAuthRuntimeContext } from "./node-runtime.js";
2
+ import type { StorefrontResolveContext, StorefrontRuntimeProvider } from "./storefront-runtime-port.js";
3
+ /** Default header the storefront BFF uses to declare its browser origin. */
4
+ export declare const STOREFRONT_ORIGIN_HEADER = "x-voyant-storefront-origin";
5
+ /** Default header carrying the storefront's publishable/secret access key. */
6
+ export declare const STOREFRONT_KEY_HEADER = "x-api-key";
7
+ export interface LocalStorefrontCustomerAuthResolverConfig<Env> {
8
+ provider: StorefrontRuntimeProvider;
9
+ /**
10
+ * Open the request-time resolve context (db + bindings) for this runtime.
11
+ * The self-host host wires this to its database lifecycle; `dispose` is
12
+ * always awaited, success or failure.
13
+ */
14
+ openResolveContext: (env: Env, request: Request) => Promise<{
15
+ context: StorefrontResolveContext;
16
+ dispose?: () => Promise<void>;
17
+ }>;
18
+ originHeader?: string;
19
+ keyHeader?: string;
20
+ }
21
+ export declare class StorefrontCustomerAuthResolutionError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ /**
25
+ * Build the `resolveCustomerAuthContext` function a self-host runtime passes to
26
+ * {@link createOperatorAuthNodeRuntime}. Keeps the existing seam intact — this
27
+ * is simply the local (port-backed) option alongside the env default and the
28
+ * managed cloud fetch.
29
+ */
30
+ export declare function createLocalStorefrontCustomerAuthResolver<Env>(config: LocalStorefrontCustomerAuthResolverConfig<Env>): (env: Env, request: Request) => Promise<CustomerAuthRuntimeContext>;
@@ -0,0 +1,72 @@
1
+ import { enabledStorefrontSocialProviders, isStorefrontOriginAllowed, } from "./storefront-origins.js";
2
+ /** Default header the storefront BFF uses to declare its browser origin. */
3
+ export const STOREFRONT_ORIGIN_HEADER = "x-voyant-storefront-origin";
4
+ /** Default header carrying the storefront's publishable/secret access key. */
5
+ export const STOREFRONT_KEY_HEADER = "x-api-key";
6
+ export class StorefrontCustomerAuthResolutionError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "StorefrontCustomerAuthResolutionError";
10
+ }
11
+ }
12
+ function toSocialProviders(secrets) {
13
+ const providers = {};
14
+ for (const [provider, credential] of Object.entries(secrets)) {
15
+ const clientId = credential?.clientId;
16
+ const clientSecret = credential?.clientSecret;
17
+ if (!clientId || !clientSecret)
18
+ continue;
19
+ providers[provider] = { clientId, clientSecret };
20
+ }
21
+ return providers;
22
+ }
23
+ /**
24
+ * Build the `resolveCustomerAuthContext` function a self-host runtime passes to
25
+ * {@link createOperatorAuthNodeRuntime}. Keeps the existing seam intact — this
26
+ * is simply the local (port-backed) option alongside the env default and the
27
+ * managed cloud fetch.
28
+ */
29
+ export function createLocalStorefrontCustomerAuthResolver(config) {
30
+ const originHeader = config.originHeader ?? STOREFRONT_ORIGIN_HEADER;
31
+ const keyHeader = config.keyHeader ?? STOREFRONT_KEY_HEADER;
32
+ return async (env, request) => {
33
+ const origin = request.headers.get(originHeader)?.trim();
34
+ if (!origin) {
35
+ throw new StorefrontCustomerAuthResolutionError(`Local storefront customer auth requires ${originHeader} from the storefront BFF.`);
36
+ }
37
+ const token = request.headers.get(keyHeader)?.trim();
38
+ if (!token) {
39
+ throw new StorefrontCustomerAuthResolutionError(`Local storefront customer auth requires a storefront key (${keyHeader}).`);
40
+ }
41
+ const { context, dispose } = await config.openResolveContext(env, request);
42
+ try {
43
+ const resolved = await config.provider.resolveStorefrontByApiKey(context, token);
44
+ if (!resolved) {
45
+ throw new StorefrontCustomerAuthResolutionError("The presented storefront key is unknown or revoked.");
46
+ }
47
+ const { storefront } = resolved;
48
+ if (!isStorefrontOriginAllowed(origin, storefront.allowedOrigins)) {
49
+ throw new StorefrontCustomerAuthResolutionError("The request origin is not a declared allowed origin for this storefront.");
50
+ }
51
+ const enabledProviders = enabledStorefrontSocialProviders(storefront.methods);
52
+ const secrets = await config.provider.resolveProviderCredentials(context, storefront.id, enabledProviders);
53
+ const methods = {
54
+ emailCode: storefront.methods.emailCode,
55
+ emailPassword: storefront.methods.emailPassword,
56
+ socialProviders: toSocialProviders(secrets),
57
+ };
58
+ return {
59
+ baseURL: origin,
60
+ publicApiBaseURL: `${origin}/api`,
61
+ invitationAcceptBaseURL: origin,
62
+ trustedOrigins: [origin],
63
+ methods,
64
+ accountPolicy: storefront.accountPolicy,
65
+ };
66
+ }
67
+ finally {
68
+ await dispose?.();
69
+ }
70
+ };
71
+ }
72
+ //# sourceMappingURL=storefront-customer-auth-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storefront-customer-auth-resolver.js","sourceRoot":"","sources":["../src/storefront-customer-auth-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local storefront customer-auth resolver.\n *\n * Implements the `resolveCustomerAuthContext` seam of\n * {@link createOperatorAuthNodeRuntime} for a self-host runtime: instead of\n * fetching a merchant policy from Voyant Cloud, it resolves the storefront from\n * the presented access key via {@link storefrontRuntimePort}, enforces the\n * operator-declared origin allowlist, and projects the storefront's methods,\n * trusted origins, buyer-account policy, and decrypted provider secrets into a\n * `CustomerAuthRuntimeContext`.\n *\n * The BFF must forward the storefront origin explicitly (never derived from\n * Host/X-Forwarded-Host) plus the storefront key, exactly like the managed\n * broker.\n */\nimport type { CustomerBuyerAccountPolicy } from \"./customer-buyer-accounts.js\"\nimport type { CustomerAuthRuntimeContext } from \"./node-runtime.js\"\nimport type { CustomerAuthMethods } from \"./server.js\"\nimport {\n enabledStorefrontSocialProviders,\n isStorefrontOriginAllowed,\n} from \"./storefront-origins.js\"\nimport type {\n StorefrontResolveContext,\n StorefrontRuntimeProvider,\n} from \"./storefront-runtime-port.js\"\n\n/** Default header the storefront BFF uses to declare its browser origin. */\nexport const STOREFRONT_ORIGIN_HEADER = \"x-voyant-storefront-origin\"\n/** Default header carrying the storefront's publishable/secret access key. */\nexport const STOREFRONT_KEY_HEADER = \"x-api-key\"\n\nexport interface LocalStorefrontCustomerAuthResolverConfig<Env> {\n provider: StorefrontRuntimeProvider\n /**\n * Open the request-time resolve context (db + bindings) for this runtime.\n * The self-host host wires this to its database lifecycle; `dispose` is\n * always awaited, success or failure.\n */\n openResolveContext: (\n env: Env,\n request: Request,\n ) => Promise<{ context: StorefrontResolveContext; dispose?: () => Promise<void> }>\n originHeader?: string\n keyHeader?: string\n}\n\nexport class StorefrontCustomerAuthResolutionError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"StorefrontCustomerAuthResolutionError\"\n }\n}\n\nfunction toSocialProviders(\n secrets: Partial<Record<\"google\" | \"facebook\" | \"apple\", Record<string, string>>>,\n): CustomerAuthMethods[\"socialProviders\"] {\n const providers: Record<string, { clientId: string; clientSecret: string }> = {}\n for (const [provider, credential] of Object.entries(secrets)) {\n const clientId = credential?.clientId\n const clientSecret = credential?.clientSecret\n if (!clientId || !clientSecret) continue\n providers[provider] = { clientId, clientSecret }\n }\n return providers as CustomerAuthMethods[\"socialProviders\"]\n}\n\n/**\n * Build the `resolveCustomerAuthContext` function a self-host runtime passes to\n * {@link createOperatorAuthNodeRuntime}. Keeps the existing seam intact — this\n * is simply the local (port-backed) option alongside the env default and the\n * managed cloud fetch.\n */\nexport function createLocalStorefrontCustomerAuthResolver<Env>(\n config: LocalStorefrontCustomerAuthResolverConfig<Env>,\n): (env: Env, request: Request) => Promise<CustomerAuthRuntimeContext> {\n const originHeader = config.originHeader ?? STOREFRONT_ORIGIN_HEADER\n const keyHeader = config.keyHeader ?? STOREFRONT_KEY_HEADER\n\n return async (env, request) => {\n const origin = request.headers.get(originHeader)?.trim()\n if (!origin) {\n throw new StorefrontCustomerAuthResolutionError(\n `Local storefront customer auth requires ${originHeader} from the storefront BFF.`,\n )\n }\n const token = request.headers.get(keyHeader)?.trim()\n if (!token) {\n throw new StorefrontCustomerAuthResolutionError(\n `Local storefront customer auth requires a storefront key (${keyHeader}).`,\n )\n }\n\n const { context, dispose } = await config.openResolveContext(env, request)\n try {\n const resolved = await config.provider.resolveStorefrontByApiKey(context, token)\n if (!resolved) {\n throw new StorefrontCustomerAuthResolutionError(\n \"The presented storefront key is unknown or revoked.\",\n )\n }\n const { storefront } = resolved\n if (!isStorefrontOriginAllowed(origin, storefront.allowedOrigins)) {\n throw new StorefrontCustomerAuthResolutionError(\n \"The request origin is not a declared allowed origin for this storefront.\",\n )\n }\n\n const enabledProviders = enabledStorefrontSocialProviders(storefront.methods)\n const secrets = await config.provider.resolveProviderCredentials(\n context,\n storefront.id,\n enabledProviders,\n )\n\n const methods: CustomerAuthMethods = {\n emailCode: storefront.methods.emailCode,\n emailPassword: storefront.methods.emailPassword,\n socialProviders: toSocialProviders(secrets),\n }\n\n return {\n baseURL: origin,\n publicApiBaseURL: `${origin}/api`,\n invitationAcceptBaseURL: origin,\n trustedOrigins: [origin],\n methods,\n accountPolicy: storefront.accountPolicy as CustomerBuyerAccountPolicy,\n }\n } finally {\n await dispose?.()\n }\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Storefront access-key helpers.
3
+ *
4
+ * A storefront access key is an opaque bearer token with a kind-specific
5
+ * prefix. Only the SHA-256 hash of the token is ever persisted; the plaintext
6
+ * is returned exactly once at issuance (reveal-once) and can never be
7
+ * re-derived. `tokenPreview` is a short, non-secret display fragment so an
8
+ * operator can recognise a key in a list without revealing it.
9
+ *
10
+ * - `vpk_` publishable — safe to embed in a browser bundle or native app;
11
+ * authorizes public read + customer-auth initiation from a declared origin.
12
+ * - `vsk_` secret — server-only (SSR/BFF); carries full storefront trust.
13
+ *
14
+ * Runtime-agnostic: uses only Web Crypto (`crypto.getRandomValues`,
15
+ * `crypto.subtle`), which is available in Node, workerd, and the browser.
16
+ */
17
+ import type { StorefrontApiKeyKind } from "@voyant-travel/db/schema/iam";
18
+ export interface GeneratedStorefrontApiKey {
19
+ kind: StorefrontApiKeyKind;
20
+ /** Full plaintext token — shown once, never stored. */
21
+ token: string;
22
+ /** SHA-256 hex digest of `token`; the only value persisted for lookup. */
23
+ tokenHash: string;
24
+ /** Non-secret display fragment, e.g. "vpk_ab12cd". */
25
+ tokenPreview: string;
26
+ }
27
+ /** SHA-256 hex digest of an access token. */
28
+ export declare function hashStorefrontApiKey(token: string): Promise<string>;
29
+ /**
30
+ * Classify a presented token by its prefix. Returns null when the token has no
31
+ * recognised storefront-key prefix, letting callers reject obviously-invalid
32
+ * tokens before hitting the database.
33
+ */
34
+ export declare function classifyStorefrontApiKey(token: string): StorefrontApiKeyKind | null;
35
+ /**
36
+ * Mint a new storefront access key. The returned `token` is the only time the
37
+ * plaintext exists; persist `tokenHash`/`tokenPreview` and surface `token` to
38
+ * the operator exactly once.
39
+ */
40
+ export declare function generateStorefrontApiKey(kind: StorefrontApiKeyKind): Promise<GeneratedStorefrontApiKey>;
@@ -0,0 +1,50 @@
1
+ const KEY_PREFIXES = {
2
+ publishable: "vpk_",
3
+ secret: "vsk_",
4
+ };
5
+ /** Bytes of entropy in the random portion of a key (256 bits). */
6
+ const KEY_RANDOM_BYTES = 32;
7
+ function base64UrlFromBytes(bytes) {
8
+ let binary = "";
9
+ for (const byte of bytes)
10
+ binary += String.fromCharCode(byte);
11
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
12
+ }
13
+ /** SHA-256 hex digest of an access token. */
14
+ export async function hashStorefrontApiKey(token) {
15
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
16
+ return Array.from(new Uint8Array(digest))
17
+ .map((byte) => byte.toString(16).padStart(2, "0"))
18
+ .join("");
19
+ }
20
+ /**
21
+ * Classify a presented token by its prefix. Returns null when the token has no
22
+ * recognised storefront-key prefix, letting callers reject obviously-invalid
23
+ * tokens before hitting the database.
24
+ */
25
+ export function classifyStorefrontApiKey(token) {
26
+ for (const kind of Object.keys(KEY_PREFIXES)) {
27
+ if (token.startsWith(KEY_PREFIXES[kind]))
28
+ return kind;
29
+ }
30
+ return null;
31
+ }
32
+ /**
33
+ * Mint a new storefront access key. The returned `token` is the only time the
34
+ * plaintext exists; persist `tokenHash`/`tokenPreview` and surface `token` to
35
+ * the operator exactly once.
36
+ */
37
+ export async function generateStorefrontApiKey(kind) {
38
+ const random = new Uint8Array(KEY_RANDOM_BYTES);
39
+ crypto.getRandomValues(random);
40
+ const token = `${KEY_PREFIXES[kind]}${base64UrlFromBytes(random)}`;
41
+ return {
42
+ kind,
43
+ token,
44
+ tokenHash: await hashStorefrontApiKey(token),
45
+ // Prefix (4 chars) + first 6 chars of the random body: enough to
46
+ // disambiguate in a list, far too little to guess the token.
47
+ tokenPreview: token.slice(0, KEY_PREFIXES[kind].length + 6),
48
+ };
49
+ }
50
+ //# sourceMappingURL=storefront-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storefront-keys.js","sourceRoot":"","sources":["../src/storefront-keys.ts"],"names":[],"mappings":"AAkBA,MAAM,YAAY,GAAG;IACnB,WAAW,EAAE,MAAM;IACnB,MAAM,EAAE,MAAM;CACyC,CAAA;AAEzD,kEAAkE;AAClE,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAY3B,SAAS,kBAAkB,CAAC,KAAiB;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAChF,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAAa;IACtD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IACrF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAA2B,EAAE,CAAC;QACvE,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;IACvD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAA0B;IAE1B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAA;IAC/C,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAA;IAClE,OAAO;QACL,IAAI;QACJ,KAAK;QACL,SAAS,EAAE,MAAM,oBAAoB,CAAC,KAAK,CAAC;QAC5C,iEAAiE;QACjE,6DAA6D;QAC7D,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;KAC5D,CAAA;AACH,CAAC","sourcesContent":["/**\n * Storefront access-key helpers.\n *\n * A storefront access key is an opaque bearer token with a kind-specific\n * prefix. Only the SHA-256 hash of the token is ever persisted; the plaintext\n * is returned exactly once at issuance (reveal-once) and can never be\n * re-derived. `tokenPreview` is a short, non-secret display fragment so an\n * operator can recognise a key in a list without revealing it.\n *\n * - `vpk_` publishable — safe to embed in a browser bundle or native app;\n * authorizes public read + customer-auth initiation from a declared origin.\n * - `vsk_` secret — server-only (SSR/BFF); carries full storefront trust.\n *\n * Runtime-agnostic: uses only Web Crypto (`crypto.getRandomValues`,\n * `crypto.subtle`), which is available in Node, workerd, and the browser.\n */\nimport type { StorefrontApiKeyKind } from \"@voyant-travel/db/schema/iam\"\n\nconst KEY_PREFIXES = {\n publishable: \"vpk_\",\n secret: \"vsk_\",\n} as const satisfies Record<StorefrontApiKeyKind, string>\n\n/** Bytes of entropy in the random portion of a key (256 bits). */\nconst KEY_RANDOM_BYTES = 32\n\nexport interface GeneratedStorefrontApiKey {\n kind: StorefrontApiKeyKind\n /** Full plaintext token — shown once, never stored. */\n token: string\n /** SHA-256 hex digest of `token`; the only value persisted for lookup. */\n tokenHash: string\n /** Non-secret display fragment, e.g. \"vpk_ab12cd\". */\n tokenPreview: string\n}\n\nfunction base64UrlFromBytes(bytes: Uint8Array): string {\n let binary = \"\"\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\")\n}\n\n/** SHA-256 hex digest of an access token. */\nexport async function hashStorefrontApiKey(token: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(token))\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\")\n}\n\n/**\n * Classify a presented token by its prefix. Returns null when the token has no\n * recognised storefront-key prefix, letting callers reject obviously-invalid\n * tokens before hitting the database.\n */\nexport function classifyStorefrontApiKey(token: string): StorefrontApiKeyKind | null {\n for (const kind of Object.keys(KEY_PREFIXES) as StorefrontApiKeyKind[]) {\n if (token.startsWith(KEY_PREFIXES[kind])) return kind\n }\n return null\n}\n\n/**\n * Mint a new storefront access key. The returned `token` is the only time the\n * plaintext exists; persist `tokenHash`/`tokenPreview` and surface `token` to\n * the operator exactly once.\n */\nexport async function generateStorefrontApiKey(\n kind: StorefrontApiKeyKind,\n): Promise<GeneratedStorefrontApiKey> {\n const random = new Uint8Array(KEY_RANDOM_BYTES)\n crypto.getRandomValues(random)\n const token = `${KEY_PREFIXES[kind]}${base64UrlFromBytes(random)}`\n return {\n kind,\n token,\n tokenHash: await hashStorefrontApiKey(token),\n // Prefix (4 chars) + first 6 chars of the random body: enough to\n // disambiguate in a list, far too little to guess the token.\n tokenPreview: token.slice(0, KEY_PREFIXES[kind].length + 6),\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { type StorefrontCredentialCipher } from "./storefront-credentials.js";
2
+ import type { StorefrontRuntimeProvider } from "./storefront-runtime-port.js";
3
+ export { isStorefrontOriginAllowed } from "./storefront-origins.js";
4
+ export declare function createLocalStorefrontAdapter(options: {
5
+ /**
6
+ * Resolve the KMS-backed credential cipher for a deployment. Called with the
7
+ * request bindings so per-deployment KMS config is honoured; the self-host
8
+ * host passes `createKmsStorefrontCredentialCipher(env)`.
9
+ */
10
+ resolveCipher: (bindings: Record<string, unknown>) => StorefrontCredentialCipher;
11
+ }): StorefrontRuntimeProvider;