@invect/user-auth 0.0.7 → 0.0.8
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/backend/index.cjs +2 -5
- package/dist/backend/index.cjs.map +1 -1
- package/dist/backend/index.d.cts +0 -9
- package/dist/backend/index.d.cts.map +1 -1
- package/dist/backend/index.d.mts +0 -9
- package/dist/backend/index.d.mts.map +1 -1
- package/dist/backend/index.mjs +2 -5
- package/dist/backend/index.mjs.map +1 -1
- package/dist/backend/plugin.d.ts.map +1 -1
- package/dist/backend/types.d.ts +0 -9
- package/dist/backend/types.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/backend/index.cjs
CHANGED
|
@@ -102,16 +102,13 @@ async function resolveSession(auth, headers) {
|
|
|
102
102
|
if (!auth) return null;
|
|
103
103
|
const h = toHeaders(headers);
|
|
104
104
|
try {
|
|
105
|
-
console.log("[auth-debug] resolveSession: cookie header =", h.get("cookie")?.slice(0, 80));
|
|
106
105
|
const result = await auth.api.getSession({ headers: h });
|
|
107
|
-
console.log("[auth-debug] resolveSession: result keys =", result ? Object.keys(result) : "null");
|
|
108
106
|
if (result?.session && result?.user) return {
|
|
109
107
|
session: result.session,
|
|
110
108
|
user: result.user
|
|
111
109
|
};
|
|
112
110
|
return null;
|
|
113
111
|
} catch (err) {
|
|
114
|
-
console.error("[auth-debug] resolveSession threw:", err?.message ?? err);
|
|
115
112
|
return null;
|
|
116
113
|
}
|
|
117
114
|
}
|
|
@@ -775,7 +772,7 @@ async function createMySQLPool(connectionString) {
|
|
|
775
772
|
* ```
|
|
776
773
|
*/
|
|
777
774
|
function authentication(options) {
|
|
778
|
-
const { prefix = DEFAULT_PREFIX, mapUser: customMapUser, mapRole = defaultMapRole, publicPaths = [],
|
|
775
|
+
const { prefix = DEFAULT_PREFIX, mapUser: customMapUser, mapRole = defaultMapRole, publicPaths = [], globalAdmins = [] } = options;
|
|
779
776
|
let auth = options.auth ?? null;
|
|
780
777
|
/** Narrow `auth` for call-sites that run only after init. */
|
|
781
778
|
function requireAuth() {
|
|
@@ -1386,7 +1383,7 @@ function authentication(options) {
|
|
|
1386
1383
|
role: identity?.role
|
|
1387
1384
|
});
|
|
1388
1385
|
context.identity = identity;
|
|
1389
|
-
if (!identity
|
|
1386
|
+
if (!identity) return { response: new Response(JSON.stringify({
|
|
1390
1387
|
error: "Unauthorized",
|
|
1391
1388
|
message: "Valid session required. Sign in via better-auth."
|
|
1392
1389
|
}), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["AUTH_DEFAULT_ROLE","isAuthVisibleRole","AUTH_ADMIN_ROLE","AUTH_VISIBLE_ROLES","isAuthAssignableRole","AUTH_ASSIGNABLE_ROLES"],"sources":["../../src/backend/plugin.ts","../../src/backend/index.ts"],"sourcesContent":["import type {\n InvectPlugin,\n InvectIdentity,\n InvectRole,\n InvectPermission,\n InvectPluginSchema,\n} from '@invect/core';\nimport type {\n AuthenticationPluginOptions,\n BetterAuthContext,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthInstance,\n} from './types';\nimport {\n AUTH_ADMIN_ROLE,\n AUTH_ASSIGNABLE_ROLES,\n AUTH_DEFAULT_ROLE,\n AUTH_VISIBLE_ROLES,\n isAuthAssignableRole,\n isAuthVisibleRole,\n} from '../shared/roles';\n\ntype PluginLoggerLike = {\n error: (message: string, meta?: unknown) => void;\n info?: (message: string, meta?: unknown) => void;\n debug?: (message: string, meta?: unknown) => void;\n warn?: (message: string, meta?: unknown) => void;\n};\n\ntype BetterAuthApiUser = {\n id?: string;\n email?: string | null;\n name?: string | null;\n role?: string | null;\n [key: string]: unknown;\n};\n\ntype BetterAuthApiUserResult = {\n user?: BetterAuthApiUser | null;\n};\n\ntype BetterAuthHeadersQueryMethod<TResult> = (args: {\n headers: Headers;\n query: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyParamsMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n params: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthBodyMethod<TResult> = (args: { body: Record<string, unknown> }) => Promise<TResult>;\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PREFIX = 'auth';\n\n/**\n * Default role mapping: keep admin/RBAC roles aligned and fall back to default.\n */\nfunction defaultMapRole(role: string | null | undefined): InvectRole {\n if (!role) {\n return AUTH_DEFAULT_ROLE;\n }\n if (role === 'viewer' || role === 'readonly') {\n return 'viewer';\n }\n if (isAuthVisibleRole(role)) {\n return role;\n }\n return AUTH_DEFAULT_ROLE;\n}\n\n/**\n * Default user → identity mapping.\n */\nfunction defaultMapUser(\n user: BetterAuthUser,\n _session: BetterAuthSession,\n mapRole: (role: string | null | undefined) => InvectRole,\n): InvectIdentity {\n const resolvedRole = mapRole(user.role);\n\n return {\n id: user.id,\n name: user.name ?? user.email ?? undefined,\n role: resolvedRole,\n permissions: resolvedRole === AUTH_ADMIN_ROLE ? ['admin:*'] : undefined,\n resourceAccess:\n resolvedRole === AUTH_ADMIN_ROLE\n ? {\n flows: '*',\n credentials: '*',\n }\n : undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Simple in-memory sliding-window rate limiter.\n *\n * Keyed by IP address (or a fallback identifier). Tracks request timestamps\n * per window and rejects requests that exceed the limit with HTTP 429.\n *\n * Only applied to authentication-sensitive endpoints (sign-in, sign-up,\n * password reset) to prevent brute-force attacks. Session reads (GET) are\n * not rate-limited.\n */\nclass RateLimiter {\n private windows = new Map<string, number[]>();\n private readonly maxRequests: number;\n private readonly windowMs: number;\n\n constructor(maxRequests = 10, windowMs = 60_000) {\n this.maxRequests = maxRequests;\n this.windowMs = windowMs;\n }\n\n /**\n * Returns `true` if the request should be rejected (over limit).\n */\n isRateLimited(key: string): { limited: boolean; retryAfterMs?: number } {\n const now = Date.now();\n const windowStart = now - this.windowMs;\n\n let timestamps = this.windows.get(key);\n if (!timestamps) {\n timestamps = [];\n this.windows.set(key, timestamps);\n }\n\n // Prune expired entries\n const valid = timestamps.filter((t) => t > windowStart);\n this.windows.set(key, valid);\n\n if (valid.length >= this.maxRequests) {\n const oldestInWindow = valid[0] ?? now;\n const retryAfterMs = oldestInWindow + this.windowMs - now;\n return { limited: true, retryAfterMs: Math.max(retryAfterMs, 1000) };\n }\n\n valid.push(now);\n return { limited: false };\n }\n\n /** Periodic cleanup of stale keys to prevent memory leaks. */\n cleanup(): void {\n const now = Date.now();\n for (const [key, timestamps] of this.windows) {\n const valid = timestamps.filter((t) => t > now - this.windowMs);\n if (valid.length === 0) {\n this.windows.delete(key);\n } else {\n this.windows.set(key, valid);\n }\n }\n }\n}\n\n/** Auth-sensitive path segments that should be rate-limited. */\nconst RATE_LIMITED_AUTH_PATHS = ['/sign-in/', '/sign-up/', '/forgot-password', '/reset-password'];\n\n/**\n * Convert a Node.js-style `IncomingHttpHeaders` record or a `Headers` instance\n * to a standard `Headers` object for passing to better-auth.\n */\nfunction toHeaders(raw: Record<string, string | undefined> | Headers): Headers {\n if (raw instanceof Headers) {\n return raw;\n }\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(raw)) {\n if (value !== undefined) {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * Resolve the session from a better-auth instance using request headers.\n */\nasync function resolveSession(\n auth: BetterAuthInstance | null,\n headers: Record<string, string | undefined> | Headers,\n): Promise<{ session: BetterAuthSession; user: BetterAuthUser } | null> {\n if (!auth) {\n return null;\n }\n\n const h = toHeaders(headers);\n\n try {\n // eslint-disable-next-line no-console\n console.log('[auth-debug] resolveSession: cookie header =', h.get('cookie')?.slice(0, 80));\n const result = await auth.api.getSession({\n headers: h,\n });\n // eslint-disable-next-line no-console\n console.log(\n '[auth-debug] resolveSession: result keys =',\n result ? Object.keys(result) : 'null',\n );\n if (result?.session && result?.user) {\n return {\n session: result.session,\n user: result.user,\n };\n }\n return null;\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[auth-debug] resolveSession threw:', (err as Error)?.message ?? err);\n return null;\n }\n}\n\nasync function callBetterAuthHandler(\n auth: BetterAuthInstance | null,\n request: Request,\n path: string,\n init?: {\n method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n query?: Record<string, string | undefined>;\n body?: Record<string, unknown>;\n },\n): Promise<{ status: number; body: unknown } | null> {\n if (!auth) {\n return null;\n }\n\n const basePath = auth.options?.basePath ?? '/api/auth';\n const targetUrl = new URL(`${basePath}${path}`, request.url);\n for (const [key, value] of Object.entries(init?.query ?? {})) {\n if (value !== undefined) {\n targetUrl.searchParams.set(key, value);\n }\n }\n\n const headers = new Headers(request.headers);\n const hasBody = init?.body !== undefined;\n if (hasBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n const authRequest = new Request(targetUrl.toString(), {\n method: init?.method ?? 'GET',\n headers,\n body: hasBody ? JSON.stringify(init?.body) : undefined,\n });\n\n const response = await auth.handler(authRequest);\n const text = await response.text();\n\n if (!text) {\n return { status: response.status, body: null };\n }\n\n try {\n return { status: response.status, body: JSON.parse(text) };\n } catch {\n return { status: response.status, body: text };\n }\n}\n\nasync function getAuthContext(auth: BetterAuthInstance | null): Promise<BetterAuthContext | null> {\n if (!auth) {\n return null;\n }\n try {\n return (await auth.$context) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction isBetterAuthUser(value: unknown): value is BetterAuthUser {\n return !!value && typeof value === 'object' && 'id' in value && typeof value.id === 'string';\n}\n\nfunction unwrapFoundUser(\n result: BetterAuthUser | { user?: BetterAuthUser | null } | null | undefined,\n): BetterAuthUser | null {\n if (!result) {\n return null;\n }\n\n if (typeof result === 'object' && 'user' in result) {\n const nestedUser = result.user;\n if (isBetterAuthUser(nestedUser)) {\n return nestedUser;\n }\n\n return null;\n }\n\n if (isBetterAuthUser(result)) {\n return result;\n }\n\n return null;\n}\n\nfunction toAuthApiErrorResponse(\n fallbackError: string,\n error: unknown,\n): { status: number; body: Record<string, unknown> } {\n if (error instanceof Response) {\n return {\n status: error.status || 500,\n body: { error: fallbackError, message: error.statusText || fallbackError },\n };\n }\n\n const status =\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status?: unknown }).status === 'number'\n ? ((error as { status: number }).status ?? 500)\n : error &&\n typeof error === 'object' &&\n 'statusCode' in error &&\n typeof (error as { statusCode?: unknown }).statusCode === 'number'\n ? ((error as { statusCode: number }).statusCode ?? 500)\n : 500;\n\n const message =\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ? (error as { message: string }).message || fallbackError\n : fallbackError;\n\n const code =\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n typeof (error as { code?: unknown }).code === 'string'\n ? (error as { code: string }).code\n : undefined;\n\n return {\n status,\n body: {\n error: fallbackError,\n message,\n ...(code ? { code } : {}),\n },\n };\n}\n\nfunction sanitizeForLogging(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeForLogging(item));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, nestedValue]) => {\n if (/password|token|secret/i.test(key)) {\n return [key, '[REDACTED]'];\n }\n\n return [key, sanitizeForLogging(nestedValue)];\n }),\n );\n }\n\n return value;\n}\n\nfunction getErrorLogDetails(error: unknown): Record<string, unknown> {\n if (error instanceof Response) {\n return {\n type: 'Response',\n status: error.status,\n statusText: error.statusText,\n };\n }\n\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n ...(error && typeof error === 'object' && 'cause' in error\n ? { cause: sanitizeForLogging((error as Error & { cause?: unknown }).cause) }\n : {}),\n ...(error && typeof error === 'object' && 'code' in error\n ? { code: (error as Error & { code?: unknown }).code }\n : {}),\n ...(error && typeof error === 'object' && 'status' in error\n ? { status: (error as Error & { status?: unknown }).status }\n : {}),\n ...(error && typeof error === 'object' && 'statusCode' in error\n ? { statusCode: (error as Error & { statusCode?: unknown }).statusCode }\n : {}),\n };\n }\n\n if (error && typeof error === 'object') {\n return sanitizeForLogging(error) as Record<string, unknown>;\n }\n\n return { value: error };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract schema — defines the tables the user-auth plugin requires\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract schema for the user-auth plugin's database tables.\n *\n * These definitions allow the Invect CLI (`npx invect-cli generate`) to include\n * the auth tables when generating Drizzle/Prisma schema files.\n *\n * The shapes match Better Auth's default table structure. If your Better Auth\n * config adds extra fields (e.g., via plugins like `twoFactor`, `organization`),\n * you can extend these in your own config.\n */\nexport const USER_AUTH_SCHEMA: InvectPluginSchema = {\n user: {\n tableName: 'user',\n order: 1,\n fields: {\n id: { type: 'string', primaryKey: true },\n name: { type: 'string', required: true },\n email: { type: 'string', required: true, unique: true },\n emailVerified: { type: 'boolean', required: true, defaultValue: false },\n image: { type: 'string', required: false },\n role: { type: 'string', required: false, defaultValue: AUTH_DEFAULT_ROLE },\n banned: { type: 'boolean', required: false, defaultValue: false },\n banReason: { type: 'string', required: false },\n banExpires: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n session: {\n tableName: 'session',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n expiresAt: { type: 'date', required: true },\n token: { type: 'string', required: true, unique: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n ipAddress: { type: 'string', required: false },\n userAgent: { type: 'string', required: false },\n impersonatedBy: { type: 'string', required: false },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n },\n },\n\n account: {\n tableName: 'account',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n accountId: { type: 'string', required: true },\n providerId: { type: 'string', required: true },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n accessToken: { type: 'string', required: false },\n refreshToken: { type: 'string', required: false },\n idToken: { type: 'string', required: false },\n accessTokenExpiresAt: { type: 'date', required: false },\n refreshTokenExpiresAt: { type: 'date', required: false },\n scope: { type: 'string', required: false },\n password: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n verification: {\n tableName: 'verification',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n identifier: { type: 'string', required: true },\n value: { type: 'string', required: true },\n expiresAt: { type: 'date', required: true },\n createdAt: { type: 'date', required: false },\n updatedAt: { type: 'date', required: false },\n },\n },\n\n // ----- Flow access control table (moved from core) -----\n flowAccess: {\n tableName: 'flow_access',\n order: 3,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n },\n userId: { type: 'string', required: false },\n teamId: { type: 'string', required: false },\n permission: {\n type: 'string',\n required: true,\n defaultValue: 'viewer',\n },\n grantedBy: { type: 'string', required: false },\n grantedAt: { type: 'date', required: true, defaultValue: 'now()' },\n expiresAt: { type: 'date', required: false },\n },\n },\n};\n\n/**\n * Abstract schema for the Better Auth API Key plugin's `apikey` table.\n *\n * Only merged into the plugin schema when `apiKey` is enabled.\n *\n * @see https://better-auth.com/docs/plugins/api-key/reference#schema\n */\nconst API_KEY_SCHEMA: InvectPluginSchema = {\n apikey: {\n tableName: 'apikey',\n order: 3,\n fields: {\n id: { type: 'string', primaryKey: true },\n configId: { type: 'string', required: true, defaultValue: 'default' },\n name: { type: 'string', required: false },\n start: { type: 'string', required: false },\n prefix: { type: 'string', required: false },\n key: { type: 'string', required: true },\n referenceId: { type: 'string', required: true },\n refillInterval: { type: 'number', required: false },\n refillAmount: { type: 'number', required: false },\n lastRefillAt: { type: 'date', required: false },\n enabled: { type: 'boolean', required: false, defaultValue: true },\n rateLimitEnabled: { type: 'boolean', required: false },\n rateLimitTimeWindow: { type: 'number', required: false },\n rateLimitMax: { type: 'number', required: false },\n requestCount: { type: 'number', required: false },\n remaining: { type: 'number', required: false },\n lastRequest: { type: 'date', required: false },\n expiresAt: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n permissions: { type: 'string', required: false },\n metadata: { type: 'string', required: false },\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Internal Better Auth instance creation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Better Auth instance internally using Invect's database config.\n *\n * Dynamically imports `better-auth` (a required peer dependency) and creates\n * a fully-configured instance with email/password auth, the admin plugin,\n * and session caching.\n *\n * Database resolution order:\n * 1. Explicit `options.database` (any value `betterAuth({ database })` accepts)\n * 2. Auto-created client from Invect's `database.connectionString`\n */\nasync function createInternalBetterAuth(\n invectConfig: Record<string, unknown>,\n options: AuthenticationPluginOptions,\n logger: PluginLoggerLike,\n): Promise<BetterAuthInstance> {\n // 1. Dynamic-import better-auth (peer dependency)\n let betterAuthFn: (config: Record<string, unknown>) => unknown;\n let adminPlugin: (config?: Record<string, unknown>) => unknown;\n\n try {\n const betterAuthModule = await import('better-auth');\n betterAuthFn = betterAuthModule.betterAuth;\n } catch {\n throw new Error(\n 'Could not import \"better-auth\". It is a required peer dependency of @invect/user-auth. ' +\n 'Install it with: npm install better-auth',\n );\n }\n\n try {\n const pluginsModule = await import('better-auth/plugins');\n adminPlugin = pluginsModule.admin;\n } catch {\n throw new Error(\n 'Could not import \"better-auth/plugins\". Ensure better-auth is properly installed.',\n );\n }\n\n // 2. Resolve database\n let database: unknown = options.database;\n\n if (!database) {\n const dbConfig = invectConfig.database as\n | { type?: string; connectionString?: string }\n | undefined;\n\n if (!dbConfig?.connectionString) {\n throw new Error(\n 'Cannot create internal Better Auth instance: no database configuration found. ' +\n 'Either provide `auth` (a Better Auth instance), `database`, or ensure ' +\n 'Invect database has a connectionString.',\n );\n }\n\n const connStr = dbConfig.connectionString;\n const dbType = (dbConfig.type ?? 'sqlite').toLowerCase();\n\n if (dbType === 'sqlite') {\n database = await createSQLiteClient(connStr, logger);\n } else if (dbType === 'pg' || dbType === 'postgresql') {\n database = await createPostgresPool(connStr);\n } else if (dbType === 'mysql') {\n database = await createMySQLPool(connStr);\n } else {\n throw new Error(\n `Unsupported database type for internal Better Auth: \"${dbType}\". ` +\n 'Supported: sqlite, pg, mysql. Alternatively, provide your own Better Auth instance via `auth`.',\n );\n }\n }\n\n // 3. Resolve base URL\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n `http://localhost:${process.env.PORT ?? '3000'}`;\n\n // 4. Resolve trusted origins\n const configuredOrigins = options.trustedOrigins;\n const trustedOrigins =\n configuredOrigins ??\n ((request: Request) => {\n const trusted = new Set<string>([baseURL, 'http://localhost:5173', 'http://localhost:5174']);\n try {\n if (request) {\n trusted.add(new URL(request.url).origin);\n }\n } catch {\n // Ignore malformed URLs\n }\n return Array.from(trusted);\n });\n\n // 5. Build passthrough config from betterAuthOptions\n const passthrough = options.betterAuthOptions ?? {};\n\n const emailAndPassword = {\n enabled: true,\n ...passthrough.emailAndPassword,\n };\n\n const session = {\n cookieCache: { enabled: true, maxAge: 5 * 60 },\n ...passthrough.session,\n // Merge cookieCache if both exist\n ...(passthrough.session?.cookieCache\n ? {\n cookieCache: {\n enabled: true,\n maxAge: 5 * 60,\n ...passthrough.session.cookieCache,\n },\n }\n : {}),\n };\n\n // 6. Resolve secret — fall back to INVECT_ENCRYPTION_KEY when no explicit\n // secret or secrets are provided. This lets deployments that already set\n // INVECT_ENCRYPTION_KEY get a valid Better Auth secret for free.\n let resolvedSecret = passthrough.secret;\n const resolvedSecrets = passthrough.secrets;\n\n if (!resolvedSecret && !resolvedSecrets) {\n const envKey = process.env.INVECT_ENCRYPTION_KEY;\n if (envKey) {\n resolvedSecret = envKey;\n logger.debug?.(\n 'Using INVECT_ENCRYPTION_KEY as Better Auth secret (no explicit secret/secrets provided)',\n );\n }\n }\n\n // 7. Optionally load the Better Auth API Key plugin\n // Merge top-level `options.apiKey` with `passthrough.apiKey` (top-level wins).\n const apiKeyOpt = options.apiKey ?? passthrough.apiKey;\n const betterAuthPlugins: unknown[] = [\n adminPlugin({ defaultRole: AUTH_DEFAULT_ROLE, adminRoles: [AUTH_ADMIN_ROLE] }),\n ];\n\n if (apiKeyOpt) {\n let apiKeyPluginFn: (config?: Record<string, unknown>) => unknown;\n try {\n // Dynamic import — @better-auth/api-key is an optional peer dependency.\n // Using a variable to prevent TypeScript from resolving the module at\n // type-check time (it may not be installed).\n const pkg = '@better-auth/api-key';\n const apiKeyModule: Record<string, unknown> = await import(pkg);\n apiKeyPluginFn = apiKeyModule.apiKey as (config?: Record<string, unknown>) => unknown;\n } catch {\n throw new Error(\n 'Could not import \"@better-auth/api-key\". Install it with: npm install @better-auth/api-key',\n );\n }\n\n const apiKeyConfig: Record<string, unknown> =\n typeof apiKeyOpt === 'object' ? { ...apiKeyOpt } : {};\n // Default API key header to \"x-invect-token\" (overridable via options).\n if (apiKeyConfig.apiKeyHeaders === undefined) {\n apiKeyConfig.apiKeyHeaders = 'x-invect-token';\n }\n // Ensure getSession() can resolve API keys to sessions.\n // Without this, auth.api.getSession() only checks cookies/tokens.\n if (apiKeyConfig.enableSessionForAPIKeys === undefined) {\n apiKeyConfig.enableSessionForAPIKeys = true;\n }\n betterAuthPlugins.push(apiKeyPluginFn(apiKeyConfig));\n logger.info?.('Better Auth API Key plugin enabled');\n }\n\n // 8. Create the instance\n logger.info?.('Creating internal Better Auth instance');\n\n const instance = betterAuthFn({\n baseURL,\n database,\n emailAndPassword,\n plugins: betterAuthPlugins,\n session,\n trustedOrigins,\n // Spread optional passthrough fields\n ...(passthrough.socialProviders ? { socialProviders: passthrough.socialProviders } : {}),\n ...(passthrough.account ? { account: passthrough.account } : {}),\n ...(passthrough.rateLimit ? { rateLimit: passthrough.rateLimit } : {}),\n // Default cookie prefix to \"invect\" (cookies become invect.session_token).\n // Users can override via betterAuthOptions.advanced.cookiePrefix.\n advanced: {\n cookiePrefix: 'invect',\n ...passthrough.advanced,\n },\n ...(passthrough.databaseHooks ? { databaseHooks: passthrough.databaseHooks } : {}),\n ...(passthrough.hooks ? { hooks: passthrough.hooks } : {}),\n ...(passthrough.disabledPaths ? { disabledPaths: passthrough.disabledPaths } : {}),\n ...(resolvedSecret ? { secret: resolvedSecret } : {}),\n ...(resolvedSecrets ? { secrets: resolvedSecrets } : {}),\n });\n\n return instance as BetterAuthInstance;\n}\n\n/** Create a SQLite client using better-sqlite3. */\nasync function createSQLiteClient(connectionString: string, logger: PluginLoggerLike) {\n try {\n const { default: Database } = await import('better-sqlite3');\n const { Kysely, SqliteDialect, CamelCasePlugin } = await import('kysely');\n logger.debug?.(`Using better-sqlite3 for internal Better Auth database`);\n // Strip file: prefix if present\n let dbPath = connectionString.replace(/^file:/, '');\n if (dbPath === '') {\n dbPath = ':memory:';\n }\n const sqliteDb = new Database(dbPath);\n // Return in Better Auth's { db, type } format so it uses our Kysely instance\n // directly, preserving CamelCasePlugin for snake_case column mapping.\n const kyselyDb = new Kysely({\n dialect: new SqliteDialect({ database: sqliteDb }),\n plugins: [new CamelCasePlugin()],\n });\n return { db: kyselyDb, type: 'sqlite' as const };\n } catch (err) {\n if (err instanceof Error && err.message.includes('better-sqlite3')) {\n throw new Error(\n 'Cannot create SQLite database for internal Better Auth: ' +\n 'install better-sqlite3 (npm install better-sqlite3). ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n throw err;\n }\n}\n\n/** Create a PostgreSQL pool from a connection string. */\nasync function createPostgresPool(connectionString: string) {\n try {\n const { Pool } = await import('pg');\n return new Pool({ connectionString });\n } catch {\n throw new Error(\n 'Cannot create PostgreSQL pool for internal Better Auth: ' +\n 'install the \"pg\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n/** Create a MySQL pool from a connection string. */\nasync function createMySQLPool(connectionString: string) {\n try {\n const mysql = await import('mysql2/promise');\n return mysql.createPool(connectionString);\n } catch {\n throw new Error(\n 'Cannot create MySQL pool for internal Better Auth: ' +\n 'install the \"mysql2\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the Invect user-auth plugin (a light wrapper around Better Auth).\n *\n * This plugin:\n *\n * 1. **Proxies Better Auth routes** — All of Better Auth's HTTP endpoints\n * (sign-in, sign-up, sign-out, OAuth callbacks, session, etc.) are mounted\n * under the plugin endpoint space at `/plugins/auth/api/auth/*` (configurable).\n *\n * 2. **Resolves sessions → identities** — On every Invect API request, the\n * `onRequest` hook reads the session cookie / bearer token via\n * `auth.api.getSession()` and populates `InvectIdentity`.\n *\n * 3. **Handles authorization** — The `onAuthorize` hook lets Better Auth's\n * session decide whether a request is allowed.\n *\n * @example\n * ```ts\n * // Simple: let the plugin manage Better Auth internally\n * import { authentication } from '@invect/user-auth';\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({\n * globalAdmins: [{ email: 'admin@co.com', pw: 'secret' }],\n * })],\n * }));\n * ```\n *\n * @example\n * ```ts\n * // Advanced: provide your own better-auth instance\n * import { betterAuth } from 'better-auth';\n * import { authentication } from '@invect/user-auth';\n *\n * const auth = betterAuth({\n * database: { ... },\n * emailAndPassword: { enabled: true },\n * // ... your better-auth config\n * });\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({ auth })],\n * }));\n * ```\n */\nexport function authentication(options: AuthenticationPluginOptions): InvectPlugin {\n const {\n prefix = DEFAULT_PREFIX,\n mapUser: customMapUser,\n mapRole = defaultMapRole,\n publicPaths = [],\n onSessionError = 'throw',\n globalAdmins = [],\n } = options;\n\n // Mutable — assigned in `init` when no external instance was provided.\n let auth: BetterAuthInstance | null = options.auth ?? null;\n\n /** Narrow `auth` for call-sites that run only after init. */\n function requireAuth(): BetterAuthInstance {\n if (!auth) {\n throw new Error('Auth plugin not initialized');\n }\n return auth;\n }\n\n let endpointLogger: PluginLoggerLike = console;\n\n // Determine Better Auth's basePath (defaults to /api/auth).\n // Computed lazily since `auth` may not exist until `init`.\n let betterAuthBasePath = '/api/auth';\n\n /**\n * Resolve an identity from a request's headers.\n */\n async function getIdentityFromHeaders(\n headers: Record<string, string | undefined>,\n ): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await resolveSession(auth, headers);\n if (!result) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(result.user, result.session);\n }\n\n return defaultMapUser(result.user, result.session, mapRole);\n }\n\n async function getIdentityFromRequest(request: Request): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await callBetterAuthHandler(auth, request, '/get-session');\n const body = result?.body as { session?: BetterAuthSession; user?: BetterAuthUser } | null;\n if (!body?.session || !body?.user) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(body.user, body.session);\n }\n\n return defaultMapUser(body.user, body.session, mapRole);\n }\n\n async function resolveEndpointIdentity(ctx: {\n identity: InvectIdentity | null;\n request: Request;\n }): Promise<InvectIdentity | null> {\n if (ctx.identity) {\n return ctx.identity;\n }\n\n return getIdentityFromRequest(ctx.request);\n }\n\n // Rate limiter for auth-sensitive endpoints (sign-in, sign-up, password reset)\n // 10 attempts per IP per 60 seconds by default\n const authRateLimiter = new RateLimiter(10, 60_000);\n\n // Periodic cleanup every 5 minutes to prevent memory leaks\n const cleanupInterval = setInterval(() => authRateLimiter.cleanup(), 5 * 60_000);\n cleanupInterval.unref?.(); // Don't keep the process alive\n\n // ----- Build plugin endpoint list -----\n // We create a catch-all endpoint that proxies everything under the prefix\n // to better-auth's handler. This covers all auth routes: sign-in, sign-up,\n // OAuth callbacks, session management, etc.\n\n const proxyMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;\n\n const endpoints = proxyMethods.map((method) => ({\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: `/${prefix}/*` as const,\n isPublic: true, // Auth routes must be accessible without existing session\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n request: Request;\n }) => {\n // Build a new Request for better-auth's handler.\n // The incoming path is: /plugins/auth/api/auth/sign-in/email (for example)\n // We need to reconstruct the URL that better-auth expects.\n const incomingUrl = new URL(ctx.request.url);\n endpointLogger.debug?.(`[auth-proxy] ${method} ${incomingUrl.pathname}`);\n\n // Strip the /plugins/<prefix> prefix from the pathname to get\n // the better-auth-relative path\n const pluginPrefixPattern = `/plugins/${prefix}`;\n let authPath = incomingUrl.pathname;\n const prefixIdx = authPath.indexOf(pluginPrefixPattern);\n if (prefixIdx !== -1) {\n authPath = authPath.slice(prefixIdx + pluginPrefixPattern.length);\n }\n if (!authPath.startsWith('/')) {\n authPath = '/' + authPath;\n }\n\n // ── Rate-limit auth-sensitive POST endpoints ──────────\n if (method === 'POST' && RATE_LIMITED_AUTH_PATHS.some((p) => authPath.includes(p))) {\n const clientIp =\n ctx.headers['x-forwarded-for']?.split(',')[0]?.trim() ||\n ctx.headers['x-real-ip'] ||\n 'unknown';\n const { limited, retryAfterMs } = authRateLimiter.isRateLimited(clientIp);\n if (limited) {\n return new Response(\n JSON.stringify({\n error: 'Too Many Requests',\n message: 'Too many authentication attempts. Please try again later.',\n }),\n {\n status: 429,\n headers: {\n 'content-type': 'application/json',\n 'retry-after': String(Math.ceil((retryAfterMs ?? 60_000) / 1000)),\n },\n },\n );\n }\n }\n\n // Construct the URL that better-auth expects\n const authUrl = new URL(`${incomingUrl.origin}${authPath}${incomingUrl.search}`);\n endpointLogger.debug?.(\n `[auth-proxy] Forwarding to better-auth: ${method} ${authUrl.pathname}`,\n );\n\n // Clone/forward the request to better-auth\n const authRequest = new Request(authUrl.toString(), {\n method: ctx.request.method,\n headers: ctx.request.headers,\n body: method !== 'GET' && method !== 'DELETE' ? ctx.request.body : undefined,\n // @ts-expect-error - duplex is needed for streaming bodies\n duplex: method !== 'GET' && method !== 'DELETE' ? 'half' : undefined,\n });\n\n // Let better-auth handle it\n const response = await requireAuth().handler(authRequest);\n endpointLogger.debug?.(`[auth-proxy] Response: ${response.status} ${response.statusText}`, {\n setCookie: response.headers.get('set-cookie') ? 'present' : 'absent',\n contentType: response.headers.get('content-type'),\n });\n return response;\n },\n }));\n\n // ----- Resolve API key opt from top-level or passthrough -----\n const apiKeyEnabled = !!(options.apiKey ?? options.betterAuthOptions?.apiKey);\n\n // ----- Build schema (conditionally includes apikey table) -----\n const schema: InvectPluginSchema = apiKeyEnabled\n ? { ...USER_AUTH_SCHEMA, ...API_KEY_SCHEMA }\n : USER_AUTH_SCHEMA;\n\n const requiredTables = ['user', 'session', 'account', 'verification', 'flow_access'];\n if (apiKeyEnabled) {\n requiredTables.push('apikey');\n }\n\n // ----- Build the plugin -----\n return {\n id: 'user-auth',\n name: 'User Auth',\n\n // Abstract schema for auth tables — the CLI reads this to generate\n // Drizzle/Prisma schema files that include auth tables automatically.\n schema,\n\n // Also declare requiredTables for the startup existence check.\n requiredTables,\n setupInstructions:\n 'Run `npx invect-cli generate` to add the better-auth tables to your schema, ' +\n 'then `npx drizzle-kit push` (or `npx invect-cli migrate`) to apply.',\n\n endpoints: [\n // ── Auth Info (specific routes must come BEFORE the catch-all proxy) ───\n\n {\n method: 'GET' as const,\n path: `/${prefix}/me`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: {\n getPermissions: (identity: InvectIdentity | null) => string[];\n getResolvedRole: (identity: InvectIdentity) => string | null;\n };\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n const permissions = ctx.core.getPermissions(identity);\n const resolvedRole = identity ? ctx.core.getResolvedRole(identity) : null;\n\n return {\n status: 200,\n body: {\n identity: identity\n ? {\n id: identity.id,\n name: identity.name,\n role: identity.role,\n resolvedRole,\n }\n : null,\n permissions,\n isAuthenticated: !!identity,\n },\n };\n },\n },\n\n {\n method: 'GET' as const,\n path: `/${prefix}/roles`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: { getAvailableRoles: () => unknown };\n }) => {\n const roles = ctx.core.getAvailableRoles() as Array<{\n role: string;\n permissions: string[];\n }>;\n const missingRoles = AUTH_VISIBLE_ROLES.filter(\n (role) => !roles.some((entry) => entry.role === role),\n ).map((role) => ({ role, permissions: [] }));\n return { status: 200, body: { roles: [...roles, ...missingRoles] } };\n },\n },\n\n // ── Auth Info ────────────────────────────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/info`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity) {\n return {\n status: 401,\n body: { error: 'Unauthorized' },\n };\n }\n return {\n status: 200,\n body: {\n apiKeysEnabled: apiKeyEnabled,\n },\n };\n },\n },\n\n // ── API Key Management (admin-only) ────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/list', {\n method: 'GET',\n query: ctx.query,\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to list API keys' },\n };\n } catch (err) {\n endpointLogger.error('Failed to list API keys', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list API keys', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const {\n name,\n expiresIn,\n prefix: keyPrefix,\n } = ctx.body as {\n name?: string;\n expiresIn?: number;\n prefix?: string;\n };\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/create', {\n method: 'POST',\n body: {\n name: name || undefined,\n expiresIn: expiresIn || undefined,\n prefix: keyPrefix || undefined,\n },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 201, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to create API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to create API key', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create API key', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/api-keys/:keyId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const { keyId } = ctx.params;\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/delete', {\n method: 'POST',\n body: { keyId },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: { success: true } };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to delete API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete API key', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete API key', err);\n }\n },\n },\n\n // ── User Management (admin-only) ──────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth's admin listUsers API first\n if (typeof api.listUsers === 'function') {\n const listUsers = api.listUsers as BetterAuthHeadersQueryMethod<\n { users?: unknown[] } | unknown[] | null\n >;\n const result = await listUsers({\n headers,\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n });\n const users = Array.isArray(result) ? result : (result?.users ?? []);\n return { status: 200, body: { users } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/list-users',\n {\n method: 'GET',\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return {\n status: 200,\n body: fallbackResult.body as Record<string, unknown>,\n };\n }\n\n // Fallback: query the user table directly via an internal request\n // to better-auth's get-session endpoint for the current user\n return {\n status: 200,\n body: {\n users: [],\n message:\n 'User listing requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to list users', {\n identity: sanitizeForLogging(identity),\n query: sanitizeForLogging(ctx.query),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list users', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { email, password, name, role } = ctx.body as {\n email?: string;\n password?: string;\n name?: string;\n role?: string;\n };\n\n if (!email || !password) {\n return {\n status: 400,\n body: { error: 'email and password are required' },\n };\n }\n\n if (role !== undefined && !isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n let result: BetterAuthApiUserResult | null = null;\n\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else if (typeof api.signUpEmail === 'function') {\n const signUpEmail =\n api.signUpEmail as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else {\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/create-user',\n {\n method: 'POST',\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n result = fallbackResult.body as BetterAuthApiUserResult;\n }\n }\n\n if (\n !result &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n return {\n status: 500,\n body: { error: 'Auth API does not support user creation' },\n };\n }\n\n if (!result?.user) {\n return {\n status: 400,\n body: {\n error: 'Failed to create user',\n message: 'User may already exist',\n },\n };\n }\n\n return {\n status: 201,\n body: {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n role: result.user.role,\n },\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to create user', {\n identity: sanitizeForLogging(identity),\n body: sanitizeForLogging({ email, name, role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create user', err);\n }\n },\n },\n\n {\n method: 'PATCH' as const,\n path: `/${prefix}/users/:userId/role`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n const { role } = ctx.body as { role?: string };\n\n if (!isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth admin API for updating user\n if (typeof api.setRole === 'function') {\n const setRole = api.setRole as BetterAuthHeadersBodyMethod<unknown>;\n await setRole({\n headers,\n body: { userId, role },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/set-role',\n {\n method: 'POST',\n body: { userId, role },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId, role } };\n }\n\n // Fallback: try updateUser API\n if (typeof api.updateUser === 'function') {\n const updateUser = api.updateUser as BetterAuthHeadersBodyParamsMethod<unknown>;\n await updateUser({\n headers,\n body: { role },\n params: { id: userId },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'Role update requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to update role', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n body: sanitizeForLogging({ role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to update role', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/users/:userId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n\n // Prevent deleting yourself\n if (identity.id === userId) {\n return {\n status: 400,\n body: { error: 'Cannot delete your own account' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n if (typeof api.removeUser === 'function') {\n const removeUser = api.removeUser as BetterAuthHeadersBodyMethod<unknown>;\n await removeUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/remove-user',\n {\n method: 'POST',\n body: { userId },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId } };\n }\n\n // Try deleteUser\n if (typeof api.deleteUser === 'function') {\n const deleteUser = api.deleteUser as BetterAuthHeadersBodyMethod<unknown>;\n await deleteUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'User deletion requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete user', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete user', err);\n }\n },\n },\n\n // ── Auth proxy catch-all (must come LAST so specific routes above win) ──\n ...endpoints,\n ],\n\n hooks: {\n /**\n * onRequest: Intercept incoming requests to resolve auth sessions.\n *\n * - Auth proxy routes are passed through untouched.\n * - For all other routes, we resolve the session. If no session exists\n * and `onSessionError === 'throw'`, we short-circuit with a 401.\n */\n onRequest: async (\n request: Request,\n context: { path: string; method: string; identity: InvectIdentity | null },\n ) => {\n // Skip session resolution for auth proxy routes\n if (isBetterAuthRoute(context.path, prefix, betterAuthBasePath)) {\n return; // Let the proxy endpoint handle it\n }\n\n // Skip for public paths\n if (publicPaths.some((p) => context.path.startsWith(p))) {\n return;\n }\n\n // Resolve session from request headers\n const headersRecord: Record<string, string | undefined> = {};\n request.headers.forEach((value, key) => {\n headersRecord[key] = value;\n });\n\n endpointLogger.debug?.(`[auth-onRequest] ${context.method} ${context.path}`, {\n hasCookie: !!headersRecord['cookie'],\n hasAuth: !!headersRecord['authorization'],\n });\n\n const identity = await getIdentityFromHeaders(headersRecord);\n\n endpointLogger.debug?.(`[auth-onRequest] Identity resolved:`, {\n authenticated: !!identity,\n userId: identity?.id,\n role: identity?.role,\n });\n\n // Write identity back so the framework adapter has it available.\n context.identity = identity;\n\n if (!identity && onSessionError === 'throw') {\n return {\n response: new Response(\n JSON.stringify({\n error: 'Unauthorized',\n message: 'Valid session required. Sign in via better-auth.',\n }),\n {\n status: 401,\n headers: { 'content-type': 'application/json' },\n },\n ),\n };\n }\n\n // Return void — identity is now on context.identity for the caller.\n return;\n },\n\n /**\n * onAuthorize: Baseline authorization guard using better-auth sessions.\n *\n * If the identity is already populated (from onRequest),\n * we defer to downstream authorization hooks. This hook is a fallback\n * for cases where the identity wasn't resolved upstream.\n */\n onAuthorize: async (context: {\n identity: InvectIdentity | null;\n action: InvectPermission;\n resource?: { type: string; id?: string };\n }) => {\n // If an identity is already attached, let downstream authorization proceed\n if (context.identity) {\n return;\n }\n\n // No identity — deny access\n return { allowed: false, reason: 'No valid better-auth session' };\n },\n },\n\n init: async (pluginContext) => {\n endpointLogger = pluginContext.logger;\n\n // ── Create internal better-auth instance if none was provided ────────\n if (!auth) {\n auth = await createInternalBetterAuth(pluginContext.config, options, pluginContext.logger);\n }\n\n betterAuthBasePath = auth.options?.basePath ?? '/api/auth';\n\n pluginContext.logger.info(\n `Better Auth plugin initialized (prefix: ${prefix}, basePath: ${betterAuthBasePath})`,\n );\n\n if (globalAdmins.length === 0) {\n pluginContext.logger.debug(\n 'No global admins configured. Pass `globalAdmins` to authentication(...) to seed admin access.',\n );\n return;\n }\n\n for (const configuredAdmin of globalAdmins) {\n const adminEmail = configuredAdmin.email?.trim();\n const adminPassword = configuredAdmin.pw;\n const adminName = configuredAdmin.name?.trim() || 'Admin';\n\n if (!adminEmail || !adminPassword) {\n pluginContext.logger.debug(\n 'Skipping invalid global admin config: both email and pw are required.',\n );\n continue;\n }\n\n try {\n const authContext = await getAuthContext(auth);\n const existingAdminUser = unwrapFoundUser(\n await authContext?.internalAdapter?.findUserByEmail(adminEmail),\n );\n\n if (existingAdminUser) {\n if (existingAdminUser.role !== 'admin') {\n await authContext?.internalAdapter?.updateUser(existingAdminUser.id, {\n role: 'admin',\n });\n pluginContext.logger.info(`Admin user promoted: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(`Admin user already configured: ${adminEmail}`);\n }\n\n continue;\n }\n\n const api = requireAuth().api as Record<string, unknown>;\n let result: BetterAuthApiUserResult | null = null;\n\n // Try createUser first (admin plugin API), then fall back to signUpEmail.\n // createUser requires admin auth headers which aren't available during\n // initial seeding, so we fall through to signUpEmail when it fails.\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers: new Headers(),\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n role: 'admin',\n },\n }).catch((_err: unknown) => {\n pluginContext.logger.debug?.(\n `createUser API requires auth, falling back to signUpEmail for ${adminEmail}`,\n );\n return null;\n });\n }\n\n if (!result?.user && typeof api.signUpEmail === 'function') {\n const signUpEmail = api.signUpEmail as BetterAuthBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n },\n }).catch((err: unknown) => {\n pluginContext.logger.error?.(\n `signUpEmail failed for ${adminEmail}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n });\n }\n\n if (\n !result?.user &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n pluginContext.logger.debug(\n `Could not create global admin ${adminEmail}: auth.api.createUser/signUpEmail are unavailable.`,\n );\n continue;\n }\n\n if (result?.user) {\n // Promote the newly created user to admin via internal adapter\n const createdAuthContext = authContext ?? (await getAuthContext(auth));\n const createdAdminUser =\n unwrapFoundUser(\n await createdAuthContext?.internalAdapter?.findUserByEmail(adminEmail),\n ) ?? result.user;\n\n if (createdAdminUser?.id && createdAdminUser.role !== 'admin') {\n await createdAuthContext?.internalAdapter?.updateUser(createdAdminUser.id, {\n role: 'admin',\n });\n }\n\n pluginContext.logger.info(`Admin user created: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(\n `Admin user already exists or could not be created: ${adminEmail}`,\n );\n }\n } catch (seedErr) {\n pluginContext.logger.debug(\n `Could not seed admin user (tables may not exist yet): ${adminEmail} — ${seedErr instanceof Error ? seedErr.message : String(seedErr)}`,\n );\n }\n }\n },\n\n $ERROR_CODES: {\n 'auth:session_expired': {\n message: 'Session has expired. Please sign in again.',\n status: 401,\n },\n 'auth:session_not_found': {\n message: 'No valid session found.',\n status: 401,\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a path is a better-auth proxy route (should skip session checks).\n * Only matches the actual better-auth API proxy routes, not custom plugin endpoints.\n */\nfunction isBetterAuthRoute(path: string, prefix: string, basePath: string): boolean {\n return path.startsWith(`/plugins/${prefix}${basePath}`);\n}\n","/**\n * @invect/user-auth — Backend Entry Point\n *\n * Wraps a [better-auth](https://better-auth.com) instance as an Invect plugin,\n * providing:\n * - Session-based identity resolution\n * - Proxied auth routes (sign-in, sign-up, OAuth, etc.)\n * - Authorization hook integration\n * - Express/NestJS middleware helpers\n *\n * @example\n * ```ts\n * // Simple — no separate auth setup needed:\n * import { auth } from '@invect/user-auth';\n *\n * defineConfig({\n * plugins: [auth()],\n * });\n * ```\n *\n * @example\n * ```ts\n * // With frontend UI:\n * import { auth } from '@invect/user-auth';\n * import { authFrontend } from '@invect/user-auth/ui';\n *\n * defineConfig({\n * plugins: [auth({ frontend: authFrontend })],\n * });\n * ```\n *\n * @packageDocumentation\n */\nexport { authentication, USER_AUTH_SCHEMA } from './plugin';\nexport type {\n AuthenticationPluginOptions,\n ApiKeyPluginOptions,\n BetterAuthPassthroughOptions,\n BetterAuthInstance,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthSessionResult,\n} from './types';\n\nimport type { InvectPluginDefinition } from '@invect/core';\nimport type { AuthenticationPluginOptions } from './types';\nimport { authentication } from './plugin';\n\n/**\n * Create the auth plugin definition for Invect config.\n *\n * @example\n * ```ts\n * // Express (backend only):\n * auth({ adminEmail: '...' })\n *\n * // Next.js (with frontend):\n * import { authFrontend } from '@invect/user-auth/ui';\n * auth({ adminEmail: '...', frontend: authFrontend })\n * ```\n */\nexport function auth(options: AuthenticationPluginOptions): InvectPluginDefinition {\n return {\n id: 'user-auth',\n name: 'User Authentication',\n backend: authentication(options),\n frontend: options.frontend,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AUTH_DEFAULT_ROLE","isAuthVisibleRole","AUTH_ADMIN_ROLE","AUTH_VISIBLE_ROLES","isAuthAssignableRole","AUTH_ASSIGNABLE_ROLES"],"sources":["../../src/backend/plugin.ts","../../src/backend/index.ts"],"sourcesContent":["import type {\n InvectPlugin,\n InvectIdentity,\n InvectRole,\n InvectPermission,\n InvectPluginSchema,\n} from '@invect/core';\nimport type {\n AuthenticationPluginOptions,\n BetterAuthContext,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthInstance,\n} from './types';\nimport {\n AUTH_ADMIN_ROLE,\n AUTH_ASSIGNABLE_ROLES,\n AUTH_DEFAULT_ROLE,\n AUTH_VISIBLE_ROLES,\n isAuthAssignableRole,\n isAuthVisibleRole,\n} from '../shared/roles';\n\ntype PluginLoggerLike = {\n error: (message: string, meta?: unknown) => void;\n info?: (message: string, meta?: unknown) => void;\n debug?: (message: string, meta?: unknown) => void;\n warn?: (message: string, meta?: unknown) => void;\n};\n\ntype BetterAuthApiUser = {\n id?: string;\n email?: string | null;\n name?: string | null;\n role?: string | null;\n [key: string]: unknown;\n};\n\ntype BetterAuthApiUserResult = {\n user?: BetterAuthApiUser | null;\n};\n\ntype BetterAuthHeadersQueryMethod<TResult> = (args: {\n headers: Headers;\n query: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyParamsMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n params: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthBodyMethod<TResult> = (args: { body: Record<string, unknown> }) => Promise<TResult>;\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PREFIX = 'auth';\n\n/**\n * Default role mapping: keep admin/RBAC roles aligned and fall back to default.\n */\nfunction defaultMapRole(role: string | null | undefined): InvectRole {\n if (!role) {\n return AUTH_DEFAULT_ROLE;\n }\n if (role === 'viewer' || role === 'readonly') {\n return 'viewer';\n }\n if (isAuthVisibleRole(role)) {\n return role;\n }\n return AUTH_DEFAULT_ROLE;\n}\n\n/**\n * Default user → identity mapping.\n */\nfunction defaultMapUser(\n user: BetterAuthUser,\n _session: BetterAuthSession,\n mapRole: (role: string | null | undefined) => InvectRole,\n): InvectIdentity {\n const resolvedRole = mapRole(user.role);\n\n return {\n id: user.id,\n name: user.name ?? user.email ?? undefined,\n role: resolvedRole,\n permissions: resolvedRole === AUTH_ADMIN_ROLE ? ['admin:*'] : undefined,\n resourceAccess:\n resolvedRole === AUTH_ADMIN_ROLE\n ? {\n flows: '*',\n credentials: '*',\n }\n : undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Simple in-memory sliding-window rate limiter.\n *\n * Keyed by IP address (or a fallback identifier). Tracks request timestamps\n * per window and rejects requests that exceed the limit with HTTP 429.\n *\n * Only applied to authentication-sensitive endpoints (sign-in, sign-up,\n * password reset) to prevent brute-force attacks. Session reads (GET) are\n * not rate-limited.\n */\nclass RateLimiter {\n private windows = new Map<string, number[]>();\n private readonly maxRequests: number;\n private readonly windowMs: number;\n\n constructor(maxRequests = 10, windowMs = 60_000) {\n this.maxRequests = maxRequests;\n this.windowMs = windowMs;\n }\n\n /**\n * Returns `true` if the request should be rejected (over limit).\n */\n isRateLimited(key: string): { limited: boolean; retryAfterMs?: number } {\n const now = Date.now();\n const windowStart = now - this.windowMs;\n\n let timestamps = this.windows.get(key);\n if (!timestamps) {\n timestamps = [];\n this.windows.set(key, timestamps);\n }\n\n // Prune expired entries\n const valid = timestamps.filter((t) => t > windowStart);\n this.windows.set(key, valid);\n\n if (valid.length >= this.maxRequests) {\n const oldestInWindow = valid[0] ?? now;\n const retryAfterMs = oldestInWindow + this.windowMs - now;\n return { limited: true, retryAfterMs: Math.max(retryAfterMs, 1000) };\n }\n\n valid.push(now);\n return { limited: false };\n }\n\n /** Periodic cleanup of stale keys to prevent memory leaks. */\n cleanup(): void {\n const now = Date.now();\n for (const [key, timestamps] of this.windows) {\n const valid = timestamps.filter((t) => t > now - this.windowMs);\n if (valid.length === 0) {\n this.windows.delete(key);\n } else {\n this.windows.set(key, valid);\n }\n }\n }\n}\n\n/** Auth-sensitive path segments that should be rate-limited. */\nconst RATE_LIMITED_AUTH_PATHS = ['/sign-in/', '/sign-up/', '/forgot-password', '/reset-password'];\n\n/**\n * Convert a Node.js-style `IncomingHttpHeaders` record or a `Headers` instance\n * to a standard `Headers` object for passing to better-auth.\n */\nfunction toHeaders(raw: Record<string, string | undefined> | Headers): Headers {\n if (raw instanceof Headers) {\n return raw;\n }\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(raw)) {\n if (value !== undefined) {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * Resolve the session from a better-auth instance using request headers.\n */\nasync function resolveSession(\n auth: BetterAuthInstance | null,\n headers: Record<string, string | undefined> | Headers,\n): Promise<{ session: BetterAuthSession; user: BetterAuthUser } | null> {\n if (!auth) {\n return null;\n }\n\n const h = toHeaders(headers);\n\n try {\n const result = await auth.api.getSession({\n headers: h,\n });\n if (result?.session && result?.user) {\n return {\n session: result.session,\n user: result.user,\n };\n }\n return null;\n } catch (err) {\n return null;\n }\n}\n\nasync function callBetterAuthHandler(\n auth: BetterAuthInstance | null,\n request: Request,\n path: string,\n init?: {\n method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n query?: Record<string, string | undefined>;\n body?: Record<string, unknown>;\n },\n): Promise<{ status: number; body: unknown } | null> {\n if (!auth) {\n return null;\n }\n\n const basePath = auth.options?.basePath ?? '/api/auth';\n const targetUrl = new URL(`${basePath}${path}`, request.url);\n for (const [key, value] of Object.entries(init?.query ?? {})) {\n if (value !== undefined) {\n targetUrl.searchParams.set(key, value);\n }\n }\n\n const headers = new Headers(request.headers);\n const hasBody = init?.body !== undefined;\n if (hasBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n const authRequest = new Request(targetUrl.toString(), {\n method: init?.method ?? 'GET',\n headers,\n body: hasBody ? JSON.stringify(init?.body) : undefined,\n });\n\n const response = await auth.handler(authRequest);\n const text = await response.text();\n\n if (!text) {\n return { status: response.status, body: null };\n }\n\n try {\n return { status: response.status, body: JSON.parse(text) };\n } catch {\n return { status: response.status, body: text };\n }\n}\n\nasync function getAuthContext(auth: BetterAuthInstance | null): Promise<BetterAuthContext | null> {\n if (!auth) {\n return null;\n }\n try {\n return (await auth.$context) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction isBetterAuthUser(value: unknown): value is BetterAuthUser {\n return !!value && typeof value === 'object' && 'id' in value && typeof value.id === 'string';\n}\n\nfunction unwrapFoundUser(\n result: BetterAuthUser | { user?: BetterAuthUser | null } | null | undefined,\n): BetterAuthUser | null {\n if (!result) {\n return null;\n }\n\n if (typeof result === 'object' && 'user' in result) {\n const nestedUser = result.user;\n if (isBetterAuthUser(nestedUser)) {\n return nestedUser;\n }\n\n return null;\n }\n\n if (isBetterAuthUser(result)) {\n return result;\n }\n\n return null;\n}\n\nfunction toAuthApiErrorResponse(\n fallbackError: string,\n error: unknown,\n): { status: number; body: Record<string, unknown> } {\n if (error instanceof Response) {\n return {\n status: error.status || 500,\n body: { error: fallbackError, message: error.statusText || fallbackError },\n };\n }\n\n const status =\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status?: unknown }).status === 'number'\n ? ((error as { status: number }).status ?? 500)\n : error &&\n typeof error === 'object' &&\n 'statusCode' in error &&\n typeof (error as { statusCode?: unknown }).statusCode === 'number'\n ? ((error as { statusCode: number }).statusCode ?? 500)\n : 500;\n\n const message =\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ? (error as { message: string }).message || fallbackError\n : fallbackError;\n\n const code =\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n typeof (error as { code?: unknown }).code === 'string'\n ? (error as { code: string }).code\n : undefined;\n\n return {\n status,\n body: {\n error: fallbackError,\n message,\n ...(code ? { code } : {}),\n },\n };\n}\n\nfunction sanitizeForLogging(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeForLogging(item));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, nestedValue]) => {\n if (/password|token|secret/i.test(key)) {\n return [key, '[REDACTED]'];\n }\n\n return [key, sanitizeForLogging(nestedValue)];\n }),\n );\n }\n\n return value;\n}\n\nfunction getErrorLogDetails(error: unknown): Record<string, unknown> {\n if (error instanceof Response) {\n return {\n type: 'Response',\n status: error.status,\n statusText: error.statusText,\n };\n }\n\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n ...(error && typeof error === 'object' && 'cause' in error\n ? { cause: sanitizeForLogging((error as Error & { cause?: unknown }).cause) }\n : {}),\n ...(error && typeof error === 'object' && 'code' in error\n ? { code: (error as Error & { code?: unknown }).code }\n : {}),\n ...(error && typeof error === 'object' && 'status' in error\n ? { status: (error as Error & { status?: unknown }).status }\n : {}),\n ...(error && typeof error === 'object' && 'statusCode' in error\n ? { statusCode: (error as Error & { statusCode?: unknown }).statusCode }\n : {}),\n };\n }\n\n if (error && typeof error === 'object') {\n return sanitizeForLogging(error) as Record<string, unknown>;\n }\n\n return { value: error };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract schema — defines the tables the user-auth plugin requires\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract schema for the user-auth plugin's database tables.\n *\n * These definitions allow the Invect CLI (`npx invect-cli generate`) to include\n * the auth tables when generating Drizzle/Prisma schema files.\n *\n * The shapes match Better Auth's default table structure. If your Better Auth\n * config adds extra fields (e.g., via plugins like `twoFactor`, `organization`),\n * you can extend these in your own config.\n */\nexport const USER_AUTH_SCHEMA: InvectPluginSchema = {\n user: {\n tableName: 'user',\n order: 1,\n fields: {\n id: { type: 'string', primaryKey: true },\n name: { type: 'string', required: true },\n email: { type: 'string', required: true, unique: true },\n emailVerified: { type: 'boolean', required: true, defaultValue: false },\n image: { type: 'string', required: false },\n role: { type: 'string', required: false, defaultValue: AUTH_DEFAULT_ROLE },\n banned: { type: 'boolean', required: false, defaultValue: false },\n banReason: { type: 'string', required: false },\n banExpires: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n session: {\n tableName: 'session',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n expiresAt: { type: 'date', required: true },\n token: { type: 'string', required: true, unique: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n ipAddress: { type: 'string', required: false },\n userAgent: { type: 'string', required: false },\n impersonatedBy: { type: 'string', required: false },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n },\n },\n\n account: {\n tableName: 'account',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n accountId: { type: 'string', required: true },\n providerId: { type: 'string', required: true },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n accessToken: { type: 'string', required: false },\n refreshToken: { type: 'string', required: false },\n idToken: { type: 'string', required: false },\n accessTokenExpiresAt: { type: 'date', required: false },\n refreshTokenExpiresAt: { type: 'date', required: false },\n scope: { type: 'string', required: false },\n password: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n verification: {\n tableName: 'verification',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n identifier: { type: 'string', required: true },\n value: { type: 'string', required: true },\n expiresAt: { type: 'date', required: true },\n createdAt: { type: 'date', required: false },\n updatedAt: { type: 'date', required: false },\n },\n },\n\n // ----- Flow access control table (moved from core) -----\n flowAccess: {\n tableName: 'flow_access',\n order: 3,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n },\n userId: { type: 'string', required: false },\n teamId: { type: 'string', required: false },\n permission: {\n type: 'string',\n required: true,\n defaultValue: 'viewer',\n },\n grantedBy: { type: 'string', required: false },\n grantedAt: { type: 'date', required: true, defaultValue: 'now()' },\n expiresAt: { type: 'date', required: false },\n },\n },\n};\n\n/**\n * Abstract schema for the Better Auth API Key plugin's `apikey` table.\n *\n * Only merged into the plugin schema when `apiKey` is enabled.\n *\n * @see https://better-auth.com/docs/plugins/api-key/reference#schema\n */\nconst API_KEY_SCHEMA: InvectPluginSchema = {\n apikey: {\n tableName: 'apikey',\n order: 3,\n fields: {\n id: { type: 'string', primaryKey: true },\n configId: { type: 'string', required: true, defaultValue: 'default' },\n name: { type: 'string', required: false },\n start: { type: 'string', required: false },\n prefix: { type: 'string', required: false },\n key: { type: 'string', required: true },\n referenceId: { type: 'string', required: true },\n refillInterval: { type: 'number', required: false },\n refillAmount: { type: 'number', required: false },\n lastRefillAt: { type: 'date', required: false },\n enabled: { type: 'boolean', required: false, defaultValue: true },\n rateLimitEnabled: { type: 'boolean', required: false },\n rateLimitTimeWindow: { type: 'number', required: false },\n rateLimitMax: { type: 'number', required: false },\n requestCount: { type: 'number', required: false },\n remaining: { type: 'number', required: false },\n lastRequest: { type: 'date', required: false },\n expiresAt: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n permissions: { type: 'string', required: false },\n metadata: { type: 'string', required: false },\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Internal Better Auth instance creation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Better Auth instance internally using Invect's database config.\n *\n * Dynamically imports `better-auth` (a required peer dependency) and creates\n * a fully-configured instance with email/password auth, the admin plugin,\n * and session caching.\n *\n * Database resolution order:\n * 1. Explicit `options.database` (any value `betterAuth({ database })` accepts)\n * 2. Auto-created client from Invect's `database.connectionString`\n */\nasync function createInternalBetterAuth(\n invectConfig: Record<string, unknown>,\n options: AuthenticationPluginOptions,\n logger: PluginLoggerLike,\n): Promise<BetterAuthInstance> {\n // 1. Dynamic-import better-auth (peer dependency)\n let betterAuthFn: (config: Record<string, unknown>) => unknown;\n let adminPlugin: (config?: Record<string, unknown>) => unknown;\n\n try {\n const betterAuthModule = await import('better-auth');\n betterAuthFn = betterAuthModule.betterAuth;\n } catch {\n throw new Error(\n 'Could not import \"better-auth\". It is a required peer dependency of @invect/user-auth. ' +\n 'Install it with: npm install better-auth',\n );\n }\n\n try {\n const pluginsModule = await import('better-auth/plugins');\n adminPlugin = pluginsModule.admin;\n } catch {\n throw new Error(\n 'Could not import \"better-auth/plugins\". Ensure better-auth is properly installed.',\n );\n }\n\n // 2. Resolve database\n let database: unknown = options.database;\n\n if (!database) {\n const dbConfig = invectConfig.database as\n | { type?: string; connectionString?: string }\n | undefined;\n\n if (!dbConfig?.connectionString) {\n throw new Error(\n 'Cannot create internal Better Auth instance: no database configuration found. ' +\n 'Either provide `auth` (a Better Auth instance), `database`, or ensure ' +\n 'Invect database has a connectionString.',\n );\n }\n\n const connStr = dbConfig.connectionString;\n const dbType = (dbConfig.type ?? 'sqlite').toLowerCase();\n\n if (dbType === 'sqlite') {\n database = await createSQLiteClient(connStr, logger);\n } else if (dbType === 'pg' || dbType === 'postgresql') {\n database = await createPostgresPool(connStr);\n } else if (dbType === 'mysql') {\n database = await createMySQLPool(connStr);\n } else {\n throw new Error(\n `Unsupported database type for internal Better Auth: \"${dbType}\". ` +\n 'Supported: sqlite, pg, mysql. Alternatively, provide your own Better Auth instance via `auth`.',\n );\n }\n }\n\n // 3. Resolve base URL\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n `http://localhost:${process.env.PORT ?? '3000'}`;\n\n // 4. Resolve trusted origins\n const configuredOrigins = options.trustedOrigins;\n const trustedOrigins =\n configuredOrigins ??\n ((request: Request) => {\n const trusted = new Set<string>([baseURL, 'http://localhost:5173', 'http://localhost:5174']);\n try {\n if (request) {\n trusted.add(new URL(request.url).origin);\n }\n } catch {\n // Ignore malformed URLs\n }\n return Array.from(trusted);\n });\n\n // 5. Build passthrough config from betterAuthOptions\n const passthrough = options.betterAuthOptions ?? {};\n\n const emailAndPassword = {\n enabled: true,\n ...passthrough.emailAndPassword,\n };\n\n const session = {\n cookieCache: { enabled: true, maxAge: 5 * 60 },\n ...passthrough.session,\n // Merge cookieCache if both exist\n ...(passthrough.session?.cookieCache\n ? {\n cookieCache: {\n enabled: true,\n maxAge: 5 * 60,\n ...passthrough.session.cookieCache,\n },\n }\n : {}),\n };\n\n // 6. Resolve secret — fall back to INVECT_ENCRYPTION_KEY when no explicit\n // secret or secrets are provided. This lets deployments that already set\n // INVECT_ENCRYPTION_KEY get a valid Better Auth secret for free.\n let resolvedSecret = passthrough.secret;\n const resolvedSecrets = passthrough.secrets;\n\n if (!resolvedSecret && !resolvedSecrets) {\n const envKey = process.env.INVECT_ENCRYPTION_KEY;\n if (envKey) {\n resolvedSecret = envKey;\n logger.debug?.(\n 'Using INVECT_ENCRYPTION_KEY as Better Auth secret (no explicit secret/secrets provided)',\n );\n }\n }\n\n // 7. Optionally load the Better Auth API Key plugin\n // Merge top-level `options.apiKey` with `passthrough.apiKey` (top-level wins).\n const apiKeyOpt = options.apiKey ?? passthrough.apiKey;\n const betterAuthPlugins: unknown[] = [\n adminPlugin({ defaultRole: AUTH_DEFAULT_ROLE, adminRoles: [AUTH_ADMIN_ROLE] }),\n ];\n\n if (apiKeyOpt) {\n let apiKeyPluginFn: (config?: Record<string, unknown>) => unknown;\n try {\n // Dynamic import — @better-auth/api-key is an optional peer dependency.\n // Using a variable to prevent TypeScript from resolving the module at\n // type-check time (it may not be installed).\n const pkg = '@better-auth/api-key';\n const apiKeyModule: Record<string, unknown> = await import(pkg);\n apiKeyPluginFn = apiKeyModule.apiKey as (config?: Record<string, unknown>) => unknown;\n } catch {\n throw new Error(\n 'Could not import \"@better-auth/api-key\". Install it with: npm install @better-auth/api-key',\n );\n }\n\n const apiKeyConfig: Record<string, unknown> =\n typeof apiKeyOpt === 'object' ? { ...apiKeyOpt } : {};\n // Default API key header to \"x-invect-token\" (overridable via options).\n if (apiKeyConfig.apiKeyHeaders === undefined) {\n apiKeyConfig.apiKeyHeaders = 'x-invect-token';\n }\n // Ensure getSession() can resolve API keys to sessions.\n // Without this, auth.api.getSession() only checks cookies/tokens.\n if (apiKeyConfig.enableSessionForAPIKeys === undefined) {\n apiKeyConfig.enableSessionForAPIKeys = true;\n }\n betterAuthPlugins.push(apiKeyPluginFn(apiKeyConfig));\n logger.info?.('Better Auth API Key plugin enabled');\n }\n\n // 8. Create the instance\n logger.info?.('Creating internal Better Auth instance');\n\n const instance = betterAuthFn({\n baseURL,\n database,\n emailAndPassword,\n plugins: betterAuthPlugins,\n session,\n trustedOrigins,\n // Spread optional passthrough fields\n ...(passthrough.socialProviders ? { socialProviders: passthrough.socialProviders } : {}),\n ...(passthrough.account ? { account: passthrough.account } : {}),\n ...(passthrough.rateLimit ? { rateLimit: passthrough.rateLimit } : {}),\n // Default cookie prefix to \"invect\" (cookies become invect.session_token).\n // Users can override via betterAuthOptions.advanced.cookiePrefix.\n advanced: {\n cookiePrefix: 'invect',\n ...passthrough.advanced,\n },\n ...(passthrough.databaseHooks ? { databaseHooks: passthrough.databaseHooks } : {}),\n ...(passthrough.hooks ? { hooks: passthrough.hooks } : {}),\n ...(passthrough.disabledPaths ? { disabledPaths: passthrough.disabledPaths } : {}),\n ...(resolvedSecret ? { secret: resolvedSecret } : {}),\n ...(resolvedSecrets ? { secrets: resolvedSecrets } : {}),\n });\n\n return instance as BetterAuthInstance;\n}\n\n/** Create a SQLite client using better-sqlite3. */\nasync function createSQLiteClient(connectionString: string, logger: PluginLoggerLike) {\n try {\n const { default: Database } = await import('better-sqlite3');\n const { Kysely, SqliteDialect, CamelCasePlugin } = await import('kysely');\n logger.debug?.(`Using better-sqlite3 for internal Better Auth database`);\n // Strip file: prefix if present\n let dbPath = connectionString.replace(/^file:/, '');\n if (dbPath === '') {\n dbPath = ':memory:';\n }\n const sqliteDb = new Database(dbPath);\n // Return in Better Auth's { db, type } format so it uses our Kysely instance\n // directly, preserving CamelCasePlugin for snake_case column mapping.\n const kyselyDb = new Kysely({\n dialect: new SqliteDialect({ database: sqliteDb }),\n plugins: [new CamelCasePlugin()],\n });\n return { db: kyselyDb, type: 'sqlite' as const };\n } catch (err) {\n if (err instanceof Error && err.message.includes('better-sqlite3')) {\n throw new Error(\n 'Cannot create SQLite database for internal Better Auth: ' +\n 'install better-sqlite3 (npm install better-sqlite3). ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n throw err;\n }\n}\n\n/** Create a PostgreSQL pool from a connection string. */\nasync function createPostgresPool(connectionString: string) {\n try {\n const { Pool } = await import('pg');\n return new Pool({ connectionString });\n } catch {\n throw new Error(\n 'Cannot create PostgreSQL pool for internal Better Auth: ' +\n 'install the \"pg\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n/** Create a MySQL pool from a connection string. */\nasync function createMySQLPool(connectionString: string) {\n try {\n const mysql = await import('mysql2/promise');\n return mysql.createPool(connectionString);\n } catch {\n throw new Error(\n 'Cannot create MySQL pool for internal Better Auth: ' +\n 'install the \"mysql2\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the Invect user-auth plugin (a light wrapper around Better Auth).\n *\n * This plugin:\n *\n * 1. **Proxies Better Auth routes** — All of Better Auth's HTTP endpoints\n * (sign-in, sign-up, sign-out, OAuth callbacks, session, etc.) are mounted\n * under the plugin endpoint space at `/plugins/auth/api/auth/*` (configurable).\n *\n * 2. **Resolves sessions → identities** — On every Invect API request, the\n * `onRequest` hook reads the session cookie / bearer token via\n * `auth.api.getSession()` and populates `InvectIdentity`.\n *\n * 3. **Handles authorization** — The `onAuthorize` hook lets Better Auth's\n * session decide whether a request is allowed.\n *\n * @example\n * ```ts\n * // Simple: let the plugin manage Better Auth internally\n * import { authentication } from '@invect/user-auth';\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({\n * globalAdmins: [{ email: 'admin@co.com', pw: 'secret' }],\n * })],\n * }));\n * ```\n *\n * @example\n * ```ts\n * // Advanced: provide your own better-auth instance\n * import { betterAuth } from 'better-auth';\n * import { authentication } from '@invect/user-auth';\n *\n * const auth = betterAuth({\n * database: { ... },\n * emailAndPassword: { enabled: true },\n * // ... your better-auth config\n * });\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({ auth })],\n * }));\n * ```\n */\nexport function authentication(options: AuthenticationPluginOptions): InvectPlugin {\n const {\n prefix = DEFAULT_PREFIX,\n mapUser: customMapUser,\n mapRole = defaultMapRole,\n publicPaths = [],\n globalAdmins = [],\n } = options;\n\n // Mutable — assigned in `init` when no external instance was provided.\n let auth: BetterAuthInstance | null = options.auth ?? null;\n\n /** Narrow `auth` for call-sites that run only after init. */\n function requireAuth(): BetterAuthInstance {\n if (!auth) {\n throw new Error('Auth plugin not initialized');\n }\n return auth;\n }\n\n let endpointLogger: PluginLoggerLike = console;\n\n // Determine Better Auth's basePath (defaults to /api/auth).\n // Computed lazily since `auth` may not exist until `init`.\n let betterAuthBasePath = '/api/auth';\n\n /**\n * Resolve an identity from a request's headers.\n */\n async function getIdentityFromHeaders(\n headers: Record<string, string | undefined>,\n ): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await resolveSession(auth, headers);\n if (!result) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(result.user, result.session);\n }\n\n return defaultMapUser(result.user, result.session, mapRole);\n }\n\n async function getIdentityFromRequest(request: Request): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await callBetterAuthHandler(auth, request, '/get-session');\n const body = result?.body as { session?: BetterAuthSession; user?: BetterAuthUser } | null;\n if (!body?.session || !body?.user) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(body.user, body.session);\n }\n\n return defaultMapUser(body.user, body.session, mapRole);\n }\n\n async function resolveEndpointIdentity(ctx: {\n identity: InvectIdentity | null;\n request: Request;\n }): Promise<InvectIdentity | null> {\n if (ctx.identity) {\n return ctx.identity;\n }\n\n return getIdentityFromRequest(ctx.request);\n }\n\n // Rate limiter for auth-sensitive endpoints (sign-in, sign-up, password reset)\n // 10 attempts per IP per 60 seconds by default\n const authRateLimiter = new RateLimiter(10, 60_000);\n\n // Periodic cleanup every 5 minutes to prevent memory leaks\n const cleanupInterval = setInterval(() => authRateLimiter.cleanup(), 5 * 60_000);\n cleanupInterval.unref?.(); // Don't keep the process alive\n\n // ----- Build plugin endpoint list -----\n // We create a catch-all endpoint that proxies everything under the prefix\n // to better-auth's handler. This covers all auth routes: sign-in, sign-up,\n // OAuth callbacks, session management, etc.\n\n const proxyMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;\n\n const endpoints = proxyMethods.map((method) => ({\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: `/${prefix}/*` as const,\n isPublic: true, // Auth routes must be accessible without existing session\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n request: Request;\n }) => {\n // Build a new Request for better-auth's handler.\n // The incoming path is: /plugins/auth/api/auth/sign-in/email (for example)\n // We need to reconstruct the URL that better-auth expects.\n const incomingUrl = new URL(ctx.request.url);\n endpointLogger.debug?.(`[auth-proxy] ${method} ${incomingUrl.pathname}`);\n\n // Strip the /plugins/<prefix> prefix from the pathname to get\n // the better-auth-relative path\n const pluginPrefixPattern = `/plugins/${prefix}`;\n let authPath = incomingUrl.pathname;\n const prefixIdx = authPath.indexOf(pluginPrefixPattern);\n if (prefixIdx !== -1) {\n authPath = authPath.slice(prefixIdx + pluginPrefixPattern.length);\n }\n if (!authPath.startsWith('/')) {\n authPath = '/' + authPath;\n }\n\n // ── Rate-limit auth-sensitive POST endpoints ──────────\n if (method === 'POST' && RATE_LIMITED_AUTH_PATHS.some((p) => authPath.includes(p))) {\n const clientIp =\n ctx.headers['x-forwarded-for']?.split(',')[0]?.trim() ||\n ctx.headers['x-real-ip'] ||\n 'unknown';\n const { limited, retryAfterMs } = authRateLimiter.isRateLimited(clientIp);\n if (limited) {\n return new Response(\n JSON.stringify({\n error: 'Too Many Requests',\n message: 'Too many authentication attempts. Please try again later.',\n }),\n {\n status: 429,\n headers: {\n 'content-type': 'application/json',\n 'retry-after': String(Math.ceil((retryAfterMs ?? 60_000) / 1000)),\n },\n },\n );\n }\n }\n\n // Construct the URL that better-auth expects\n const authUrl = new URL(`${incomingUrl.origin}${authPath}${incomingUrl.search}`);\n endpointLogger.debug?.(\n `[auth-proxy] Forwarding to better-auth: ${method} ${authUrl.pathname}`,\n );\n\n // Clone/forward the request to better-auth\n const authRequest = new Request(authUrl.toString(), {\n method: ctx.request.method,\n headers: ctx.request.headers,\n body: method !== 'GET' && method !== 'DELETE' ? ctx.request.body : undefined,\n // @ts-expect-error - duplex is needed for streaming bodies\n duplex: method !== 'GET' && method !== 'DELETE' ? 'half' : undefined,\n });\n\n // Let better-auth handle it\n const response = await requireAuth().handler(authRequest);\n endpointLogger.debug?.(`[auth-proxy] Response: ${response.status} ${response.statusText}`, {\n setCookie: response.headers.get('set-cookie') ? 'present' : 'absent',\n contentType: response.headers.get('content-type'),\n });\n return response;\n },\n }));\n\n // ----- Resolve API key opt from top-level or passthrough -----\n const apiKeyEnabled = !!(options.apiKey ?? options.betterAuthOptions?.apiKey);\n\n // ----- Build schema (conditionally includes apikey table) -----\n const schema: InvectPluginSchema = apiKeyEnabled\n ? { ...USER_AUTH_SCHEMA, ...API_KEY_SCHEMA }\n : USER_AUTH_SCHEMA;\n\n const requiredTables = ['user', 'session', 'account', 'verification', 'flow_access'];\n if (apiKeyEnabled) {\n requiredTables.push('apikey');\n }\n\n // ----- Build the plugin -----\n return {\n id: 'user-auth',\n name: 'User Auth',\n\n // Abstract schema for auth tables — the CLI reads this to generate\n // Drizzle/Prisma schema files that include auth tables automatically.\n schema,\n\n // Also declare requiredTables for the startup existence check.\n requiredTables,\n setupInstructions:\n 'Run `npx invect-cli generate` to add the better-auth tables to your schema, ' +\n 'then `npx drizzle-kit push` (or `npx invect-cli migrate`) to apply.',\n\n endpoints: [\n // ── Auth Info (specific routes must come BEFORE the catch-all proxy) ───\n\n {\n method: 'GET' as const,\n path: `/${prefix}/me`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: {\n getPermissions: (identity: InvectIdentity | null) => string[];\n getResolvedRole: (identity: InvectIdentity) => string | null;\n };\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n const permissions = ctx.core.getPermissions(identity);\n const resolvedRole = identity ? ctx.core.getResolvedRole(identity) : null;\n\n return {\n status: 200,\n body: {\n identity: identity\n ? {\n id: identity.id,\n name: identity.name,\n role: identity.role,\n resolvedRole,\n }\n : null,\n permissions,\n isAuthenticated: !!identity,\n },\n };\n },\n },\n\n {\n method: 'GET' as const,\n path: `/${prefix}/roles`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: { getAvailableRoles: () => unknown };\n }) => {\n const roles = ctx.core.getAvailableRoles() as Array<{\n role: string;\n permissions: string[];\n }>;\n const missingRoles = AUTH_VISIBLE_ROLES.filter(\n (role) => !roles.some((entry) => entry.role === role),\n ).map((role) => ({ role, permissions: [] }));\n return { status: 200, body: { roles: [...roles, ...missingRoles] } };\n },\n },\n\n // ── Auth Info ────────────────────────────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/info`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity) {\n return {\n status: 401,\n body: { error: 'Unauthorized' },\n };\n }\n return {\n status: 200,\n body: {\n apiKeysEnabled: apiKeyEnabled,\n },\n };\n },\n },\n\n // ── API Key Management (admin-only) ────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/list', {\n method: 'GET',\n query: ctx.query,\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to list API keys' },\n };\n } catch (err) {\n endpointLogger.error('Failed to list API keys', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list API keys', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const {\n name,\n expiresIn,\n prefix: keyPrefix,\n } = ctx.body as {\n name?: string;\n expiresIn?: number;\n prefix?: string;\n };\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/create', {\n method: 'POST',\n body: {\n name: name || undefined,\n expiresIn: expiresIn || undefined,\n prefix: keyPrefix || undefined,\n },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 201, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to create API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to create API key', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create API key', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/api-keys/:keyId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const { keyId } = ctx.params;\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/delete', {\n method: 'POST',\n body: { keyId },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: { success: true } };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to delete API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete API key', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete API key', err);\n }\n },\n },\n\n // ── User Management (admin-only) ──────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth's admin listUsers API first\n if (typeof api.listUsers === 'function') {\n const listUsers = api.listUsers as BetterAuthHeadersQueryMethod<\n { users?: unknown[] } | unknown[] | null\n >;\n const result = await listUsers({\n headers,\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n });\n const users = Array.isArray(result) ? result : (result?.users ?? []);\n return { status: 200, body: { users } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/list-users',\n {\n method: 'GET',\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return {\n status: 200,\n body: fallbackResult.body as Record<string, unknown>,\n };\n }\n\n // Fallback: query the user table directly via an internal request\n // to better-auth's get-session endpoint for the current user\n return {\n status: 200,\n body: {\n users: [],\n message:\n 'User listing requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to list users', {\n identity: sanitizeForLogging(identity),\n query: sanitizeForLogging(ctx.query),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list users', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { email, password, name, role } = ctx.body as {\n email?: string;\n password?: string;\n name?: string;\n role?: string;\n };\n\n if (!email || !password) {\n return {\n status: 400,\n body: { error: 'email and password are required' },\n };\n }\n\n if (role !== undefined && !isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n let result: BetterAuthApiUserResult | null = null;\n\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else if (typeof api.signUpEmail === 'function') {\n const signUpEmail =\n api.signUpEmail as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else {\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/create-user',\n {\n method: 'POST',\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n result = fallbackResult.body as BetterAuthApiUserResult;\n }\n }\n\n if (\n !result &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n return {\n status: 500,\n body: { error: 'Auth API does not support user creation' },\n };\n }\n\n if (!result?.user) {\n return {\n status: 400,\n body: {\n error: 'Failed to create user',\n message: 'User may already exist',\n },\n };\n }\n\n return {\n status: 201,\n body: {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n role: result.user.role,\n },\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to create user', {\n identity: sanitizeForLogging(identity),\n body: sanitizeForLogging({ email, name, role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create user', err);\n }\n },\n },\n\n {\n method: 'PATCH' as const,\n path: `/${prefix}/users/:userId/role`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n const { role } = ctx.body as { role?: string };\n\n if (!isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth admin API for updating user\n if (typeof api.setRole === 'function') {\n const setRole = api.setRole as BetterAuthHeadersBodyMethod<unknown>;\n await setRole({\n headers,\n body: { userId, role },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/set-role',\n {\n method: 'POST',\n body: { userId, role },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId, role } };\n }\n\n // Fallback: try updateUser API\n if (typeof api.updateUser === 'function') {\n const updateUser = api.updateUser as BetterAuthHeadersBodyParamsMethod<unknown>;\n await updateUser({\n headers,\n body: { role },\n params: { id: userId },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'Role update requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to update role', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n body: sanitizeForLogging({ role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to update role', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/users/:userId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n\n // Prevent deleting yourself\n if (identity.id === userId) {\n return {\n status: 400,\n body: { error: 'Cannot delete your own account' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n if (typeof api.removeUser === 'function') {\n const removeUser = api.removeUser as BetterAuthHeadersBodyMethod<unknown>;\n await removeUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/remove-user',\n {\n method: 'POST',\n body: { userId },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId } };\n }\n\n // Try deleteUser\n if (typeof api.deleteUser === 'function') {\n const deleteUser = api.deleteUser as BetterAuthHeadersBodyMethod<unknown>;\n await deleteUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'User deletion requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete user', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete user', err);\n }\n },\n },\n\n // ── Auth proxy catch-all (must come LAST so specific routes above win) ──\n ...endpoints,\n ],\n\n hooks: {\n /**\n * onRequest: Intercept incoming requests to resolve auth sessions.\n *\n * - Auth proxy routes are passed through untouched.\n * - For all other routes, we resolve the session. If no session exists,\n * we short-circuit with a 401.\n */\n onRequest: async (\n request: Request,\n context: { path: string; method: string; identity: InvectIdentity | null },\n ) => {\n // Skip session resolution for auth proxy routes\n if (isBetterAuthRoute(context.path, prefix, betterAuthBasePath)) {\n return; // Let the proxy endpoint handle it\n }\n\n // Skip for public paths\n if (publicPaths.some((p) => context.path.startsWith(p))) {\n return;\n }\n\n // Resolve session from request headers\n const headersRecord: Record<string, string | undefined> = {};\n request.headers.forEach((value, key) => {\n headersRecord[key] = value;\n });\n\n endpointLogger.debug?.(`[auth-onRequest] ${context.method} ${context.path}`, {\n hasCookie: !!headersRecord['cookie'],\n hasAuth: !!headersRecord['authorization'],\n });\n\n const identity = await getIdentityFromHeaders(headersRecord);\n\n endpointLogger.debug?.(`[auth-onRequest] Identity resolved:`, {\n authenticated: !!identity,\n userId: identity?.id,\n role: identity?.role,\n });\n\n // Write identity back so the framework adapter has it available.\n context.identity = identity;\n\n if (!identity) {\n return {\n response: new Response(\n JSON.stringify({\n error: 'Unauthorized',\n message: 'Valid session required. Sign in via better-auth.',\n }),\n {\n status: 401,\n headers: { 'content-type': 'application/json' },\n },\n ),\n };\n }\n },\n\n /**\n * onAuthorize: Baseline authorization guard using better-auth sessions.\n *\n * If the identity is already populated (from onRequest),\n * we defer to downstream authorization hooks. This hook is a fallback\n * for cases where the identity wasn't resolved upstream.\n */\n onAuthorize: async (context: {\n identity: InvectIdentity | null;\n action: InvectPermission;\n resource?: { type: string; id?: string };\n }) => {\n // If an identity is already attached, let downstream authorization proceed\n if (context.identity) {\n return;\n }\n\n // No identity — deny access\n return { allowed: false, reason: 'No valid better-auth session' };\n },\n },\n\n init: async (pluginContext) => {\n endpointLogger = pluginContext.logger;\n\n // ── Create internal better-auth instance if none was provided ────────\n if (!auth) {\n auth = await createInternalBetterAuth(pluginContext.config, options, pluginContext.logger);\n }\n\n betterAuthBasePath = auth.options?.basePath ?? '/api/auth';\n\n pluginContext.logger.info(\n `Better Auth plugin initialized (prefix: ${prefix}, basePath: ${betterAuthBasePath})`,\n );\n\n if (globalAdmins.length === 0) {\n pluginContext.logger.debug(\n 'No global admins configured. Pass `globalAdmins` to authentication(...) to seed admin access.',\n );\n return;\n }\n\n for (const configuredAdmin of globalAdmins) {\n const adminEmail = configuredAdmin.email?.trim();\n const adminPassword = configuredAdmin.pw;\n const adminName = configuredAdmin.name?.trim() || 'Admin';\n\n if (!adminEmail || !adminPassword) {\n pluginContext.logger.debug(\n 'Skipping invalid global admin config: both email and pw are required.',\n );\n continue;\n }\n\n try {\n const authContext = await getAuthContext(auth);\n const existingAdminUser = unwrapFoundUser(\n await authContext?.internalAdapter?.findUserByEmail(adminEmail),\n );\n\n if (existingAdminUser) {\n if (existingAdminUser.role !== 'admin') {\n await authContext?.internalAdapter?.updateUser(existingAdminUser.id, {\n role: 'admin',\n });\n pluginContext.logger.info(`Admin user promoted: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(`Admin user already configured: ${adminEmail}`);\n }\n\n continue;\n }\n\n const api = requireAuth().api as Record<string, unknown>;\n let result: BetterAuthApiUserResult | null = null;\n\n // Try createUser first (admin plugin API), then fall back to signUpEmail.\n // createUser requires admin auth headers which aren't available during\n // initial seeding, so we fall through to signUpEmail when it fails.\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers: new Headers(),\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n role: 'admin',\n },\n }).catch((_err: unknown) => {\n pluginContext.logger.debug?.(\n `createUser API requires auth, falling back to signUpEmail for ${adminEmail}`,\n );\n return null;\n });\n }\n\n if (!result?.user && typeof api.signUpEmail === 'function') {\n const signUpEmail = api.signUpEmail as BetterAuthBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n },\n }).catch((err: unknown) => {\n pluginContext.logger.error?.(\n `signUpEmail failed for ${adminEmail}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n });\n }\n\n if (\n !result?.user &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n pluginContext.logger.debug(\n `Could not create global admin ${adminEmail}: auth.api.createUser/signUpEmail are unavailable.`,\n );\n continue;\n }\n\n if (result?.user) {\n // Promote the newly created user to admin via internal adapter\n const createdAuthContext = authContext ?? (await getAuthContext(auth));\n const createdAdminUser =\n unwrapFoundUser(\n await createdAuthContext?.internalAdapter?.findUserByEmail(adminEmail),\n ) ?? result.user;\n\n if (createdAdminUser?.id && createdAdminUser.role !== 'admin') {\n await createdAuthContext?.internalAdapter?.updateUser(createdAdminUser.id, {\n role: 'admin',\n });\n }\n\n pluginContext.logger.info(`Admin user created: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(\n `Admin user already exists or could not be created: ${adminEmail}`,\n );\n }\n } catch (seedErr) {\n pluginContext.logger.debug(\n `Could not seed admin user (tables may not exist yet): ${adminEmail} — ${seedErr instanceof Error ? seedErr.message : String(seedErr)}`,\n );\n }\n }\n },\n\n $ERROR_CODES: {\n 'auth:session_expired': {\n message: 'Session has expired. Please sign in again.',\n status: 401,\n },\n 'auth:session_not_found': {\n message: 'No valid session found.',\n status: 401,\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a path is a better-auth proxy route (should skip session checks).\n * Only matches the actual better-auth API proxy routes, not custom plugin endpoints.\n */\nfunction isBetterAuthRoute(path: string, prefix: string, basePath: string): boolean {\n return path.startsWith(`/plugins/${prefix}${basePath}`);\n}\n","/**\n * @invect/user-auth — Backend Entry Point\n *\n * Wraps a [better-auth](https://better-auth.com) instance as an Invect plugin,\n * providing:\n * - Session-based identity resolution\n * - Proxied auth routes (sign-in, sign-up, OAuth, etc.)\n * - Authorization hook integration\n * - Express/NestJS middleware helpers\n *\n * @example\n * ```ts\n * // Simple — no separate auth setup needed:\n * import { auth } from '@invect/user-auth';\n *\n * defineConfig({\n * plugins: [auth()],\n * });\n * ```\n *\n * @example\n * ```ts\n * // With frontend UI:\n * import { auth } from '@invect/user-auth';\n * import { authFrontend } from '@invect/user-auth/ui';\n *\n * defineConfig({\n * plugins: [auth({ frontend: authFrontend })],\n * });\n * ```\n *\n * @packageDocumentation\n */\nexport { authentication, USER_AUTH_SCHEMA } from './plugin';\nexport type {\n AuthenticationPluginOptions,\n ApiKeyPluginOptions,\n BetterAuthPassthroughOptions,\n BetterAuthInstance,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthSessionResult,\n} from './types';\n\nimport type { InvectPluginDefinition } from '@invect/core';\nimport type { AuthenticationPluginOptions } from './types';\nimport { authentication } from './plugin';\n\n/**\n * Create the auth plugin definition for Invect config.\n *\n * @example\n * ```ts\n * // Express (backend only):\n * auth({ adminEmail: '...' })\n *\n * // Next.js (with frontend):\n * import { authFrontend } from '@invect/user-auth/ui';\n * auth({ adminEmail: '...', frontend: authFrontend })\n * ```\n */\nexport function auth(options: AuthenticationPluginOptions): InvectPluginDefinition {\n return {\n id: 'user-auth',\n name: 'User Authentication',\n backend: authentication(options),\n frontend: options.frontend,\n };\n}\n"],"mappings":"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"}
|
package/dist/backend/index.d.cts
CHANGED
|
@@ -307,15 +307,6 @@ interface AuthenticationPluginOptions {
|
|
|
307
307
|
* @default []
|
|
308
308
|
*/
|
|
309
309
|
publicPaths?: string[];
|
|
310
|
-
/**
|
|
311
|
-
* What to do when session resolution fails (network error, malformed token, etc.).
|
|
312
|
-
*
|
|
313
|
-
* - `'throw'` — Return 401 Unauthorized.
|
|
314
|
-
* - `'continue'` — Set identity to null and proceed (useful for mixed auth).
|
|
315
|
-
*
|
|
316
|
-
* @default 'throw'
|
|
317
|
-
*/
|
|
318
|
-
onSessionError?: 'throw' | 'continue';
|
|
319
310
|
/**
|
|
320
311
|
* Explicit list of global admin accounts to seed and/or promote on startup.
|
|
321
312
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/backend/types.ts","../../src/backend/plugin.ts","../../src/backend/index.ts"],"mappings":";;;;;AAcA;UAAiB,cAAA;EACf,EAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;;;;UAMc,iBAAA;EACf,EAAA;EACA,MAAA;EACA,KAAA;EACA,SAAA,EAAW,IAAA;EAAA,CACV,GAAA;AAAA;;;;UAMc,uBAAA;EACf,IAAA,EAAM,cAAA;EACN,OAAA,EAAS,iBAAA;AAAA;AAAA,UAGM,yBAAA;EACf,eAAA,GACE,KAAA,aACG,OAAA,CAAQ,cAAA;IAAmB,IAAA,GAAO,cAAA;EAAA;EACvC,UAAA,GAAa,MAAA,UAAgB,IAAA,EAAM,MAAA,sBAA4B,OAAA,CAAQ,cAAA;AAAA;AAAA,UAGxD,iBAAA;EACf,eAAA,GAAkB,yBAAA;AAAA;AAAA,UAGH,qBAAA;EACf,KAAA;EACA,EAAA;EACA,IAAA;AAAA;;;;;;;UASe,kBAAA;EApBV;EAsBL,OAAA,GAAU,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EAtBP;EAyBhC,GAAA;IACE,UAAA,GAAa,OAAA;MAAW,OAAA,EAAS,OAAA;IAAA,MAAc,OAAA,CAAQ,uBAAA;IAAA,CACtD,GAAA;EAAA;EA1BoE;EA8BvE,OAAA;IACE,QAAA;IAAA,CACC,GAAA;EAAA;EAGH,QAAA,GAAW,OAAA,CAAQ,iBAAA;EAAA,CAElB,GAAA;AAAA;AA9BH;;;AAAA,UAwCiB,sBAAA;EACf,MAAA;EACA,QAAA;EACA,IAAA;EACA,MAAA;EACA,MAAA;EACA,QAAA;AAAA;;;;;;;;;UAWe,4BAAA;EA7BG;EA+BlB,gBAAA;IACE,OAAA;IACA,aAAA;IACA,wBAAA;IACA,iBAAA;IACA,iBAAA;IACA,UAAA;IACA,6BAAA;EAAA;EAhDiC;EAoDnC,OAAA;IACE,SAAA;IACA,SAAA;IACA,qBAAA;IACA,QAAA;IACA,WAAA;MACE,OAAA;MACA,MAAA;MACA,QAAA;IAAA;EAAA;EAhDQ;EAqDZ,OAAA;IACE,qBAAA;IACA,cAAA;MACE,OAAA;MACA,sBAAA;MACA,oBAAA;MACA,iBAAA;IAAA;EAAA;EA5CJ;EAiDA,eAAA,GAAkB,MAAA;EAhDV;EAmDR,SAAA;IACE,OAAA;IACA,MAAA;IACA,GAAA;EAAA;EAQ0B;EAJ5B,QAAA;IACE,gBAAA;IACA,gBAAA;IACA,YAAA;IACA,uBAAA,GAA0B,sBAAA;IAC1B,qBAAA;MACE,OAAA;MACA,iBAAA;MACA,MAAA;IAAA;IAEF,SAAA;MACE,gBAAA;MACA,iBAAA;IAAA;EAAA;EA7CF;EAkDF,aAAA,GAAgB,MAAA;EAhDd;EAmDF,KAAA,GAAQ,MAAA;EAjDN;EAoDF,aAAA;EAlDI;;;;;;;;EA4DJ,MAAA;EA3CA;;;;;;;;;EAsDA,OAAA,GAAU,KAAA;IAAQ,OAAA;IAAiB,KAAA;EAAA;EAtC/B;;;;;;;;;;EAkDJ,MAAA,aAAmB,mBAAA;AAAA;;;;;;UAQJ,mBAAA;EARuB;EAUtC,gBAAA;EAFe;EAIf,aAAA;;EAEA,WAAA;EAJA;EAMA,cAAA;EAFA;EAIA,uBAAA;EAAA;EAEA,iBAAA;EAEA;EAAA,aAAA;EAGE;EADF,aAAA;IACE,gBAAA;IACA,wBAAA;IACA,YAAA;IACA,YAAA;EAAA;EAMA;EAHF,SAAA;IACE,OAAA;IACA,UAAA;IACA,WAAA;EAAA;AAAA;;;;;;UAaa,2BAAA;EAmFgC;;;;;;;;;;;;;;;;;;EAhE/C,IAAA,GAAO,kBAAA;EAuDF;;;;;;;;;;;EA1CL,QAAA;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/backend/types.ts","../../src/backend/plugin.ts","../../src/backend/index.ts"],"mappings":"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"}
|
package/dist/backend/index.d.mts
CHANGED
|
@@ -307,15 +307,6 @@ interface AuthenticationPluginOptions {
|
|
|
307
307
|
* @default []
|
|
308
308
|
*/
|
|
309
309
|
publicPaths?: string[];
|
|
310
|
-
/**
|
|
311
|
-
* What to do when session resolution fails (network error, malformed token, etc.).
|
|
312
|
-
*
|
|
313
|
-
* - `'throw'` — Return 401 Unauthorized.
|
|
314
|
-
* - `'continue'` — Set identity to null and proceed (useful for mixed auth).
|
|
315
|
-
*
|
|
316
|
-
* @default 'throw'
|
|
317
|
-
*/
|
|
318
|
-
onSessionError?: 'throw' | 'continue';
|
|
319
310
|
/**
|
|
320
311
|
* Explicit list of global admin accounts to seed and/or promote on startup.
|
|
321
312
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/backend/types.ts","../../src/backend/plugin.ts","../../src/backend/index.ts"],"mappings":";;;;;AAcA;UAAiB,cAAA;EACf,EAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;;;;UAMc,iBAAA;EACf,EAAA;EACA,MAAA;EACA,KAAA;EACA,SAAA,EAAW,IAAA;EAAA,CACV,GAAA;AAAA;;;;UAMc,uBAAA;EACf,IAAA,EAAM,cAAA;EACN,OAAA,EAAS,iBAAA;AAAA;AAAA,UAGM,yBAAA;EACf,eAAA,GACE,KAAA,aACG,OAAA,CAAQ,cAAA;IAAmB,IAAA,GAAO,cAAA;EAAA;EACvC,UAAA,GAAa,MAAA,UAAgB,IAAA,EAAM,MAAA,sBAA4B,OAAA,CAAQ,cAAA;AAAA;AAAA,UAGxD,iBAAA;EACf,eAAA,GAAkB,yBAAA;AAAA;AAAA,UAGH,qBAAA;EACf,KAAA;EACA,EAAA;EACA,IAAA;AAAA;;;;;;;UASe,kBAAA;EApBV;EAsBL,OAAA,GAAU,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,QAAA;EAtBP;EAyBhC,GAAA;IACE,UAAA,GAAa,OAAA;MAAW,OAAA,EAAS,OAAA;IAAA,MAAc,OAAA,CAAQ,uBAAA;IAAA,CACtD,GAAA;EAAA;EA1BoE;EA8BvE,OAAA;IACE,QAAA;IAAA,CACC,GAAA;EAAA;EAGH,QAAA,GAAW,OAAA,CAAQ,iBAAA;EAAA,CAElB,GAAA;AAAA;AA9BH;;;AAAA,UAwCiB,sBAAA;EACf,MAAA;EACA,QAAA;EACA,IAAA;EACA,MAAA;EACA,MAAA;EACA,QAAA;AAAA;;;;;;;;;UAWe,4BAAA;EA7BG;EA+BlB,gBAAA;IACE,OAAA;IACA,aAAA;IACA,wBAAA;IACA,iBAAA;IACA,iBAAA;IACA,UAAA;IACA,6BAAA;EAAA;EAhDiC;EAoDnC,OAAA;IACE,SAAA;IACA,SAAA;IACA,qBAAA;IACA,QAAA;IACA,WAAA;MACE,OAAA;MACA,MAAA;MACA,QAAA;IAAA;EAAA;EAhDQ;EAqDZ,OAAA;IACE,qBAAA;IACA,cAAA;MACE,OAAA;MACA,sBAAA;MACA,oBAAA;MACA,iBAAA;IAAA;EAAA;EA5CJ;EAiDA,eAAA,GAAkB,MAAA;EAhDV;EAmDR,SAAA;IACE,OAAA;IACA,MAAA;IACA,GAAA;EAAA;EAQ0B;EAJ5B,QAAA;IACE,gBAAA;IACA,gBAAA;IACA,YAAA;IACA,uBAAA,GAA0B,sBAAA;IAC1B,qBAAA;MACE,OAAA;MACA,iBAAA;MACA,MAAA;IAAA;IAEF,SAAA;MACE,gBAAA;MACA,iBAAA;IAAA;EAAA;EA7CF;EAkDF,aAAA,GAAgB,MAAA;EAhDd;EAmDF,KAAA,GAAQ,MAAA;EAjDN;EAoDF,aAAA;EAlDI;;;;;;;;EA4DJ,MAAA;EA3CA;;;;;;;;;EAsDA,OAAA,GAAU,KAAA;IAAQ,OAAA;IAAiB,KAAA;EAAA;EAtC/B;;;;;;;;;;EAkDJ,MAAA,aAAmB,mBAAA;AAAA;;;;;;UAQJ,mBAAA;EARuB;EAUtC,gBAAA;EAFe;EAIf,aAAA;;EAEA,WAAA;EAJA;EAMA,cAAA;EAFA;EAIA,uBAAA;EAAA;EAEA,iBAAA;EAEA;EAAA,aAAA;EAGE;EADF,aAAA;IACE,gBAAA;IACA,wBAAA;IACA,YAAA;IACA,YAAA;EAAA;EAMA;EAHF,SAAA;IACE,OAAA;IACA,UAAA;IACA,WAAA;EAAA;AAAA;;;;;;UAaa,2BAAA;EAmFgC;;;;;;;;;;;;;;;;;;EAhE/C,IAAA,GAAO,kBAAA;EAuDF;;;;;;;;;;;EA1CL,QAAA;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/backend/types.ts","../../src/backend/plugin.ts","../../src/backend/index.ts"],"mappings":"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"}
|
package/dist/backend/index.mjs
CHANGED
|
@@ -101,16 +101,13 @@ async function resolveSession(auth, headers) {
|
|
|
101
101
|
if (!auth) return null;
|
|
102
102
|
const h = toHeaders(headers);
|
|
103
103
|
try {
|
|
104
|
-
console.log("[auth-debug] resolveSession: cookie header =", h.get("cookie")?.slice(0, 80));
|
|
105
104
|
const result = await auth.api.getSession({ headers: h });
|
|
106
|
-
console.log("[auth-debug] resolveSession: result keys =", result ? Object.keys(result) : "null");
|
|
107
105
|
if (result?.session && result?.user) return {
|
|
108
106
|
session: result.session,
|
|
109
107
|
user: result.user
|
|
110
108
|
};
|
|
111
109
|
return null;
|
|
112
110
|
} catch (err) {
|
|
113
|
-
console.error("[auth-debug] resolveSession threw:", err?.message ?? err);
|
|
114
111
|
return null;
|
|
115
112
|
}
|
|
116
113
|
}
|
|
@@ -774,7 +771,7 @@ async function createMySQLPool(connectionString) {
|
|
|
774
771
|
* ```
|
|
775
772
|
*/
|
|
776
773
|
function authentication(options) {
|
|
777
|
-
const { prefix = DEFAULT_PREFIX, mapUser: customMapUser, mapRole = defaultMapRole, publicPaths = [],
|
|
774
|
+
const { prefix = DEFAULT_PREFIX, mapUser: customMapUser, mapRole = defaultMapRole, publicPaths = [], globalAdmins = [] } = options;
|
|
778
775
|
let auth = options.auth ?? null;
|
|
779
776
|
/** Narrow `auth` for call-sites that run only after init. */
|
|
780
777
|
function requireAuth() {
|
|
@@ -1385,7 +1382,7 @@ function authentication(options) {
|
|
|
1385
1382
|
role: identity?.role
|
|
1386
1383
|
});
|
|
1387
1384
|
context.identity = identity;
|
|
1388
|
-
if (!identity
|
|
1385
|
+
if (!identity) return { response: new Response(JSON.stringify({
|
|
1389
1386
|
error: "Unauthorized",
|
|
1390
1387
|
message: "Valid session required. Sign in via better-auth."
|
|
1391
1388
|
}), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/backend/plugin.ts","../../src/backend/index.ts"],"sourcesContent":["import type {\n InvectPlugin,\n InvectIdentity,\n InvectRole,\n InvectPermission,\n InvectPluginSchema,\n} from '@invect/core';\nimport type {\n AuthenticationPluginOptions,\n BetterAuthContext,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthInstance,\n} from './types';\nimport {\n AUTH_ADMIN_ROLE,\n AUTH_ASSIGNABLE_ROLES,\n AUTH_DEFAULT_ROLE,\n AUTH_VISIBLE_ROLES,\n isAuthAssignableRole,\n isAuthVisibleRole,\n} from '../shared/roles';\n\ntype PluginLoggerLike = {\n error: (message: string, meta?: unknown) => void;\n info?: (message: string, meta?: unknown) => void;\n debug?: (message: string, meta?: unknown) => void;\n warn?: (message: string, meta?: unknown) => void;\n};\n\ntype BetterAuthApiUser = {\n id?: string;\n email?: string | null;\n name?: string | null;\n role?: string | null;\n [key: string]: unknown;\n};\n\ntype BetterAuthApiUserResult = {\n user?: BetterAuthApiUser | null;\n};\n\ntype BetterAuthHeadersQueryMethod<TResult> = (args: {\n headers: Headers;\n query: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyParamsMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n params: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthBodyMethod<TResult> = (args: { body: Record<string, unknown> }) => Promise<TResult>;\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PREFIX = 'auth';\n\n/**\n * Default role mapping: keep admin/RBAC roles aligned and fall back to default.\n */\nfunction defaultMapRole(role: string | null | undefined): InvectRole {\n if (!role) {\n return AUTH_DEFAULT_ROLE;\n }\n if (role === 'viewer' || role === 'readonly') {\n return 'viewer';\n }\n if (isAuthVisibleRole(role)) {\n return role;\n }\n return AUTH_DEFAULT_ROLE;\n}\n\n/**\n * Default user → identity mapping.\n */\nfunction defaultMapUser(\n user: BetterAuthUser,\n _session: BetterAuthSession,\n mapRole: (role: string | null | undefined) => InvectRole,\n): InvectIdentity {\n const resolvedRole = mapRole(user.role);\n\n return {\n id: user.id,\n name: user.name ?? user.email ?? undefined,\n role: resolvedRole,\n permissions: resolvedRole === AUTH_ADMIN_ROLE ? ['admin:*'] : undefined,\n resourceAccess:\n resolvedRole === AUTH_ADMIN_ROLE\n ? {\n flows: '*',\n credentials: '*',\n }\n : undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Simple in-memory sliding-window rate limiter.\n *\n * Keyed by IP address (or a fallback identifier). Tracks request timestamps\n * per window and rejects requests that exceed the limit with HTTP 429.\n *\n * Only applied to authentication-sensitive endpoints (sign-in, sign-up,\n * password reset) to prevent brute-force attacks. Session reads (GET) are\n * not rate-limited.\n */\nclass RateLimiter {\n private windows = new Map<string, number[]>();\n private readonly maxRequests: number;\n private readonly windowMs: number;\n\n constructor(maxRequests = 10, windowMs = 60_000) {\n this.maxRequests = maxRequests;\n this.windowMs = windowMs;\n }\n\n /**\n * Returns `true` if the request should be rejected (over limit).\n */\n isRateLimited(key: string): { limited: boolean; retryAfterMs?: number } {\n const now = Date.now();\n const windowStart = now - this.windowMs;\n\n let timestamps = this.windows.get(key);\n if (!timestamps) {\n timestamps = [];\n this.windows.set(key, timestamps);\n }\n\n // Prune expired entries\n const valid = timestamps.filter((t) => t > windowStart);\n this.windows.set(key, valid);\n\n if (valid.length >= this.maxRequests) {\n const oldestInWindow = valid[0] ?? now;\n const retryAfterMs = oldestInWindow + this.windowMs - now;\n return { limited: true, retryAfterMs: Math.max(retryAfterMs, 1000) };\n }\n\n valid.push(now);\n return { limited: false };\n }\n\n /** Periodic cleanup of stale keys to prevent memory leaks. */\n cleanup(): void {\n const now = Date.now();\n for (const [key, timestamps] of this.windows) {\n const valid = timestamps.filter((t) => t > now - this.windowMs);\n if (valid.length === 0) {\n this.windows.delete(key);\n } else {\n this.windows.set(key, valid);\n }\n }\n }\n}\n\n/** Auth-sensitive path segments that should be rate-limited. */\nconst RATE_LIMITED_AUTH_PATHS = ['/sign-in/', '/sign-up/', '/forgot-password', '/reset-password'];\n\n/**\n * Convert a Node.js-style `IncomingHttpHeaders` record or a `Headers` instance\n * to a standard `Headers` object for passing to better-auth.\n */\nfunction toHeaders(raw: Record<string, string | undefined> | Headers): Headers {\n if (raw instanceof Headers) {\n return raw;\n }\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(raw)) {\n if (value !== undefined) {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * Resolve the session from a better-auth instance using request headers.\n */\nasync function resolveSession(\n auth: BetterAuthInstance | null,\n headers: Record<string, string | undefined> | Headers,\n): Promise<{ session: BetterAuthSession; user: BetterAuthUser } | null> {\n if (!auth) {\n return null;\n }\n\n const h = toHeaders(headers);\n\n try {\n // eslint-disable-next-line no-console\n console.log('[auth-debug] resolveSession: cookie header =', h.get('cookie')?.slice(0, 80));\n const result = await auth.api.getSession({\n headers: h,\n });\n // eslint-disable-next-line no-console\n console.log(\n '[auth-debug] resolveSession: result keys =',\n result ? Object.keys(result) : 'null',\n );\n if (result?.session && result?.user) {\n return {\n session: result.session,\n user: result.user,\n };\n }\n return null;\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[auth-debug] resolveSession threw:', (err as Error)?.message ?? err);\n return null;\n }\n}\n\nasync function callBetterAuthHandler(\n auth: BetterAuthInstance | null,\n request: Request,\n path: string,\n init?: {\n method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n query?: Record<string, string | undefined>;\n body?: Record<string, unknown>;\n },\n): Promise<{ status: number; body: unknown } | null> {\n if (!auth) {\n return null;\n }\n\n const basePath = auth.options?.basePath ?? '/api/auth';\n const targetUrl = new URL(`${basePath}${path}`, request.url);\n for (const [key, value] of Object.entries(init?.query ?? {})) {\n if (value !== undefined) {\n targetUrl.searchParams.set(key, value);\n }\n }\n\n const headers = new Headers(request.headers);\n const hasBody = init?.body !== undefined;\n if (hasBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n const authRequest = new Request(targetUrl.toString(), {\n method: init?.method ?? 'GET',\n headers,\n body: hasBody ? JSON.stringify(init?.body) : undefined,\n });\n\n const response = await auth.handler(authRequest);\n const text = await response.text();\n\n if (!text) {\n return { status: response.status, body: null };\n }\n\n try {\n return { status: response.status, body: JSON.parse(text) };\n } catch {\n return { status: response.status, body: text };\n }\n}\n\nasync function getAuthContext(auth: BetterAuthInstance | null): Promise<BetterAuthContext | null> {\n if (!auth) {\n return null;\n }\n try {\n return (await auth.$context) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction isBetterAuthUser(value: unknown): value is BetterAuthUser {\n return !!value && typeof value === 'object' && 'id' in value && typeof value.id === 'string';\n}\n\nfunction unwrapFoundUser(\n result: BetterAuthUser | { user?: BetterAuthUser | null } | null | undefined,\n): BetterAuthUser | null {\n if (!result) {\n return null;\n }\n\n if (typeof result === 'object' && 'user' in result) {\n const nestedUser = result.user;\n if (isBetterAuthUser(nestedUser)) {\n return nestedUser;\n }\n\n return null;\n }\n\n if (isBetterAuthUser(result)) {\n return result;\n }\n\n return null;\n}\n\nfunction toAuthApiErrorResponse(\n fallbackError: string,\n error: unknown,\n): { status: number; body: Record<string, unknown> } {\n if (error instanceof Response) {\n return {\n status: error.status || 500,\n body: { error: fallbackError, message: error.statusText || fallbackError },\n };\n }\n\n const status =\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status?: unknown }).status === 'number'\n ? ((error as { status: number }).status ?? 500)\n : error &&\n typeof error === 'object' &&\n 'statusCode' in error &&\n typeof (error as { statusCode?: unknown }).statusCode === 'number'\n ? ((error as { statusCode: number }).statusCode ?? 500)\n : 500;\n\n const message =\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ? (error as { message: string }).message || fallbackError\n : fallbackError;\n\n const code =\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n typeof (error as { code?: unknown }).code === 'string'\n ? (error as { code: string }).code\n : undefined;\n\n return {\n status,\n body: {\n error: fallbackError,\n message,\n ...(code ? { code } : {}),\n },\n };\n}\n\nfunction sanitizeForLogging(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeForLogging(item));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, nestedValue]) => {\n if (/password|token|secret/i.test(key)) {\n return [key, '[REDACTED]'];\n }\n\n return [key, sanitizeForLogging(nestedValue)];\n }),\n );\n }\n\n return value;\n}\n\nfunction getErrorLogDetails(error: unknown): Record<string, unknown> {\n if (error instanceof Response) {\n return {\n type: 'Response',\n status: error.status,\n statusText: error.statusText,\n };\n }\n\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n ...(error && typeof error === 'object' && 'cause' in error\n ? { cause: sanitizeForLogging((error as Error & { cause?: unknown }).cause) }\n : {}),\n ...(error && typeof error === 'object' && 'code' in error\n ? { code: (error as Error & { code?: unknown }).code }\n : {}),\n ...(error && typeof error === 'object' && 'status' in error\n ? { status: (error as Error & { status?: unknown }).status }\n : {}),\n ...(error && typeof error === 'object' && 'statusCode' in error\n ? { statusCode: (error as Error & { statusCode?: unknown }).statusCode }\n : {}),\n };\n }\n\n if (error && typeof error === 'object') {\n return sanitizeForLogging(error) as Record<string, unknown>;\n }\n\n return { value: error };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract schema — defines the tables the user-auth plugin requires\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract schema for the user-auth plugin's database tables.\n *\n * These definitions allow the Invect CLI (`npx invect-cli generate`) to include\n * the auth tables when generating Drizzle/Prisma schema files.\n *\n * The shapes match Better Auth's default table structure. If your Better Auth\n * config adds extra fields (e.g., via plugins like `twoFactor`, `organization`),\n * you can extend these in your own config.\n */\nexport const USER_AUTH_SCHEMA: InvectPluginSchema = {\n user: {\n tableName: 'user',\n order: 1,\n fields: {\n id: { type: 'string', primaryKey: true },\n name: { type: 'string', required: true },\n email: { type: 'string', required: true, unique: true },\n emailVerified: { type: 'boolean', required: true, defaultValue: false },\n image: { type: 'string', required: false },\n role: { type: 'string', required: false, defaultValue: AUTH_DEFAULT_ROLE },\n banned: { type: 'boolean', required: false, defaultValue: false },\n banReason: { type: 'string', required: false },\n banExpires: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n session: {\n tableName: 'session',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n expiresAt: { type: 'date', required: true },\n token: { type: 'string', required: true, unique: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n ipAddress: { type: 'string', required: false },\n userAgent: { type: 'string', required: false },\n impersonatedBy: { type: 'string', required: false },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n },\n },\n\n account: {\n tableName: 'account',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n accountId: { type: 'string', required: true },\n providerId: { type: 'string', required: true },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n accessToken: { type: 'string', required: false },\n refreshToken: { type: 'string', required: false },\n idToken: { type: 'string', required: false },\n accessTokenExpiresAt: { type: 'date', required: false },\n refreshTokenExpiresAt: { type: 'date', required: false },\n scope: { type: 'string', required: false },\n password: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n verification: {\n tableName: 'verification',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n identifier: { type: 'string', required: true },\n value: { type: 'string', required: true },\n expiresAt: { type: 'date', required: true },\n createdAt: { type: 'date', required: false },\n updatedAt: { type: 'date', required: false },\n },\n },\n\n // ----- Flow access control table (moved from core) -----\n flowAccess: {\n tableName: 'flow_access',\n order: 3,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n },\n userId: { type: 'string', required: false },\n teamId: { type: 'string', required: false },\n permission: {\n type: 'string',\n required: true,\n defaultValue: 'viewer',\n },\n grantedBy: { type: 'string', required: false },\n grantedAt: { type: 'date', required: true, defaultValue: 'now()' },\n expiresAt: { type: 'date', required: false },\n },\n },\n};\n\n/**\n * Abstract schema for the Better Auth API Key plugin's `apikey` table.\n *\n * Only merged into the plugin schema when `apiKey` is enabled.\n *\n * @see https://better-auth.com/docs/plugins/api-key/reference#schema\n */\nconst API_KEY_SCHEMA: InvectPluginSchema = {\n apikey: {\n tableName: 'apikey',\n order: 3,\n fields: {\n id: { type: 'string', primaryKey: true },\n configId: { type: 'string', required: true, defaultValue: 'default' },\n name: { type: 'string', required: false },\n start: { type: 'string', required: false },\n prefix: { type: 'string', required: false },\n key: { type: 'string', required: true },\n referenceId: { type: 'string', required: true },\n refillInterval: { type: 'number', required: false },\n refillAmount: { type: 'number', required: false },\n lastRefillAt: { type: 'date', required: false },\n enabled: { type: 'boolean', required: false, defaultValue: true },\n rateLimitEnabled: { type: 'boolean', required: false },\n rateLimitTimeWindow: { type: 'number', required: false },\n rateLimitMax: { type: 'number', required: false },\n requestCount: { type: 'number', required: false },\n remaining: { type: 'number', required: false },\n lastRequest: { type: 'date', required: false },\n expiresAt: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n permissions: { type: 'string', required: false },\n metadata: { type: 'string', required: false },\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Internal Better Auth instance creation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Better Auth instance internally using Invect's database config.\n *\n * Dynamically imports `better-auth` (a required peer dependency) and creates\n * a fully-configured instance with email/password auth, the admin plugin,\n * and session caching.\n *\n * Database resolution order:\n * 1. Explicit `options.database` (any value `betterAuth({ database })` accepts)\n * 2. Auto-created client from Invect's `database.connectionString`\n */\nasync function createInternalBetterAuth(\n invectConfig: Record<string, unknown>,\n options: AuthenticationPluginOptions,\n logger: PluginLoggerLike,\n): Promise<BetterAuthInstance> {\n // 1. Dynamic-import better-auth (peer dependency)\n let betterAuthFn: (config: Record<string, unknown>) => unknown;\n let adminPlugin: (config?: Record<string, unknown>) => unknown;\n\n try {\n const betterAuthModule = await import('better-auth');\n betterAuthFn = betterAuthModule.betterAuth;\n } catch {\n throw new Error(\n 'Could not import \"better-auth\". It is a required peer dependency of @invect/user-auth. ' +\n 'Install it with: npm install better-auth',\n );\n }\n\n try {\n const pluginsModule = await import('better-auth/plugins');\n adminPlugin = pluginsModule.admin;\n } catch {\n throw new Error(\n 'Could not import \"better-auth/plugins\". Ensure better-auth is properly installed.',\n );\n }\n\n // 2. Resolve database\n let database: unknown = options.database;\n\n if (!database) {\n const dbConfig = invectConfig.database as\n | { type?: string; connectionString?: string }\n | undefined;\n\n if (!dbConfig?.connectionString) {\n throw new Error(\n 'Cannot create internal Better Auth instance: no database configuration found. ' +\n 'Either provide `auth` (a Better Auth instance), `database`, or ensure ' +\n 'Invect database has a connectionString.',\n );\n }\n\n const connStr = dbConfig.connectionString;\n const dbType = (dbConfig.type ?? 'sqlite').toLowerCase();\n\n if (dbType === 'sqlite') {\n database = await createSQLiteClient(connStr, logger);\n } else if (dbType === 'pg' || dbType === 'postgresql') {\n database = await createPostgresPool(connStr);\n } else if (dbType === 'mysql') {\n database = await createMySQLPool(connStr);\n } else {\n throw new Error(\n `Unsupported database type for internal Better Auth: \"${dbType}\". ` +\n 'Supported: sqlite, pg, mysql. Alternatively, provide your own Better Auth instance via `auth`.',\n );\n }\n }\n\n // 3. Resolve base URL\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n `http://localhost:${process.env.PORT ?? '3000'}`;\n\n // 4. Resolve trusted origins\n const configuredOrigins = options.trustedOrigins;\n const trustedOrigins =\n configuredOrigins ??\n ((request: Request) => {\n const trusted = new Set<string>([baseURL, 'http://localhost:5173', 'http://localhost:5174']);\n try {\n if (request) {\n trusted.add(new URL(request.url).origin);\n }\n } catch {\n // Ignore malformed URLs\n }\n return Array.from(trusted);\n });\n\n // 5. Build passthrough config from betterAuthOptions\n const passthrough = options.betterAuthOptions ?? {};\n\n const emailAndPassword = {\n enabled: true,\n ...passthrough.emailAndPassword,\n };\n\n const session = {\n cookieCache: { enabled: true, maxAge: 5 * 60 },\n ...passthrough.session,\n // Merge cookieCache if both exist\n ...(passthrough.session?.cookieCache\n ? {\n cookieCache: {\n enabled: true,\n maxAge: 5 * 60,\n ...passthrough.session.cookieCache,\n },\n }\n : {}),\n };\n\n // 6. Resolve secret — fall back to INVECT_ENCRYPTION_KEY when no explicit\n // secret or secrets are provided. This lets deployments that already set\n // INVECT_ENCRYPTION_KEY get a valid Better Auth secret for free.\n let resolvedSecret = passthrough.secret;\n const resolvedSecrets = passthrough.secrets;\n\n if (!resolvedSecret && !resolvedSecrets) {\n const envKey = process.env.INVECT_ENCRYPTION_KEY;\n if (envKey) {\n resolvedSecret = envKey;\n logger.debug?.(\n 'Using INVECT_ENCRYPTION_KEY as Better Auth secret (no explicit secret/secrets provided)',\n );\n }\n }\n\n // 7. Optionally load the Better Auth API Key plugin\n // Merge top-level `options.apiKey` with `passthrough.apiKey` (top-level wins).\n const apiKeyOpt = options.apiKey ?? passthrough.apiKey;\n const betterAuthPlugins: unknown[] = [\n adminPlugin({ defaultRole: AUTH_DEFAULT_ROLE, adminRoles: [AUTH_ADMIN_ROLE] }),\n ];\n\n if (apiKeyOpt) {\n let apiKeyPluginFn: (config?: Record<string, unknown>) => unknown;\n try {\n // Dynamic import — @better-auth/api-key is an optional peer dependency.\n // Using a variable to prevent TypeScript from resolving the module at\n // type-check time (it may not be installed).\n const pkg = '@better-auth/api-key';\n const apiKeyModule: Record<string, unknown> = await import(pkg);\n apiKeyPluginFn = apiKeyModule.apiKey as (config?: Record<string, unknown>) => unknown;\n } catch {\n throw new Error(\n 'Could not import \"@better-auth/api-key\". Install it with: npm install @better-auth/api-key',\n );\n }\n\n const apiKeyConfig: Record<string, unknown> =\n typeof apiKeyOpt === 'object' ? { ...apiKeyOpt } : {};\n // Default API key header to \"x-invect-token\" (overridable via options).\n if (apiKeyConfig.apiKeyHeaders === undefined) {\n apiKeyConfig.apiKeyHeaders = 'x-invect-token';\n }\n // Ensure getSession() can resolve API keys to sessions.\n // Without this, auth.api.getSession() only checks cookies/tokens.\n if (apiKeyConfig.enableSessionForAPIKeys === undefined) {\n apiKeyConfig.enableSessionForAPIKeys = true;\n }\n betterAuthPlugins.push(apiKeyPluginFn(apiKeyConfig));\n logger.info?.('Better Auth API Key plugin enabled');\n }\n\n // 8. Create the instance\n logger.info?.('Creating internal Better Auth instance');\n\n const instance = betterAuthFn({\n baseURL,\n database,\n emailAndPassword,\n plugins: betterAuthPlugins,\n session,\n trustedOrigins,\n // Spread optional passthrough fields\n ...(passthrough.socialProviders ? { socialProviders: passthrough.socialProviders } : {}),\n ...(passthrough.account ? { account: passthrough.account } : {}),\n ...(passthrough.rateLimit ? { rateLimit: passthrough.rateLimit } : {}),\n // Default cookie prefix to \"invect\" (cookies become invect.session_token).\n // Users can override via betterAuthOptions.advanced.cookiePrefix.\n advanced: {\n cookiePrefix: 'invect',\n ...passthrough.advanced,\n },\n ...(passthrough.databaseHooks ? { databaseHooks: passthrough.databaseHooks } : {}),\n ...(passthrough.hooks ? { hooks: passthrough.hooks } : {}),\n ...(passthrough.disabledPaths ? { disabledPaths: passthrough.disabledPaths } : {}),\n ...(resolvedSecret ? { secret: resolvedSecret } : {}),\n ...(resolvedSecrets ? { secrets: resolvedSecrets } : {}),\n });\n\n return instance as BetterAuthInstance;\n}\n\n/** Create a SQLite client using better-sqlite3. */\nasync function createSQLiteClient(connectionString: string, logger: PluginLoggerLike) {\n try {\n const { default: Database } = await import('better-sqlite3');\n const { Kysely, SqliteDialect, CamelCasePlugin } = await import('kysely');\n logger.debug?.(`Using better-sqlite3 for internal Better Auth database`);\n // Strip file: prefix if present\n let dbPath = connectionString.replace(/^file:/, '');\n if (dbPath === '') {\n dbPath = ':memory:';\n }\n const sqliteDb = new Database(dbPath);\n // Return in Better Auth's { db, type } format so it uses our Kysely instance\n // directly, preserving CamelCasePlugin for snake_case column mapping.\n const kyselyDb = new Kysely({\n dialect: new SqliteDialect({ database: sqliteDb }),\n plugins: [new CamelCasePlugin()],\n });\n return { db: kyselyDb, type: 'sqlite' as const };\n } catch (err) {\n if (err instanceof Error && err.message.includes('better-sqlite3')) {\n throw new Error(\n 'Cannot create SQLite database for internal Better Auth: ' +\n 'install better-sqlite3 (npm install better-sqlite3). ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n throw err;\n }\n}\n\n/** Create a PostgreSQL pool from a connection string. */\nasync function createPostgresPool(connectionString: string) {\n try {\n const { Pool } = await import('pg');\n return new Pool({ connectionString });\n } catch {\n throw new Error(\n 'Cannot create PostgreSQL pool for internal Better Auth: ' +\n 'install the \"pg\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n/** Create a MySQL pool from a connection string. */\nasync function createMySQLPool(connectionString: string) {\n try {\n const mysql = await import('mysql2/promise');\n return mysql.createPool(connectionString);\n } catch {\n throw new Error(\n 'Cannot create MySQL pool for internal Better Auth: ' +\n 'install the \"mysql2\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the Invect user-auth plugin (a light wrapper around Better Auth).\n *\n * This plugin:\n *\n * 1. **Proxies Better Auth routes** — All of Better Auth's HTTP endpoints\n * (sign-in, sign-up, sign-out, OAuth callbacks, session, etc.) are mounted\n * under the plugin endpoint space at `/plugins/auth/api/auth/*` (configurable).\n *\n * 2. **Resolves sessions → identities** — On every Invect API request, the\n * `onRequest` hook reads the session cookie / bearer token via\n * `auth.api.getSession()` and populates `InvectIdentity`.\n *\n * 3. **Handles authorization** — The `onAuthorize` hook lets Better Auth's\n * session decide whether a request is allowed.\n *\n * @example\n * ```ts\n * // Simple: let the plugin manage Better Auth internally\n * import { authentication } from '@invect/user-auth';\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({\n * globalAdmins: [{ email: 'admin@co.com', pw: 'secret' }],\n * })],\n * }));\n * ```\n *\n * @example\n * ```ts\n * // Advanced: provide your own better-auth instance\n * import { betterAuth } from 'better-auth';\n * import { authentication } from '@invect/user-auth';\n *\n * const auth = betterAuth({\n * database: { ... },\n * emailAndPassword: { enabled: true },\n * // ... your better-auth config\n * });\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({ auth })],\n * }));\n * ```\n */\nexport function authentication(options: AuthenticationPluginOptions): InvectPlugin {\n const {\n prefix = DEFAULT_PREFIX,\n mapUser: customMapUser,\n mapRole = defaultMapRole,\n publicPaths = [],\n onSessionError = 'throw',\n globalAdmins = [],\n } = options;\n\n // Mutable — assigned in `init` when no external instance was provided.\n let auth: BetterAuthInstance | null = options.auth ?? null;\n\n /** Narrow `auth` for call-sites that run only after init. */\n function requireAuth(): BetterAuthInstance {\n if (!auth) {\n throw new Error('Auth plugin not initialized');\n }\n return auth;\n }\n\n let endpointLogger: PluginLoggerLike = console;\n\n // Determine Better Auth's basePath (defaults to /api/auth).\n // Computed lazily since `auth` may not exist until `init`.\n let betterAuthBasePath = '/api/auth';\n\n /**\n * Resolve an identity from a request's headers.\n */\n async function getIdentityFromHeaders(\n headers: Record<string, string | undefined>,\n ): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await resolveSession(auth, headers);\n if (!result) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(result.user, result.session);\n }\n\n return defaultMapUser(result.user, result.session, mapRole);\n }\n\n async function getIdentityFromRequest(request: Request): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await callBetterAuthHandler(auth, request, '/get-session');\n const body = result?.body as { session?: BetterAuthSession; user?: BetterAuthUser } | null;\n if (!body?.session || !body?.user) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(body.user, body.session);\n }\n\n return defaultMapUser(body.user, body.session, mapRole);\n }\n\n async function resolveEndpointIdentity(ctx: {\n identity: InvectIdentity | null;\n request: Request;\n }): Promise<InvectIdentity | null> {\n if (ctx.identity) {\n return ctx.identity;\n }\n\n return getIdentityFromRequest(ctx.request);\n }\n\n // Rate limiter for auth-sensitive endpoints (sign-in, sign-up, password reset)\n // 10 attempts per IP per 60 seconds by default\n const authRateLimiter = new RateLimiter(10, 60_000);\n\n // Periodic cleanup every 5 minutes to prevent memory leaks\n const cleanupInterval = setInterval(() => authRateLimiter.cleanup(), 5 * 60_000);\n cleanupInterval.unref?.(); // Don't keep the process alive\n\n // ----- Build plugin endpoint list -----\n // We create a catch-all endpoint that proxies everything under the prefix\n // to better-auth's handler. This covers all auth routes: sign-in, sign-up,\n // OAuth callbacks, session management, etc.\n\n const proxyMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;\n\n const endpoints = proxyMethods.map((method) => ({\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: `/${prefix}/*` as const,\n isPublic: true, // Auth routes must be accessible without existing session\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n request: Request;\n }) => {\n // Build a new Request for better-auth's handler.\n // The incoming path is: /plugins/auth/api/auth/sign-in/email (for example)\n // We need to reconstruct the URL that better-auth expects.\n const incomingUrl = new URL(ctx.request.url);\n endpointLogger.debug?.(`[auth-proxy] ${method} ${incomingUrl.pathname}`);\n\n // Strip the /plugins/<prefix> prefix from the pathname to get\n // the better-auth-relative path\n const pluginPrefixPattern = `/plugins/${prefix}`;\n let authPath = incomingUrl.pathname;\n const prefixIdx = authPath.indexOf(pluginPrefixPattern);\n if (prefixIdx !== -1) {\n authPath = authPath.slice(prefixIdx + pluginPrefixPattern.length);\n }\n if (!authPath.startsWith('/')) {\n authPath = '/' + authPath;\n }\n\n // ── Rate-limit auth-sensitive POST endpoints ──────────\n if (method === 'POST' && RATE_LIMITED_AUTH_PATHS.some((p) => authPath.includes(p))) {\n const clientIp =\n ctx.headers['x-forwarded-for']?.split(',')[0]?.trim() ||\n ctx.headers['x-real-ip'] ||\n 'unknown';\n const { limited, retryAfterMs } = authRateLimiter.isRateLimited(clientIp);\n if (limited) {\n return new Response(\n JSON.stringify({\n error: 'Too Many Requests',\n message: 'Too many authentication attempts. Please try again later.',\n }),\n {\n status: 429,\n headers: {\n 'content-type': 'application/json',\n 'retry-after': String(Math.ceil((retryAfterMs ?? 60_000) / 1000)),\n },\n },\n );\n }\n }\n\n // Construct the URL that better-auth expects\n const authUrl = new URL(`${incomingUrl.origin}${authPath}${incomingUrl.search}`);\n endpointLogger.debug?.(\n `[auth-proxy] Forwarding to better-auth: ${method} ${authUrl.pathname}`,\n );\n\n // Clone/forward the request to better-auth\n const authRequest = new Request(authUrl.toString(), {\n method: ctx.request.method,\n headers: ctx.request.headers,\n body: method !== 'GET' && method !== 'DELETE' ? ctx.request.body : undefined,\n // @ts-expect-error - duplex is needed for streaming bodies\n duplex: method !== 'GET' && method !== 'DELETE' ? 'half' : undefined,\n });\n\n // Let better-auth handle it\n const response = await requireAuth().handler(authRequest);\n endpointLogger.debug?.(`[auth-proxy] Response: ${response.status} ${response.statusText}`, {\n setCookie: response.headers.get('set-cookie') ? 'present' : 'absent',\n contentType: response.headers.get('content-type'),\n });\n return response;\n },\n }));\n\n // ----- Resolve API key opt from top-level or passthrough -----\n const apiKeyEnabled = !!(options.apiKey ?? options.betterAuthOptions?.apiKey);\n\n // ----- Build schema (conditionally includes apikey table) -----\n const schema: InvectPluginSchema = apiKeyEnabled\n ? { ...USER_AUTH_SCHEMA, ...API_KEY_SCHEMA }\n : USER_AUTH_SCHEMA;\n\n const requiredTables = ['user', 'session', 'account', 'verification', 'flow_access'];\n if (apiKeyEnabled) {\n requiredTables.push('apikey');\n }\n\n // ----- Build the plugin -----\n return {\n id: 'user-auth',\n name: 'User Auth',\n\n // Abstract schema for auth tables — the CLI reads this to generate\n // Drizzle/Prisma schema files that include auth tables automatically.\n schema,\n\n // Also declare requiredTables for the startup existence check.\n requiredTables,\n setupInstructions:\n 'Run `npx invect-cli generate` to add the better-auth tables to your schema, ' +\n 'then `npx drizzle-kit push` (or `npx invect-cli migrate`) to apply.',\n\n endpoints: [\n // ── Auth Info (specific routes must come BEFORE the catch-all proxy) ───\n\n {\n method: 'GET' as const,\n path: `/${prefix}/me`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: {\n getPermissions: (identity: InvectIdentity | null) => string[];\n getResolvedRole: (identity: InvectIdentity) => string | null;\n };\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n const permissions = ctx.core.getPermissions(identity);\n const resolvedRole = identity ? ctx.core.getResolvedRole(identity) : null;\n\n return {\n status: 200,\n body: {\n identity: identity\n ? {\n id: identity.id,\n name: identity.name,\n role: identity.role,\n resolvedRole,\n }\n : null,\n permissions,\n isAuthenticated: !!identity,\n },\n };\n },\n },\n\n {\n method: 'GET' as const,\n path: `/${prefix}/roles`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: { getAvailableRoles: () => unknown };\n }) => {\n const roles = ctx.core.getAvailableRoles() as Array<{\n role: string;\n permissions: string[];\n }>;\n const missingRoles = AUTH_VISIBLE_ROLES.filter(\n (role) => !roles.some((entry) => entry.role === role),\n ).map((role) => ({ role, permissions: [] }));\n return { status: 200, body: { roles: [...roles, ...missingRoles] } };\n },\n },\n\n // ── Auth Info ────────────────────────────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/info`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity) {\n return {\n status: 401,\n body: { error: 'Unauthorized' },\n };\n }\n return {\n status: 200,\n body: {\n apiKeysEnabled: apiKeyEnabled,\n },\n };\n },\n },\n\n // ── API Key Management (admin-only) ────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/list', {\n method: 'GET',\n query: ctx.query,\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to list API keys' },\n };\n } catch (err) {\n endpointLogger.error('Failed to list API keys', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list API keys', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const {\n name,\n expiresIn,\n prefix: keyPrefix,\n } = ctx.body as {\n name?: string;\n expiresIn?: number;\n prefix?: string;\n };\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/create', {\n method: 'POST',\n body: {\n name: name || undefined,\n expiresIn: expiresIn || undefined,\n prefix: keyPrefix || undefined,\n },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 201, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to create API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to create API key', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create API key', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/api-keys/:keyId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const { keyId } = ctx.params;\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/delete', {\n method: 'POST',\n body: { keyId },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: { success: true } };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to delete API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete API key', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete API key', err);\n }\n },\n },\n\n // ── User Management (admin-only) ──────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth's admin listUsers API first\n if (typeof api.listUsers === 'function') {\n const listUsers = api.listUsers as BetterAuthHeadersQueryMethod<\n { users?: unknown[] } | unknown[] | null\n >;\n const result = await listUsers({\n headers,\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n });\n const users = Array.isArray(result) ? result : (result?.users ?? []);\n return { status: 200, body: { users } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/list-users',\n {\n method: 'GET',\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return {\n status: 200,\n body: fallbackResult.body as Record<string, unknown>,\n };\n }\n\n // Fallback: query the user table directly via an internal request\n // to better-auth's get-session endpoint for the current user\n return {\n status: 200,\n body: {\n users: [],\n message:\n 'User listing requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to list users', {\n identity: sanitizeForLogging(identity),\n query: sanitizeForLogging(ctx.query),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list users', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { email, password, name, role } = ctx.body as {\n email?: string;\n password?: string;\n name?: string;\n role?: string;\n };\n\n if (!email || !password) {\n return {\n status: 400,\n body: { error: 'email and password are required' },\n };\n }\n\n if (role !== undefined && !isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n let result: BetterAuthApiUserResult | null = null;\n\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else if (typeof api.signUpEmail === 'function') {\n const signUpEmail =\n api.signUpEmail as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else {\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/create-user',\n {\n method: 'POST',\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n result = fallbackResult.body as BetterAuthApiUserResult;\n }\n }\n\n if (\n !result &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n return {\n status: 500,\n body: { error: 'Auth API does not support user creation' },\n };\n }\n\n if (!result?.user) {\n return {\n status: 400,\n body: {\n error: 'Failed to create user',\n message: 'User may already exist',\n },\n };\n }\n\n return {\n status: 201,\n body: {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n role: result.user.role,\n },\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to create user', {\n identity: sanitizeForLogging(identity),\n body: sanitizeForLogging({ email, name, role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create user', err);\n }\n },\n },\n\n {\n method: 'PATCH' as const,\n path: `/${prefix}/users/:userId/role`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n const { role } = ctx.body as { role?: string };\n\n if (!isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth admin API for updating user\n if (typeof api.setRole === 'function') {\n const setRole = api.setRole as BetterAuthHeadersBodyMethod<unknown>;\n await setRole({\n headers,\n body: { userId, role },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/set-role',\n {\n method: 'POST',\n body: { userId, role },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId, role } };\n }\n\n // Fallback: try updateUser API\n if (typeof api.updateUser === 'function') {\n const updateUser = api.updateUser as BetterAuthHeadersBodyParamsMethod<unknown>;\n await updateUser({\n headers,\n body: { role },\n params: { id: userId },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'Role update requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to update role', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n body: sanitizeForLogging({ role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to update role', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/users/:userId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n\n // Prevent deleting yourself\n if (identity.id === userId) {\n return {\n status: 400,\n body: { error: 'Cannot delete your own account' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n if (typeof api.removeUser === 'function') {\n const removeUser = api.removeUser as BetterAuthHeadersBodyMethod<unknown>;\n await removeUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/remove-user',\n {\n method: 'POST',\n body: { userId },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId } };\n }\n\n // Try deleteUser\n if (typeof api.deleteUser === 'function') {\n const deleteUser = api.deleteUser as BetterAuthHeadersBodyMethod<unknown>;\n await deleteUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'User deletion requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete user', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete user', err);\n }\n },\n },\n\n // ── Auth proxy catch-all (must come LAST so specific routes above win) ──\n ...endpoints,\n ],\n\n hooks: {\n /**\n * onRequest: Intercept incoming requests to resolve auth sessions.\n *\n * - Auth proxy routes are passed through untouched.\n * - For all other routes, we resolve the session. If no session exists\n * and `onSessionError === 'throw'`, we short-circuit with a 401.\n */\n onRequest: async (\n request: Request,\n context: { path: string; method: string; identity: InvectIdentity | null },\n ) => {\n // Skip session resolution for auth proxy routes\n if (isBetterAuthRoute(context.path, prefix, betterAuthBasePath)) {\n return; // Let the proxy endpoint handle it\n }\n\n // Skip for public paths\n if (publicPaths.some((p) => context.path.startsWith(p))) {\n return;\n }\n\n // Resolve session from request headers\n const headersRecord: Record<string, string | undefined> = {};\n request.headers.forEach((value, key) => {\n headersRecord[key] = value;\n });\n\n endpointLogger.debug?.(`[auth-onRequest] ${context.method} ${context.path}`, {\n hasCookie: !!headersRecord['cookie'],\n hasAuth: !!headersRecord['authorization'],\n });\n\n const identity = await getIdentityFromHeaders(headersRecord);\n\n endpointLogger.debug?.(`[auth-onRequest] Identity resolved:`, {\n authenticated: !!identity,\n userId: identity?.id,\n role: identity?.role,\n });\n\n // Write identity back so the framework adapter has it available.\n context.identity = identity;\n\n if (!identity && onSessionError === 'throw') {\n return {\n response: new Response(\n JSON.stringify({\n error: 'Unauthorized',\n message: 'Valid session required. Sign in via better-auth.',\n }),\n {\n status: 401,\n headers: { 'content-type': 'application/json' },\n },\n ),\n };\n }\n\n // Return void — identity is now on context.identity for the caller.\n return;\n },\n\n /**\n * onAuthorize: Baseline authorization guard using better-auth sessions.\n *\n * If the identity is already populated (from onRequest),\n * we defer to downstream authorization hooks. This hook is a fallback\n * for cases where the identity wasn't resolved upstream.\n */\n onAuthorize: async (context: {\n identity: InvectIdentity | null;\n action: InvectPermission;\n resource?: { type: string; id?: string };\n }) => {\n // If an identity is already attached, let downstream authorization proceed\n if (context.identity) {\n return;\n }\n\n // No identity — deny access\n return { allowed: false, reason: 'No valid better-auth session' };\n },\n },\n\n init: async (pluginContext) => {\n endpointLogger = pluginContext.logger;\n\n // ── Create internal better-auth instance if none was provided ────────\n if (!auth) {\n auth = await createInternalBetterAuth(pluginContext.config, options, pluginContext.logger);\n }\n\n betterAuthBasePath = auth.options?.basePath ?? '/api/auth';\n\n pluginContext.logger.info(\n `Better Auth plugin initialized (prefix: ${prefix}, basePath: ${betterAuthBasePath})`,\n );\n\n if (globalAdmins.length === 0) {\n pluginContext.logger.debug(\n 'No global admins configured. Pass `globalAdmins` to authentication(...) to seed admin access.',\n );\n return;\n }\n\n for (const configuredAdmin of globalAdmins) {\n const adminEmail = configuredAdmin.email?.trim();\n const adminPassword = configuredAdmin.pw;\n const adminName = configuredAdmin.name?.trim() || 'Admin';\n\n if (!adminEmail || !adminPassword) {\n pluginContext.logger.debug(\n 'Skipping invalid global admin config: both email and pw are required.',\n );\n continue;\n }\n\n try {\n const authContext = await getAuthContext(auth);\n const existingAdminUser = unwrapFoundUser(\n await authContext?.internalAdapter?.findUserByEmail(adminEmail),\n );\n\n if (existingAdminUser) {\n if (existingAdminUser.role !== 'admin') {\n await authContext?.internalAdapter?.updateUser(existingAdminUser.id, {\n role: 'admin',\n });\n pluginContext.logger.info(`Admin user promoted: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(`Admin user already configured: ${adminEmail}`);\n }\n\n continue;\n }\n\n const api = requireAuth().api as Record<string, unknown>;\n let result: BetterAuthApiUserResult | null = null;\n\n // Try createUser first (admin plugin API), then fall back to signUpEmail.\n // createUser requires admin auth headers which aren't available during\n // initial seeding, so we fall through to signUpEmail when it fails.\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers: new Headers(),\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n role: 'admin',\n },\n }).catch((_err: unknown) => {\n pluginContext.logger.debug?.(\n `createUser API requires auth, falling back to signUpEmail for ${adminEmail}`,\n );\n return null;\n });\n }\n\n if (!result?.user && typeof api.signUpEmail === 'function') {\n const signUpEmail = api.signUpEmail as BetterAuthBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n },\n }).catch((err: unknown) => {\n pluginContext.logger.error?.(\n `signUpEmail failed for ${adminEmail}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n });\n }\n\n if (\n !result?.user &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n pluginContext.logger.debug(\n `Could not create global admin ${adminEmail}: auth.api.createUser/signUpEmail are unavailable.`,\n );\n continue;\n }\n\n if (result?.user) {\n // Promote the newly created user to admin via internal adapter\n const createdAuthContext = authContext ?? (await getAuthContext(auth));\n const createdAdminUser =\n unwrapFoundUser(\n await createdAuthContext?.internalAdapter?.findUserByEmail(adminEmail),\n ) ?? result.user;\n\n if (createdAdminUser?.id && createdAdminUser.role !== 'admin') {\n await createdAuthContext?.internalAdapter?.updateUser(createdAdminUser.id, {\n role: 'admin',\n });\n }\n\n pluginContext.logger.info(`Admin user created: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(\n `Admin user already exists or could not be created: ${adminEmail}`,\n );\n }\n } catch (seedErr) {\n pluginContext.logger.debug(\n `Could not seed admin user (tables may not exist yet): ${adminEmail} — ${seedErr instanceof Error ? seedErr.message : String(seedErr)}`,\n );\n }\n }\n },\n\n $ERROR_CODES: {\n 'auth:session_expired': {\n message: 'Session has expired. Please sign in again.',\n status: 401,\n },\n 'auth:session_not_found': {\n message: 'No valid session found.',\n status: 401,\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a path is a better-auth proxy route (should skip session checks).\n * Only matches the actual better-auth API proxy routes, not custom plugin endpoints.\n */\nfunction isBetterAuthRoute(path: string, prefix: string, basePath: string): boolean {\n return path.startsWith(`/plugins/${prefix}${basePath}`);\n}\n","/**\n * @invect/user-auth — Backend Entry Point\n *\n * Wraps a [better-auth](https://better-auth.com) instance as an Invect plugin,\n * providing:\n * - Session-based identity resolution\n * - Proxied auth routes (sign-in, sign-up, OAuth, etc.)\n * - Authorization hook integration\n * - Express/NestJS middleware helpers\n *\n * @example\n * ```ts\n * // Simple — no separate auth setup needed:\n * import { auth } from '@invect/user-auth';\n *\n * defineConfig({\n * plugins: [auth()],\n * });\n * ```\n *\n * @example\n * ```ts\n * // With frontend UI:\n * import { auth } from '@invect/user-auth';\n * import { authFrontend } from '@invect/user-auth/ui';\n *\n * defineConfig({\n * plugins: [auth({ frontend: authFrontend })],\n * });\n * ```\n *\n * @packageDocumentation\n */\nexport { authentication, USER_AUTH_SCHEMA } from './plugin';\nexport type {\n AuthenticationPluginOptions,\n ApiKeyPluginOptions,\n BetterAuthPassthroughOptions,\n BetterAuthInstance,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthSessionResult,\n} from './types';\n\nimport type { InvectPluginDefinition } from '@invect/core';\nimport type { AuthenticationPluginOptions } from './types';\nimport { authentication } from './plugin';\n\n/**\n * Create the auth plugin definition for Invect config.\n *\n * @example\n * ```ts\n * // Express (backend only):\n * auth({ adminEmail: '...' })\n *\n * // Next.js (with frontend):\n * import { authFrontend } from '@invect/user-auth/ui';\n * auth({ adminEmail: '...', frontend: authFrontend })\n * ```\n */\nexport function auth(options: AuthenticationPluginOptions): InvectPluginDefinition {\n return {\n id: 'user-auth',\n name: 'User Authentication',\n backend: authentication(options),\n frontend: options.frontend,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/backend/plugin.ts","../../src/backend/index.ts"],"sourcesContent":["import type {\n InvectPlugin,\n InvectIdentity,\n InvectRole,\n InvectPermission,\n InvectPluginSchema,\n} from '@invect/core';\nimport type {\n AuthenticationPluginOptions,\n BetterAuthContext,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthInstance,\n} from './types';\nimport {\n AUTH_ADMIN_ROLE,\n AUTH_ASSIGNABLE_ROLES,\n AUTH_DEFAULT_ROLE,\n AUTH_VISIBLE_ROLES,\n isAuthAssignableRole,\n isAuthVisibleRole,\n} from '../shared/roles';\n\ntype PluginLoggerLike = {\n error: (message: string, meta?: unknown) => void;\n info?: (message: string, meta?: unknown) => void;\n debug?: (message: string, meta?: unknown) => void;\n warn?: (message: string, meta?: unknown) => void;\n};\n\ntype BetterAuthApiUser = {\n id?: string;\n email?: string | null;\n name?: string | null;\n role?: string | null;\n [key: string]: unknown;\n};\n\ntype BetterAuthApiUserResult = {\n user?: BetterAuthApiUser | null;\n};\n\ntype BetterAuthHeadersQueryMethod<TResult> = (args: {\n headers: Headers;\n query: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n}) => Promise<TResult>;\n\ntype BetterAuthHeadersBodyParamsMethod<TResult> = (args: {\n headers: Headers;\n body: Record<string, unknown>;\n params: Record<string, string>;\n}) => Promise<TResult>;\n\ntype BetterAuthBodyMethod<TResult> = (args: { body: Record<string, unknown> }) => Promise<TResult>;\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PREFIX = 'auth';\n\n/**\n * Default role mapping: keep admin/RBAC roles aligned and fall back to default.\n */\nfunction defaultMapRole(role: string | null | undefined): InvectRole {\n if (!role) {\n return AUTH_DEFAULT_ROLE;\n }\n if (role === 'viewer' || role === 'readonly') {\n return 'viewer';\n }\n if (isAuthVisibleRole(role)) {\n return role;\n }\n return AUTH_DEFAULT_ROLE;\n}\n\n/**\n * Default user → identity mapping.\n */\nfunction defaultMapUser(\n user: BetterAuthUser,\n _session: BetterAuthSession,\n mapRole: (role: string | null | undefined) => InvectRole,\n): InvectIdentity {\n const resolvedRole = mapRole(user.role);\n\n return {\n id: user.id,\n name: user.name ?? user.email ?? undefined,\n role: resolvedRole,\n permissions: resolvedRole === AUTH_ADMIN_ROLE ? ['admin:*'] : undefined,\n resourceAccess:\n resolvedRole === AUTH_ADMIN_ROLE\n ? {\n flows: '*',\n credentials: '*',\n }\n : undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Simple in-memory sliding-window rate limiter.\n *\n * Keyed by IP address (or a fallback identifier). Tracks request timestamps\n * per window and rejects requests that exceed the limit with HTTP 429.\n *\n * Only applied to authentication-sensitive endpoints (sign-in, sign-up,\n * password reset) to prevent brute-force attacks. Session reads (GET) are\n * not rate-limited.\n */\nclass RateLimiter {\n private windows = new Map<string, number[]>();\n private readonly maxRequests: number;\n private readonly windowMs: number;\n\n constructor(maxRequests = 10, windowMs = 60_000) {\n this.maxRequests = maxRequests;\n this.windowMs = windowMs;\n }\n\n /**\n * Returns `true` if the request should be rejected (over limit).\n */\n isRateLimited(key: string): { limited: boolean; retryAfterMs?: number } {\n const now = Date.now();\n const windowStart = now - this.windowMs;\n\n let timestamps = this.windows.get(key);\n if (!timestamps) {\n timestamps = [];\n this.windows.set(key, timestamps);\n }\n\n // Prune expired entries\n const valid = timestamps.filter((t) => t > windowStart);\n this.windows.set(key, valid);\n\n if (valid.length >= this.maxRequests) {\n const oldestInWindow = valid[0] ?? now;\n const retryAfterMs = oldestInWindow + this.windowMs - now;\n return { limited: true, retryAfterMs: Math.max(retryAfterMs, 1000) };\n }\n\n valid.push(now);\n return { limited: false };\n }\n\n /** Periodic cleanup of stale keys to prevent memory leaks. */\n cleanup(): void {\n const now = Date.now();\n for (const [key, timestamps] of this.windows) {\n const valid = timestamps.filter((t) => t > now - this.windowMs);\n if (valid.length === 0) {\n this.windows.delete(key);\n } else {\n this.windows.set(key, valid);\n }\n }\n }\n}\n\n/** Auth-sensitive path segments that should be rate-limited. */\nconst RATE_LIMITED_AUTH_PATHS = ['/sign-in/', '/sign-up/', '/forgot-password', '/reset-password'];\n\n/**\n * Convert a Node.js-style `IncomingHttpHeaders` record or a `Headers` instance\n * to a standard `Headers` object for passing to better-auth.\n */\nfunction toHeaders(raw: Record<string, string | undefined> | Headers): Headers {\n if (raw instanceof Headers) {\n return raw;\n }\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(raw)) {\n if (value !== undefined) {\n headers.set(key, value);\n }\n }\n return headers;\n}\n\n/**\n * Resolve the session from a better-auth instance using request headers.\n */\nasync function resolveSession(\n auth: BetterAuthInstance | null,\n headers: Record<string, string | undefined> | Headers,\n): Promise<{ session: BetterAuthSession; user: BetterAuthUser } | null> {\n if (!auth) {\n return null;\n }\n\n const h = toHeaders(headers);\n\n try {\n const result = await auth.api.getSession({\n headers: h,\n });\n if (result?.session && result?.user) {\n return {\n session: result.session,\n user: result.user,\n };\n }\n return null;\n } catch (err) {\n return null;\n }\n}\n\nasync function callBetterAuthHandler(\n auth: BetterAuthInstance | null,\n request: Request,\n path: string,\n init?: {\n method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n query?: Record<string, string | undefined>;\n body?: Record<string, unknown>;\n },\n): Promise<{ status: number; body: unknown } | null> {\n if (!auth) {\n return null;\n }\n\n const basePath = auth.options?.basePath ?? '/api/auth';\n const targetUrl = new URL(`${basePath}${path}`, request.url);\n for (const [key, value] of Object.entries(init?.query ?? {})) {\n if (value !== undefined) {\n targetUrl.searchParams.set(key, value);\n }\n }\n\n const headers = new Headers(request.headers);\n const hasBody = init?.body !== undefined;\n if (hasBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n const authRequest = new Request(targetUrl.toString(), {\n method: init?.method ?? 'GET',\n headers,\n body: hasBody ? JSON.stringify(init?.body) : undefined,\n });\n\n const response = await auth.handler(authRequest);\n const text = await response.text();\n\n if (!text) {\n return { status: response.status, body: null };\n }\n\n try {\n return { status: response.status, body: JSON.parse(text) };\n } catch {\n return { status: response.status, body: text };\n }\n}\n\nasync function getAuthContext(auth: BetterAuthInstance | null): Promise<BetterAuthContext | null> {\n if (!auth) {\n return null;\n }\n try {\n return (await auth.$context) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction isBetterAuthUser(value: unknown): value is BetterAuthUser {\n return !!value && typeof value === 'object' && 'id' in value && typeof value.id === 'string';\n}\n\nfunction unwrapFoundUser(\n result: BetterAuthUser | { user?: BetterAuthUser | null } | null | undefined,\n): BetterAuthUser | null {\n if (!result) {\n return null;\n }\n\n if (typeof result === 'object' && 'user' in result) {\n const nestedUser = result.user;\n if (isBetterAuthUser(nestedUser)) {\n return nestedUser;\n }\n\n return null;\n }\n\n if (isBetterAuthUser(result)) {\n return result;\n }\n\n return null;\n}\n\nfunction toAuthApiErrorResponse(\n fallbackError: string,\n error: unknown,\n): { status: number; body: Record<string, unknown> } {\n if (error instanceof Response) {\n return {\n status: error.status || 500,\n body: { error: fallbackError, message: error.statusText || fallbackError },\n };\n }\n\n const status =\n error &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status?: unknown }).status === 'number'\n ? ((error as { status: number }).status ?? 500)\n : error &&\n typeof error === 'object' &&\n 'statusCode' in error &&\n typeof (error as { statusCode?: unknown }).statusCode === 'number'\n ? ((error as { statusCode: number }).statusCode ?? 500)\n : 500;\n\n const message =\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ? (error as { message: string }).message || fallbackError\n : fallbackError;\n\n const code =\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n typeof (error as { code?: unknown }).code === 'string'\n ? (error as { code: string }).code\n : undefined;\n\n return {\n status,\n body: {\n error: fallbackError,\n message,\n ...(code ? { code } : {}),\n },\n };\n}\n\nfunction sanitizeForLogging(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeForLogging(item));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, nestedValue]) => {\n if (/password|token|secret/i.test(key)) {\n return [key, '[REDACTED]'];\n }\n\n return [key, sanitizeForLogging(nestedValue)];\n }),\n );\n }\n\n return value;\n}\n\nfunction getErrorLogDetails(error: unknown): Record<string, unknown> {\n if (error instanceof Response) {\n return {\n type: 'Response',\n status: error.status,\n statusText: error.statusText,\n };\n }\n\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n ...(error && typeof error === 'object' && 'cause' in error\n ? { cause: sanitizeForLogging((error as Error & { cause?: unknown }).cause) }\n : {}),\n ...(error && typeof error === 'object' && 'code' in error\n ? { code: (error as Error & { code?: unknown }).code }\n : {}),\n ...(error && typeof error === 'object' && 'status' in error\n ? { status: (error as Error & { status?: unknown }).status }\n : {}),\n ...(error && typeof error === 'object' && 'statusCode' in error\n ? { statusCode: (error as Error & { statusCode?: unknown }).statusCode }\n : {}),\n };\n }\n\n if (error && typeof error === 'object') {\n return sanitizeForLogging(error) as Record<string, unknown>;\n }\n\n return { value: error };\n}\n\n// ---------------------------------------------------------------------------\n// Abstract schema — defines the tables the user-auth plugin requires\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract schema for the user-auth plugin's database tables.\n *\n * These definitions allow the Invect CLI (`npx invect-cli generate`) to include\n * the auth tables when generating Drizzle/Prisma schema files.\n *\n * The shapes match Better Auth's default table structure. If your Better Auth\n * config adds extra fields (e.g., via plugins like `twoFactor`, `organization`),\n * you can extend these in your own config.\n */\nexport const USER_AUTH_SCHEMA: InvectPluginSchema = {\n user: {\n tableName: 'user',\n order: 1,\n fields: {\n id: { type: 'string', primaryKey: true },\n name: { type: 'string', required: true },\n email: { type: 'string', required: true, unique: true },\n emailVerified: { type: 'boolean', required: true, defaultValue: false },\n image: { type: 'string', required: false },\n role: { type: 'string', required: false, defaultValue: AUTH_DEFAULT_ROLE },\n banned: { type: 'boolean', required: false, defaultValue: false },\n banReason: { type: 'string', required: false },\n banExpires: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n session: {\n tableName: 'session',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n expiresAt: { type: 'date', required: true },\n token: { type: 'string', required: true, unique: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n ipAddress: { type: 'string', required: false },\n userAgent: { type: 'string', required: false },\n impersonatedBy: { type: 'string', required: false },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n },\n },\n\n account: {\n tableName: 'account',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n accountId: { type: 'string', required: true },\n providerId: { type: 'string', required: true },\n userId: {\n type: 'string',\n required: true,\n references: { table: 'user', field: 'id', onDelete: 'cascade' },\n },\n accessToken: { type: 'string', required: false },\n refreshToken: { type: 'string', required: false },\n idToken: { type: 'string', required: false },\n accessTokenExpiresAt: { type: 'date', required: false },\n refreshTokenExpiresAt: { type: 'date', required: false },\n scope: { type: 'string', required: false },\n password: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n verification: {\n tableName: 'verification',\n order: 2,\n fields: {\n id: { type: 'string', primaryKey: true },\n identifier: { type: 'string', required: true },\n value: { type: 'string', required: true },\n expiresAt: { type: 'date', required: true },\n createdAt: { type: 'date', required: false },\n updatedAt: { type: 'date', required: false },\n },\n },\n\n // ----- Flow access control table (moved from core) -----\n flowAccess: {\n tableName: 'flow_access',\n order: 3,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n },\n userId: { type: 'string', required: false },\n teamId: { type: 'string', required: false },\n permission: {\n type: 'string',\n required: true,\n defaultValue: 'viewer',\n },\n grantedBy: { type: 'string', required: false },\n grantedAt: { type: 'date', required: true, defaultValue: 'now()' },\n expiresAt: { type: 'date', required: false },\n },\n },\n};\n\n/**\n * Abstract schema for the Better Auth API Key plugin's `apikey` table.\n *\n * Only merged into the plugin schema when `apiKey` is enabled.\n *\n * @see https://better-auth.com/docs/plugins/api-key/reference#schema\n */\nconst API_KEY_SCHEMA: InvectPluginSchema = {\n apikey: {\n tableName: 'apikey',\n order: 3,\n fields: {\n id: { type: 'string', primaryKey: true },\n configId: { type: 'string', required: true, defaultValue: 'default' },\n name: { type: 'string', required: false },\n start: { type: 'string', required: false },\n prefix: { type: 'string', required: false },\n key: { type: 'string', required: true },\n referenceId: { type: 'string', required: true },\n refillInterval: { type: 'number', required: false },\n refillAmount: { type: 'number', required: false },\n lastRefillAt: { type: 'date', required: false },\n enabled: { type: 'boolean', required: false, defaultValue: true },\n rateLimitEnabled: { type: 'boolean', required: false },\n rateLimitTimeWindow: { type: 'number', required: false },\n rateLimitMax: { type: 'number', required: false },\n requestCount: { type: 'number', required: false },\n remaining: { type: 'number', required: false },\n lastRequest: { type: 'date', required: false },\n expiresAt: { type: 'date', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n permissions: { type: 'string', required: false },\n metadata: { type: 'string', required: false },\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Internal Better Auth instance creation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a Better Auth instance internally using Invect's database config.\n *\n * Dynamically imports `better-auth` (a required peer dependency) and creates\n * a fully-configured instance with email/password auth, the admin plugin,\n * and session caching.\n *\n * Database resolution order:\n * 1. Explicit `options.database` (any value `betterAuth({ database })` accepts)\n * 2. Auto-created client from Invect's `database.connectionString`\n */\nasync function createInternalBetterAuth(\n invectConfig: Record<string, unknown>,\n options: AuthenticationPluginOptions,\n logger: PluginLoggerLike,\n): Promise<BetterAuthInstance> {\n // 1. Dynamic-import better-auth (peer dependency)\n let betterAuthFn: (config: Record<string, unknown>) => unknown;\n let adminPlugin: (config?: Record<string, unknown>) => unknown;\n\n try {\n const betterAuthModule = await import('better-auth');\n betterAuthFn = betterAuthModule.betterAuth;\n } catch {\n throw new Error(\n 'Could not import \"better-auth\". It is a required peer dependency of @invect/user-auth. ' +\n 'Install it with: npm install better-auth',\n );\n }\n\n try {\n const pluginsModule = await import('better-auth/plugins');\n adminPlugin = pluginsModule.admin;\n } catch {\n throw new Error(\n 'Could not import \"better-auth/plugins\". Ensure better-auth is properly installed.',\n );\n }\n\n // 2. Resolve database\n let database: unknown = options.database;\n\n if (!database) {\n const dbConfig = invectConfig.database as\n | { type?: string; connectionString?: string }\n | undefined;\n\n if (!dbConfig?.connectionString) {\n throw new Error(\n 'Cannot create internal Better Auth instance: no database configuration found. ' +\n 'Either provide `auth` (a Better Auth instance), `database`, or ensure ' +\n 'Invect database has a connectionString.',\n );\n }\n\n const connStr = dbConfig.connectionString;\n const dbType = (dbConfig.type ?? 'sqlite').toLowerCase();\n\n if (dbType === 'sqlite') {\n database = await createSQLiteClient(connStr, logger);\n } else if (dbType === 'pg' || dbType === 'postgresql') {\n database = await createPostgresPool(connStr);\n } else if (dbType === 'mysql') {\n database = await createMySQLPool(connStr);\n } else {\n throw new Error(\n `Unsupported database type for internal Better Auth: \"${dbType}\". ` +\n 'Supported: sqlite, pg, mysql. Alternatively, provide your own Better Auth instance via `auth`.',\n );\n }\n }\n\n // 3. Resolve base URL\n const baseURL =\n options.baseURL ??\n process.env.BETTER_AUTH_URL ??\n `http://localhost:${process.env.PORT ?? '3000'}`;\n\n // 4. Resolve trusted origins\n const configuredOrigins = options.trustedOrigins;\n const trustedOrigins =\n configuredOrigins ??\n ((request: Request) => {\n const trusted = new Set<string>([baseURL, 'http://localhost:5173', 'http://localhost:5174']);\n try {\n if (request) {\n trusted.add(new URL(request.url).origin);\n }\n } catch {\n // Ignore malformed URLs\n }\n return Array.from(trusted);\n });\n\n // 5. Build passthrough config from betterAuthOptions\n const passthrough = options.betterAuthOptions ?? {};\n\n const emailAndPassword = {\n enabled: true,\n ...passthrough.emailAndPassword,\n };\n\n const session = {\n cookieCache: { enabled: true, maxAge: 5 * 60 },\n ...passthrough.session,\n // Merge cookieCache if both exist\n ...(passthrough.session?.cookieCache\n ? {\n cookieCache: {\n enabled: true,\n maxAge: 5 * 60,\n ...passthrough.session.cookieCache,\n },\n }\n : {}),\n };\n\n // 6. Resolve secret — fall back to INVECT_ENCRYPTION_KEY when no explicit\n // secret or secrets are provided. This lets deployments that already set\n // INVECT_ENCRYPTION_KEY get a valid Better Auth secret for free.\n let resolvedSecret = passthrough.secret;\n const resolvedSecrets = passthrough.secrets;\n\n if (!resolvedSecret && !resolvedSecrets) {\n const envKey = process.env.INVECT_ENCRYPTION_KEY;\n if (envKey) {\n resolvedSecret = envKey;\n logger.debug?.(\n 'Using INVECT_ENCRYPTION_KEY as Better Auth secret (no explicit secret/secrets provided)',\n );\n }\n }\n\n // 7. Optionally load the Better Auth API Key plugin\n // Merge top-level `options.apiKey` with `passthrough.apiKey` (top-level wins).\n const apiKeyOpt = options.apiKey ?? passthrough.apiKey;\n const betterAuthPlugins: unknown[] = [\n adminPlugin({ defaultRole: AUTH_DEFAULT_ROLE, adminRoles: [AUTH_ADMIN_ROLE] }),\n ];\n\n if (apiKeyOpt) {\n let apiKeyPluginFn: (config?: Record<string, unknown>) => unknown;\n try {\n // Dynamic import — @better-auth/api-key is an optional peer dependency.\n // Using a variable to prevent TypeScript from resolving the module at\n // type-check time (it may not be installed).\n const pkg = '@better-auth/api-key';\n const apiKeyModule: Record<string, unknown> = await import(pkg);\n apiKeyPluginFn = apiKeyModule.apiKey as (config?: Record<string, unknown>) => unknown;\n } catch {\n throw new Error(\n 'Could not import \"@better-auth/api-key\". Install it with: npm install @better-auth/api-key',\n );\n }\n\n const apiKeyConfig: Record<string, unknown> =\n typeof apiKeyOpt === 'object' ? { ...apiKeyOpt } : {};\n // Default API key header to \"x-invect-token\" (overridable via options).\n if (apiKeyConfig.apiKeyHeaders === undefined) {\n apiKeyConfig.apiKeyHeaders = 'x-invect-token';\n }\n // Ensure getSession() can resolve API keys to sessions.\n // Without this, auth.api.getSession() only checks cookies/tokens.\n if (apiKeyConfig.enableSessionForAPIKeys === undefined) {\n apiKeyConfig.enableSessionForAPIKeys = true;\n }\n betterAuthPlugins.push(apiKeyPluginFn(apiKeyConfig));\n logger.info?.('Better Auth API Key plugin enabled');\n }\n\n // 8. Create the instance\n logger.info?.('Creating internal Better Auth instance');\n\n const instance = betterAuthFn({\n baseURL,\n database,\n emailAndPassword,\n plugins: betterAuthPlugins,\n session,\n trustedOrigins,\n // Spread optional passthrough fields\n ...(passthrough.socialProviders ? { socialProviders: passthrough.socialProviders } : {}),\n ...(passthrough.account ? { account: passthrough.account } : {}),\n ...(passthrough.rateLimit ? { rateLimit: passthrough.rateLimit } : {}),\n // Default cookie prefix to \"invect\" (cookies become invect.session_token).\n // Users can override via betterAuthOptions.advanced.cookiePrefix.\n advanced: {\n cookiePrefix: 'invect',\n ...passthrough.advanced,\n },\n ...(passthrough.databaseHooks ? { databaseHooks: passthrough.databaseHooks } : {}),\n ...(passthrough.hooks ? { hooks: passthrough.hooks } : {}),\n ...(passthrough.disabledPaths ? { disabledPaths: passthrough.disabledPaths } : {}),\n ...(resolvedSecret ? { secret: resolvedSecret } : {}),\n ...(resolvedSecrets ? { secrets: resolvedSecrets } : {}),\n });\n\n return instance as BetterAuthInstance;\n}\n\n/** Create a SQLite client using better-sqlite3. */\nasync function createSQLiteClient(connectionString: string, logger: PluginLoggerLike) {\n try {\n const { default: Database } = await import('better-sqlite3');\n const { Kysely, SqliteDialect, CamelCasePlugin } = await import('kysely');\n logger.debug?.(`Using better-sqlite3 for internal Better Auth database`);\n // Strip file: prefix if present\n let dbPath = connectionString.replace(/^file:/, '');\n if (dbPath === '') {\n dbPath = ':memory:';\n }\n const sqliteDb = new Database(dbPath);\n // Return in Better Auth's { db, type } format so it uses our Kysely instance\n // directly, preserving CamelCasePlugin for snake_case column mapping.\n const kyselyDb = new Kysely({\n dialect: new SqliteDialect({ database: sqliteDb }),\n plugins: [new CamelCasePlugin()],\n });\n return { db: kyselyDb, type: 'sqlite' as const };\n } catch (err) {\n if (err instanceof Error && err.message.includes('better-sqlite3')) {\n throw new Error(\n 'Cannot create SQLite database for internal Better Auth: ' +\n 'install better-sqlite3 (npm install better-sqlite3). ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n throw err;\n }\n}\n\n/** Create a PostgreSQL pool from a connection string. */\nasync function createPostgresPool(connectionString: string) {\n try {\n const { Pool } = await import('pg');\n return new Pool({ connectionString });\n } catch {\n throw new Error(\n 'Cannot create PostgreSQL pool for internal Better Auth: ' +\n 'install the \"pg\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n/** Create a MySQL pool from a connection string. */\nasync function createMySQLPool(connectionString: string) {\n try {\n const mysql = await import('mysql2/promise');\n return mysql.createPool(connectionString);\n } catch {\n throw new Error(\n 'Cannot create MySQL pool for internal Better Auth: ' +\n 'install the \"mysql2\" package. ' +\n 'Alternatively, provide your own Better Auth instance via the `auth` option.',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the Invect user-auth plugin (a light wrapper around Better Auth).\n *\n * This plugin:\n *\n * 1. **Proxies Better Auth routes** — All of Better Auth's HTTP endpoints\n * (sign-in, sign-up, sign-out, OAuth callbacks, session, etc.) are mounted\n * under the plugin endpoint space at `/plugins/auth/api/auth/*` (configurable).\n *\n * 2. **Resolves sessions → identities** — On every Invect API request, the\n * `onRequest` hook reads the session cookie / bearer token via\n * `auth.api.getSession()` and populates `InvectIdentity`.\n *\n * 3. **Handles authorization** — The `onAuthorize` hook lets Better Auth's\n * session decide whether a request is allowed.\n *\n * @example\n * ```ts\n * // Simple: let the plugin manage Better Auth internally\n * import { authentication } from '@invect/user-auth';\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({\n * globalAdmins: [{ email: 'admin@co.com', pw: 'secret' }],\n * })],\n * }));\n * ```\n *\n * @example\n * ```ts\n * // Advanced: provide your own better-auth instance\n * import { betterAuth } from 'better-auth';\n * import { authentication } from '@invect/user-auth';\n *\n * const auth = betterAuth({\n * database: { ... },\n * emailAndPassword: { enabled: true },\n * // ... your better-auth config\n * });\n *\n * app.use('/invect', createInvectRouter({\n * databaseUrl: 'file:./dev.db',\n * plugins: [authentication({ auth })],\n * }));\n * ```\n */\nexport function authentication(options: AuthenticationPluginOptions): InvectPlugin {\n const {\n prefix = DEFAULT_PREFIX,\n mapUser: customMapUser,\n mapRole = defaultMapRole,\n publicPaths = [],\n globalAdmins = [],\n } = options;\n\n // Mutable — assigned in `init` when no external instance was provided.\n let auth: BetterAuthInstance | null = options.auth ?? null;\n\n /** Narrow `auth` for call-sites that run only after init. */\n function requireAuth(): BetterAuthInstance {\n if (!auth) {\n throw new Error('Auth plugin not initialized');\n }\n return auth;\n }\n\n let endpointLogger: PluginLoggerLike = console;\n\n // Determine Better Auth's basePath (defaults to /api/auth).\n // Computed lazily since `auth` may not exist until `init`.\n let betterAuthBasePath = '/api/auth';\n\n /**\n * Resolve an identity from a request's headers.\n */\n async function getIdentityFromHeaders(\n headers: Record<string, string | undefined>,\n ): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await resolveSession(auth, headers);\n if (!result) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(result.user, result.session);\n }\n\n return defaultMapUser(result.user, result.session, mapRole);\n }\n\n async function getIdentityFromRequest(request: Request): Promise<InvectIdentity | null> {\n if (!auth) {\n return null;\n }\n\n const result = await callBetterAuthHandler(auth, request, '/get-session');\n const body = result?.body as { session?: BetterAuthSession; user?: BetterAuthUser } | null;\n if (!body?.session || !body?.user) {\n return null;\n }\n\n if (customMapUser) {\n return customMapUser(body.user, body.session);\n }\n\n return defaultMapUser(body.user, body.session, mapRole);\n }\n\n async function resolveEndpointIdentity(ctx: {\n identity: InvectIdentity | null;\n request: Request;\n }): Promise<InvectIdentity | null> {\n if (ctx.identity) {\n return ctx.identity;\n }\n\n return getIdentityFromRequest(ctx.request);\n }\n\n // Rate limiter for auth-sensitive endpoints (sign-in, sign-up, password reset)\n // 10 attempts per IP per 60 seconds by default\n const authRateLimiter = new RateLimiter(10, 60_000);\n\n // Periodic cleanup every 5 minutes to prevent memory leaks\n const cleanupInterval = setInterval(() => authRateLimiter.cleanup(), 5 * 60_000);\n cleanupInterval.unref?.(); // Don't keep the process alive\n\n // ----- Build plugin endpoint list -----\n // We create a catch-all endpoint that proxies everything under the prefix\n // to better-auth's handler. This covers all auth routes: sign-in, sign-up,\n // OAuth callbacks, session management, etc.\n\n const proxyMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] as const;\n\n const endpoints = proxyMethods.map((method) => ({\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: `/${prefix}/*` as const,\n isPublic: true, // Auth routes must be accessible without existing session\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n request: Request;\n }) => {\n // Build a new Request for better-auth's handler.\n // The incoming path is: /plugins/auth/api/auth/sign-in/email (for example)\n // We need to reconstruct the URL that better-auth expects.\n const incomingUrl = new URL(ctx.request.url);\n endpointLogger.debug?.(`[auth-proxy] ${method} ${incomingUrl.pathname}`);\n\n // Strip the /plugins/<prefix> prefix from the pathname to get\n // the better-auth-relative path\n const pluginPrefixPattern = `/plugins/${prefix}`;\n let authPath = incomingUrl.pathname;\n const prefixIdx = authPath.indexOf(pluginPrefixPattern);\n if (prefixIdx !== -1) {\n authPath = authPath.slice(prefixIdx + pluginPrefixPattern.length);\n }\n if (!authPath.startsWith('/')) {\n authPath = '/' + authPath;\n }\n\n // ── Rate-limit auth-sensitive POST endpoints ──────────\n if (method === 'POST' && RATE_LIMITED_AUTH_PATHS.some((p) => authPath.includes(p))) {\n const clientIp =\n ctx.headers['x-forwarded-for']?.split(',')[0]?.trim() ||\n ctx.headers['x-real-ip'] ||\n 'unknown';\n const { limited, retryAfterMs } = authRateLimiter.isRateLimited(clientIp);\n if (limited) {\n return new Response(\n JSON.stringify({\n error: 'Too Many Requests',\n message: 'Too many authentication attempts. Please try again later.',\n }),\n {\n status: 429,\n headers: {\n 'content-type': 'application/json',\n 'retry-after': String(Math.ceil((retryAfterMs ?? 60_000) / 1000)),\n },\n },\n );\n }\n }\n\n // Construct the URL that better-auth expects\n const authUrl = new URL(`${incomingUrl.origin}${authPath}${incomingUrl.search}`);\n endpointLogger.debug?.(\n `[auth-proxy] Forwarding to better-auth: ${method} ${authUrl.pathname}`,\n );\n\n // Clone/forward the request to better-auth\n const authRequest = new Request(authUrl.toString(), {\n method: ctx.request.method,\n headers: ctx.request.headers,\n body: method !== 'GET' && method !== 'DELETE' ? ctx.request.body : undefined,\n // @ts-expect-error - duplex is needed for streaming bodies\n duplex: method !== 'GET' && method !== 'DELETE' ? 'half' : undefined,\n });\n\n // Let better-auth handle it\n const response = await requireAuth().handler(authRequest);\n endpointLogger.debug?.(`[auth-proxy] Response: ${response.status} ${response.statusText}`, {\n setCookie: response.headers.get('set-cookie') ? 'present' : 'absent',\n contentType: response.headers.get('content-type'),\n });\n return response;\n },\n }));\n\n // ----- Resolve API key opt from top-level or passthrough -----\n const apiKeyEnabled = !!(options.apiKey ?? options.betterAuthOptions?.apiKey);\n\n // ----- Build schema (conditionally includes apikey table) -----\n const schema: InvectPluginSchema = apiKeyEnabled\n ? { ...USER_AUTH_SCHEMA, ...API_KEY_SCHEMA }\n : USER_AUTH_SCHEMA;\n\n const requiredTables = ['user', 'session', 'account', 'verification', 'flow_access'];\n if (apiKeyEnabled) {\n requiredTables.push('apikey');\n }\n\n // ----- Build the plugin -----\n return {\n id: 'user-auth',\n name: 'User Auth',\n\n // Abstract schema for auth tables — the CLI reads this to generate\n // Drizzle/Prisma schema files that include auth tables automatically.\n schema,\n\n // Also declare requiredTables for the startup existence check.\n requiredTables,\n setupInstructions:\n 'Run `npx invect-cli generate` to add the better-auth tables to your schema, ' +\n 'then `npx drizzle-kit push` (or `npx invect-cli migrate`) to apply.',\n\n endpoints: [\n // ── Auth Info (specific routes must come BEFORE the catch-all proxy) ───\n\n {\n method: 'GET' as const,\n path: `/${prefix}/me`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: {\n getPermissions: (identity: InvectIdentity | null) => string[];\n getResolvedRole: (identity: InvectIdentity) => string | null;\n };\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n const permissions = ctx.core.getPermissions(identity);\n const resolvedRole = identity ? ctx.core.getResolvedRole(identity) : null;\n\n return {\n status: 200,\n body: {\n identity: identity\n ? {\n id: identity.id,\n name: identity.name,\n role: identity.role,\n resolvedRole,\n }\n : null,\n permissions,\n isAuthenticated: !!identity,\n },\n };\n },\n },\n\n {\n method: 'GET' as const,\n path: `/${prefix}/roles`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n core: { getAvailableRoles: () => unknown };\n }) => {\n const roles = ctx.core.getAvailableRoles() as Array<{\n role: string;\n permissions: string[];\n }>;\n const missingRoles = AUTH_VISIBLE_ROLES.filter(\n (role) => !roles.some((entry) => entry.role === role),\n ).map((role) => ({ role, permissions: [] }));\n return { status: 200, body: { roles: [...roles, ...missingRoles] } };\n },\n },\n\n // ── Auth Info ────────────────────────────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/info`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity) {\n return {\n status: 401,\n body: { error: 'Unauthorized' },\n };\n }\n return {\n status: 200,\n body: {\n apiKeysEnabled: apiKeyEnabled,\n },\n };\n },\n },\n\n // ── API Key Management (admin-only) ────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/list', {\n method: 'GET',\n query: ctx.query,\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to list API keys' },\n };\n } catch (err) {\n endpointLogger.error('Failed to list API keys', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list API keys', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/api-keys`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const {\n name,\n expiresIn,\n prefix: keyPrefix,\n } = ctx.body as {\n name?: string;\n expiresIn?: number;\n prefix?: string;\n };\n\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/create', {\n method: 'POST',\n body: {\n name: name || undefined,\n expiresIn: expiresIn || undefined,\n prefix: keyPrefix || undefined,\n },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 201, body: result.body };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to create API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to create API key', {\n identity: sanitizeForLogging(identity),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create API key', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/api-keys/:keyId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n if (!apiKeyEnabled) {\n return {\n status: 400,\n body: { error: 'API keys are not enabled' },\n };\n }\n\n const { keyId } = ctx.params;\n try {\n const result = await callBetterAuthHandler(auth, ctx.request, '/api-key/delete', {\n method: 'POST',\n body: { keyId },\n });\n if (result && result.status >= 200 && result.status < 300) {\n return { status: 200, body: { success: true } };\n }\n return {\n status: result?.status ?? 500,\n body: result?.body ?? { error: 'Failed to delete API key' },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete API key', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete API key', err);\n }\n },\n },\n\n // ── User Management (admin-only) ──────────────────────\n\n {\n method: 'GET' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth's admin listUsers API first\n if (typeof api.listUsers === 'function') {\n const listUsers = api.listUsers as BetterAuthHeadersQueryMethod<\n { users?: unknown[] } | unknown[] | null\n >;\n const result = await listUsers({\n headers,\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n });\n const users = Array.isArray(result) ? result : (result?.users ?? []);\n return { status: 200, body: { users } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/list-users',\n {\n method: 'GET',\n query: {\n limit: ctx.query.limit ?? '100',\n offset: ctx.query.offset ?? '0',\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return {\n status: 200,\n body: fallbackResult.body as Record<string, unknown>,\n };\n }\n\n // Fallback: query the user table directly via an internal request\n // to better-auth's get-session endpoint for the current user\n return {\n status: 200,\n body: {\n users: [],\n message:\n 'User listing requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to list users', {\n identity: sanitizeForLogging(identity),\n query: sanitizeForLogging(ctx.query),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to list users', err);\n }\n },\n },\n\n {\n method: 'POST' as const,\n path: `/${prefix}/users`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { email, password, name, role } = ctx.body as {\n email?: string;\n password?: string;\n name?: string;\n role?: string;\n };\n\n if (!email || !password) {\n return {\n status: 400,\n body: { error: 'email and password are required' },\n };\n }\n\n if (role !== undefined && !isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n let result: BetterAuthApiUserResult | null = null;\n\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else if (typeof api.signUpEmail === 'function') {\n const signUpEmail =\n api.signUpEmail as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n headers,\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n });\n } else {\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/create-user',\n {\n method: 'POST',\n body: {\n email,\n password,\n name: name ?? email.split('@')[0],\n role: role ?? AUTH_DEFAULT_ROLE,\n },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n result = fallbackResult.body as BetterAuthApiUserResult;\n }\n }\n\n if (\n !result &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n return {\n status: 500,\n body: { error: 'Auth API does not support user creation' },\n };\n }\n\n if (!result?.user) {\n return {\n status: 400,\n body: {\n error: 'Failed to create user',\n message: 'User may already exist',\n },\n };\n }\n\n return {\n status: 201,\n body: {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n role: result.user.role,\n },\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to create user', {\n identity: sanitizeForLogging(identity),\n body: sanitizeForLogging({ email, name, role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to create user', err);\n }\n },\n },\n\n {\n method: 'PATCH' as const,\n path: `/${prefix}/users/:userId/role`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n const { role } = ctx.body as { role?: string };\n\n if (!isAuthAssignableRole(role)) {\n return {\n status: 400,\n body: {\n error: 'role must be one of: ' + AUTH_ASSIGNABLE_ROLES.join(', '),\n },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n // Try better-auth admin API for updating user\n if (typeof api.setRole === 'function') {\n const setRole = api.setRole as BetterAuthHeadersBodyMethod<unknown>;\n await setRole({\n headers,\n body: { userId, role },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/set-role',\n {\n method: 'POST',\n body: { userId, role },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId, role } };\n }\n\n // Fallback: try updateUser API\n if (typeof api.updateUser === 'function') {\n const updateUser = api.updateUser as BetterAuthHeadersBodyParamsMethod<unknown>;\n await updateUser({\n headers,\n body: { role },\n params: { id: userId },\n });\n return { status: 200, body: { success: true, userId, role } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'Role update requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to update role', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n body: sanitizeForLogging({ role }),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to update role', err);\n }\n },\n },\n\n {\n method: 'DELETE' as const,\n path: `/${prefix}/users/:userId`,\n isPublic: false,\n handler: async (ctx: {\n body: Record<string, unknown>;\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n headers: Record<string, string | undefined>;\n identity: InvectIdentity | null;\n request: Request;\n }) => {\n const identity = await resolveEndpointIdentity(ctx);\n if (!identity || identity.role !== 'admin') {\n return {\n status: 403,\n body: { error: 'Forbidden', message: 'Admin access required' },\n };\n }\n\n const { userId } = ctx.params;\n\n // Prevent deleting yourself\n if (identity.id === userId) {\n return {\n status: 400,\n body: { error: 'Cannot delete your own account' },\n };\n }\n\n try {\n const api = requireAuth().api as Record<string, unknown>;\n const headers = toHeaders(ctx.headers);\n\n if (typeof api.removeUser === 'function') {\n const removeUser = api.removeUser as BetterAuthHeadersBodyMethod<unknown>;\n await removeUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n const fallbackResult = await callBetterAuthHandler(\n auth,\n ctx.request,\n '/admin/remove-user',\n {\n method: 'POST',\n body: { userId },\n },\n );\n if (fallbackResult && fallbackResult.status >= 200 && fallbackResult.status < 300) {\n return { status: 200, body: { success: true, userId } };\n }\n\n // Try deleteUser\n if (typeof api.deleteUser === 'function') {\n const deleteUser = api.deleteUser as BetterAuthHeadersBodyMethod<unknown>;\n await deleteUser({\n headers,\n body: { userId },\n });\n return { status: 200, body: { success: true, userId } };\n }\n\n return {\n status: 501,\n body: {\n error:\n 'User deletion requires the better-auth admin plugin. ' +\n 'Add `admin()` to your better-auth plugins config.',\n },\n };\n } catch (err) {\n endpointLogger.error('Failed to delete user', {\n identity: sanitizeForLogging(identity),\n params: sanitizeForLogging(ctx.params),\n error: getErrorLogDetails(err),\n });\n return toAuthApiErrorResponse('Failed to delete user', err);\n }\n },\n },\n\n // ── Auth proxy catch-all (must come LAST so specific routes above win) ──\n ...endpoints,\n ],\n\n hooks: {\n /**\n * onRequest: Intercept incoming requests to resolve auth sessions.\n *\n * - Auth proxy routes are passed through untouched.\n * - For all other routes, we resolve the session. If no session exists,\n * we short-circuit with a 401.\n */\n onRequest: async (\n request: Request,\n context: { path: string; method: string; identity: InvectIdentity | null },\n ) => {\n // Skip session resolution for auth proxy routes\n if (isBetterAuthRoute(context.path, prefix, betterAuthBasePath)) {\n return; // Let the proxy endpoint handle it\n }\n\n // Skip for public paths\n if (publicPaths.some((p) => context.path.startsWith(p))) {\n return;\n }\n\n // Resolve session from request headers\n const headersRecord: Record<string, string | undefined> = {};\n request.headers.forEach((value, key) => {\n headersRecord[key] = value;\n });\n\n endpointLogger.debug?.(`[auth-onRequest] ${context.method} ${context.path}`, {\n hasCookie: !!headersRecord['cookie'],\n hasAuth: !!headersRecord['authorization'],\n });\n\n const identity = await getIdentityFromHeaders(headersRecord);\n\n endpointLogger.debug?.(`[auth-onRequest] Identity resolved:`, {\n authenticated: !!identity,\n userId: identity?.id,\n role: identity?.role,\n });\n\n // Write identity back so the framework adapter has it available.\n context.identity = identity;\n\n if (!identity) {\n return {\n response: new Response(\n JSON.stringify({\n error: 'Unauthorized',\n message: 'Valid session required. Sign in via better-auth.',\n }),\n {\n status: 401,\n headers: { 'content-type': 'application/json' },\n },\n ),\n };\n }\n },\n\n /**\n * onAuthorize: Baseline authorization guard using better-auth sessions.\n *\n * If the identity is already populated (from onRequest),\n * we defer to downstream authorization hooks. This hook is a fallback\n * for cases where the identity wasn't resolved upstream.\n */\n onAuthorize: async (context: {\n identity: InvectIdentity | null;\n action: InvectPermission;\n resource?: { type: string; id?: string };\n }) => {\n // If an identity is already attached, let downstream authorization proceed\n if (context.identity) {\n return;\n }\n\n // No identity — deny access\n return { allowed: false, reason: 'No valid better-auth session' };\n },\n },\n\n init: async (pluginContext) => {\n endpointLogger = pluginContext.logger;\n\n // ── Create internal better-auth instance if none was provided ────────\n if (!auth) {\n auth = await createInternalBetterAuth(pluginContext.config, options, pluginContext.logger);\n }\n\n betterAuthBasePath = auth.options?.basePath ?? '/api/auth';\n\n pluginContext.logger.info(\n `Better Auth plugin initialized (prefix: ${prefix}, basePath: ${betterAuthBasePath})`,\n );\n\n if (globalAdmins.length === 0) {\n pluginContext.logger.debug(\n 'No global admins configured. Pass `globalAdmins` to authentication(...) to seed admin access.',\n );\n return;\n }\n\n for (const configuredAdmin of globalAdmins) {\n const adminEmail = configuredAdmin.email?.trim();\n const adminPassword = configuredAdmin.pw;\n const adminName = configuredAdmin.name?.trim() || 'Admin';\n\n if (!adminEmail || !adminPassword) {\n pluginContext.logger.debug(\n 'Skipping invalid global admin config: both email and pw are required.',\n );\n continue;\n }\n\n try {\n const authContext = await getAuthContext(auth);\n const existingAdminUser = unwrapFoundUser(\n await authContext?.internalAdapter?.findUserByEmail(adminEmail),\n );\n\n if (existingAdminUser) {\n if (existingAdminUser.role !== 'admin') {\n await authContext?.internalAdapter?.updateUser(existingAdminUser.id, {\n role: 'admin',\n });\n pluginContext.logger.info(`Admin user promoted: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(`Admin user already configured: ${adminEmail}`);\n }\n\n continue;\n }\n\n const api = requireAuth().api as Record<string, unknown>;\n let result: BetterAuthApiUserResult | null = null;\n\n // Try createUser first (admin plugin API), then fall back to signUpEmail.\n // createUser requires admin auth headers which aren't available during\n // initial seeding, so we fall through to signUpEmail when it fails.\n if (typeof api.createUser === 'function') {\n const createUser =\n api.createUser as BetterAuthHeadersBodyMethod<BetterAuthApiUserResult>;\n result = await createUser({\n headers: new Headers(),\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n role: 'admin',\n },\n }).catch((_err: unknown) => {\n pluginContext.logger.debug?.(\n `createUser API requires auth, falling back to signUpEmail for ${adminEmail}`,\n );\n return null;\n });\n }\n\n if (!result?.user && typeof api.signUpEmail === 'function') {\n const signUpEmail = api.signUpEmail as BetterAuthBodyMethod<BetterAuthApiUserResult>;\n result = await signUpEmail({\n body: {\n email: adminEmail,\n password: adminPassword,\n name: adminName,\n },\n }).catch((err: unknown) => {\n pluginContext.logger.error?.(\n `signUpEmail failed for ${adminEmail}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return null;\n });\n }\n\n if (\n !result?.user &&\n typeof api.createUser !== 'function' &&\n typeof api.signUpEmail !== 'function'\n ) {\n pluginContext.logger.debug(\n `Could not create global admin ${adminEmail}: auth.api.createUser/signUpEmail are unavailable.`,\n );\n continue;\n }\n\n if (result?.user) {\n // Promote the newly created user to admin via internal adapter\n const createdAuthContext = authContext ?? (await getAuthContext(auth));\n const createdAdminUser =\n unwrapFoundUser(\n await createdAuthContext?.internalAdapter?.findUserByEmail(adminEmail),\n ) ?? result.user;\n\n if (createdAdminUser?.id && createdAdminUser.role !== 'admin') {\n await createdAuthContext?.internalAdapter?.updateUser(createdAdminUser.id, {\n role: 'admin',\n });\n }\n\n pluginContext.logger.info(`Admin user created: ${adminEmail}`);\n } else {\n pluginContext.logger.debug(\n `Admin user already exists or could not be created: ${adminEmail}`,\n );\n }\n } catch (seedErr) {\n pluginContext.logger.debug(\n `Could not seed admin user (tables may not exist yet): ${adminEmail} — ${seedErr instanceof Error ? seedErr.message : String(seedErr)}`,\n );\n }\n }\n },\n\n $ERROR_CODES: {\n 'auth:session_expired': {\n message: 'Session has expired. Please sign in again.',\n status: 401,\n },\n 'auth:session_not_found': {\n message: 'No valid session found.',\n status: 401,\n },\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a path is a better-auth proxy route (should skip session checks).\n * Only matches the actual better-auth API proxy routes, not custom plugin endpoints.\n */\nfunction isBetterAuthRoute(path: string, prefix: string, basePath: string): boolean {\n return path.startsWith(`/plugins/${prefix}${basePath}`);\n}\n","/**\n * @invect/user-auth — Backend Entry Point\n *\n * Wraps a [better-auth](https://better-auth.com) instance as an Invect plugin,\n * providing:\n * - Session-based identity resolution\n * - Proxied auth routes (sign-in, sign-up, OAuth, etc.)\n * - Authorization hook integration\n * - Express/NestJS middleware helpers\n *\n * @example\n * ```ts\n * // Simple — no separate auth setup needed:\n * import { auth } from '@invect/user-auth';\n *\n * defineConfig({\n * plugins: [auth()],\n * });\n * ```\n *\n * @example\n * ```ts\n * // With frontend UI:\n * import { auth } from '@invect/user-auth';\n * import { authFrontend } from '@invect/user-auth/ui';\n *\n * defineConfig({\n * plugins: [auth({ frontend: authFrontend })],\n * });\n * ```\n *\n * @packageDocumentation\n */\nexport { authentication, USER_AUTH_SCHEMA } from './plugin';\nexport type {\n AuthenticationPluginOptions,\n ApiKeyPluginOptions,\n BetterAuthPassthroughOptions,\n BetterAuthInstance,\n BetterAuthUser,\n BetterAuthSession,\n BetterAuthSessionResult,\n} from './types';\n\nimport type { InvectPluginDefinition } from '@invect/core';\nimport type { AuthenticationPluginOptions } from './types';\nimport { authentication } from './plugin';\n\n/**\n * Create the auth plugin definition for Invect config.\n *\n * @example\n * ```ts\n * // Express (backend only):\n * auth({ adminEmail: '...' })\n *\n * // Next.js (with frontend):\n * import { authFrontend } from '@invect/user-auth/ui';\n * auth({ adminEmail: '...', frontend: authFrontend })\n * ```\n */\nexport function auth(options: AuthenticationPluginOptions): InvectPluginDefinition {\n return {\n id: 'user-auth',\n name: 'User Authentication',\n backend: authentication(options),\n frontend: options.frontend,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAIZ,kBAAkB,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACV,2BAA2B,EAK5B,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAIZ,kBAAkB,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACV,2BAA2B,EAK5B,MAAM,SAAS,CAAC;AAqZjB;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAmG9B,CAAC;AAmTF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,2BAA2B,GAAG,YAAY,CAqjCjF"}
|
package/dist/backend/types.d.ts
CHANGED
|
@@ -305,15 +305,6 @@ export interface AuthenticationPluginOptions {
|
|
|
305
305
|
* @default []
|
|
306
306
|
*/
|
|
307
307
|
publicPaths?: string[];
|
|
308
|
-
/**
|
|
309
|
-
* What to do when session resolution fails (network error, malformed token, etc.).
|
|
310
|
-
*
|
|
311
|
-
* - `'throw'` — Return 401 Unauthorized.
|
|
312
|
-
* - `'continue'` — Set identity to null and proceed (useful for mixed auth).
|
|
313
|
-
*
|
|
314
|
-
* @default 'throw'
|
|
315
|
-
*/
|
|
316
|
-
onSessionError?: 'throw' | 'continue';
|
|
317
308
|
/**
|
|
318
309
|
* Explicit list of global admin accounts to seed and/or promote on startup.
|
|
319
310
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invect/user-auth",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Better Auth plugin for Invect — adds user authentication, session management, identity resolution, and auth UI components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"kysely": "^0.28.0",
|
|
53
|
-
"@invect/core": "0.0.
|
|
53
|
+
"@invect/core": "0.0.8"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@tanstack/react-query": "^5.0.0",
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
"tsdown": "^0.21.1",
|
|
66
66
|
"typescript": "^5.3.3",
|
|
67
67
|
"vitest": "^3.2.4",
|
|
68
|
-
"@invect/ui": "0.0.
|
|
68
|
+
"@invect/ui": "0.0.8"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@better-auth/api-key": ">=1.0.0",
|
|
72
|
-
"@invect/ui": ">=0.0.
|
|
72
|
+
"@invect/ui": ">=0.0.8",
|
|
73
73
|
"@tanstack/react-query": ">=5.0.0",
|
|
74
74
|
"better-auth": ">=1.0.0",
|
|
75
75
|
"better-sqlite3": ">=9.0.0",
|