@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.
- package/dist/customer-business-accounts-contracts.d.ts +159 -0
- package/dist/customer-business-accounts-contracts.js +112 -0
- package/dist/customer-business-accounts-contracts.js.map +1 -0
- package/dist/customer-business-onboarding-graph-runtime.d.ts +13 -0
- package/dist/customer-business-onboarding-graph-runtime.js +8 -0
- package/dist/customer-business-onboarding-graph-runtime.js.map +1 -0
- package/dist/customer-business-onboarding-routes.d.ts +13 -0
- package/dist/customer-business-onboarding-routes.js +151 -0
- package/dist/customer-business-onboarding-routes.js.map +1 -0
- package/dist/customer-business-onboarding-runtime-port.d.ts +40 -0
- package/dist/customer-business-onboarding-runtime-port.js +25 -0
- package/dist/customer-business-onboarding-runtime-port.js.map +1 -0
- package/dist/customer-business-onboarding-service.d.ts +32 -0
- package/dist/customer-business-onboarding-service.js +237 -0
- package/dist/customer-business-onboarding-service.js.map +1 -0
- package/dist/customer-buyer-accounts.d.ts +0 -19
- package/dist/customer-buyer-accounts.js.map +1 -1
- package/dist/node-runtime.d.ts +13 -0
- package/dist/node-runtime.js +285 -1
- package/dist/node-runtime.js.map +1 -1
- package/dist/ports.d.ts +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/runtime-contributor.js +11 -0
- package/dist/runtime-contributor.js.map +1 -1
- package/dist/server.d.ts +39 -3
- package/dist/server.js.map +1 -1
- package/dist/storefront-credentials.d.ts +31 -0
- package/dist/storefront-credentials.js +54 -0
- package/dist/storefront-credentials.js.map +1 -0
- package/dist/storefront-customer-auth-resolver.d.ts +30 -0
- package/dist/storefront-customer-auth-resolver.js +72 -0
- package/dist/storefront-customer-auth-resolver.js.map +1 -0
- package/dist/storefront-keys.d.ts +40 -0
- package/dist/storefront-keys.js +50 -0
- package/dist/storefront-keys.js.map +1 -0
- package/dist/storefront-local-adapter.d.ts +11 -0
- package/dist/storefront-local-adapter.js +313 -0
- package/dist/storefront-local-adapter.js.map +1 -0
- package/dist/storefront-origins.d.ts +34 -0
- package/dist/storefront-origins.js +132 -0
- package/dist/storefront-origins.js.map +1 -0
- package/dist/storefront-runtime-port.d.ts +93 -0
- package/dist/storefront-runtime-port.js +35 -0
- package/dist/storefront-runtime-port.js.map +1 -0
- package/dist/voyant.d.ts +2 -0
- package/dist/voyant.js +99 -0
- package/dist/voyant.js.map +1 -1
- package/openapi/admin/customer-business-accounts.json +44 -0
- 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;
|
package/dist/server.js.map
CHANGED
|
@@ -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;
|