@objectstack/plugin-auth 10.3.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/auth-plugin.ts","../src/auth-manager.ts","../src/objectql-adapter.ts","../src/auth-schema-config.ts","../src/set-initial-password.ts","../src/manifest.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-auth\n * \n * Authentication & Identity Plugin for ObjectStack\n * Powered by better-auth for robust, secure authentication\n * Uses ObjectQL for data persistence (no third-party ORM required)\n */\n\nexport * from './auth-plugin.js';\nexport * from './auth-manager.js';\nexport * from './set-initial-password.js';\nexport * from './objectql-adapter.js';\nexport * from './auth-schema-config.js';\nexport type { AuthConfig, AuthProviderConfig, AuthPluginConfig } from '@objectstack/spec/system';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport type { BetterAuthOptions } from 'better-auth';\nimport { AuthConfig, type SocialProviderConfig, SystemObjectName, SystemUserId } from '@objectstack/spec/system';\nimport {\n // ADR-0048 — the Setup/Studio/Account apps moved to their own packages\n // (@objectstack/{setup,studio,account}); plugin-auth no longer registers them.\n SystemOverviewDashboard,\n SystemOverviewDatasets,\n} from '@objectstack/platform-objects/apps';\nimport { SysOrganizationDetailPage, SysUserDetailPage } from '@objectstack/platform-objects/pages';\nimport { AuthManager, type AuthManagerOptions } from './auth-manager.js';\nimport { runSetInitialPassword } from './set-initial-password.js';\nimport {\n authIdentityObjects,\n authPluginManifestHeader,\n} from './manifest.js';\n\n/**\n * Auth Plugin Options\n * Extends AuthConfig from spec with additional runtime options\n */\nexport interface AuthPluginOptions extends Partial<AuthConfig> {\n /**\n * Whether to automatically register auth routes\n * @default true\n */\n registerRoutes?: boolean;\n \n /**\n * Base path for auth routes\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * Override the datasource that owns the identity tables (sys_user,\n * sys_session, …) when AuthPlugin's manifest is registered.\n *\n * Defaults to `'cloud'` (control-plane DB) so the historical\n * single-tenant control-plane behaviour is preserved. Per-project\n * kernels in objectos pass `'default'` so identity tables live in the\n * project's own database — each project owns its own users.\n */\n manifestDatasource?: string;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin so invitations to those roles aren't rejected with\n * ROLE_NOT_FOUND. Forwarded as-is to AuthManager. See\n * {@link AuthManagerOptions.additionalOrgRoles} for details.\n */\n additionalOrgRoles?: string[];\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. Used by\n * platform consumers (objectos kernel) to attach a\n * `user.create.after` hook that auto-provisions a personal\n * organization for JIT-created SSO users — better-auth's adapter\n * bypasses kernel-level ObjectQL middleware, so this is the only\n * hook point that fires for every user creation path (email signup,\n * social/OIDC sign-in, admin-created accounts).\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Plugin\n * \n * Provides authentication and identity services for ObjectStack applications.\n * \n * **Dual-Mode Operation:**\n * - **Server mode** (HonoServerPlugin active): Registers HTTP routes at basePath,\n * forwarding all auth requests to better-auth's universal handler.\n * - **MSW/Mock mode** (no HTTP server): Gracefully skips route registration but\n * still registers the `auth` service, allowing HttpDispatcher.handleAuth() to\n * simulate auth flows (sign-up, sign-in, etc.) for development and testing.\n * \n * Features:\n * - Session management\n * - User registration/login\n * - OAuth providers (Google, GitHub, etc.)\n * - Organization/team support\n * - 2FA, passkeys, magic links\n * \n * This plugin registers:\n * - `auth` service (auth manager instance) — always\n * - `app.com.objectstack.system` service (system object definitions) — always\n * - HTTP routes for authentication endpoints — only when HTTP server is available\n * \n * Integrates with better-auth library to provide comprehensive\n * authentication capabilities including email/password, OAuth, 2FA,\n * magic links, passkeys, and organization support.\n */\nexport class AuthPlugin implements Plugin {\n name = 'com.objectstack.auth';\n type = 'standard';\n version = '1.0.0';\n dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required\n \n private options: AuthPluginOptions;\n private authManager: AuthManager | null = null;\n private configuredSocialProviders: SocialProviderConfig | undefined;\n\n constructor(options: AuthPluginOptions = {}) {\n this.options = {\n registerRoutes: true,\n basePath: '/api/v1/auth',\n ...options\n };\n }\n\n /**\n * Open-source provider fallback: enable Google sign-in from conventional\n * provider env vars when the application did not configure Google itself.\n * Enterprise / product packages can contribute richer provider sets through\n * the `auth:configure` hook below.\n */\n private applyEnvSocialProviderFallbacks(config: AuthManagerOptions & AuthPluginOptions): void {\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n if (String(env?.OS_AUTH_GOOGLE_ENABLED ?? 'true').toLowerCase() === 'false') return;\n const googleClientId = env?.GOOGLE_CLIENT_ID;\n const googleClientSecret = env?.GOOGLE_CLIENT_SECRET;\n if (!googleClientId || !googleClientSecret) return;\n\n const socialProviders = {\n ...(config.socialProviders ?? {}),\n } as NonNullable<AuthPluginOptions['socialProviders']>;\n\n if (!socialProviders.google) {\n socialProviders.google = {\n clientId: googleClientId,\n clientSecret: googleClientSecret,\n enabled: true,\n };\n config.socialProviders = socialProviders;\n }\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Auth Plugin...');\n\n // Validate required configuration\n if (!this.options.secret) {\n throw new Error('AuthPlugin: secret is required');\n }\n\n // Get data engine service for database operations\n const dataEngine = ctx.getService<any>('data');\n if (!dataEngine) {\n ctx.logger.warn('No data engine service found - auth will use in-memory storage');\n }\n\n const authConfig: AuthManagerOptions & AuthPluginOptions = {\n ...this.options,\n dataEngine,\n };\n this.applyEnvSocialProviderFallbacks(authConfig);\n\n // Open extension point for packages that contribute auth providers\n // (enterprise SSO, hosted control-plane SSO, etc.) without forking\n // framework's AuthPlugin. Handlers mutate the draft config in place.\n await ctx.trigger('auth:configure', authConfig, ctx);\n this.configuredSocialProviders = authConfig.socialProviders\n ? { ...authConfig.socialProviders }\n : undefined;\n\n // Initialize auth manager with data engine\n this.authManager = new AuthManager(authConfig);\n\n // Register auth service\n ctx.registerService('auth', this.authManager);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...authPluginManifestHeader,\n ...(this.options.manifestDatasource\n ? { defaultDatasource: this.options.manifestDatasource }\n : {}),\n objects: authIdentityObjects,\n // ADR-0048 — Setup/Studio/Account apps (and the Setup nav contributions)\n // moved to their own one-app packages (@objectstack/{setup,studio,account}),\n // each registering under its own package id so /apps/<packageId> resolves\n // unambiguously. plugin-auth keeps only the auth objects + their pages.\n // Slotted record-detail pages for system objects — currently\n // sys_organization gets a Members / Invitations / Teams tab strip\n // (see SysOrganizationDetailPage for the rationale and the\n // intentionally-omitted OAuth / SSO tabs).\n pages: [SysOrganizationDetailPage, SysUserDetailPage],\n // List views for each Setup-nav object are defined on the schema\n // itself via the canonical `listViews` map (e.g.\n // sys_user.listViews.{all_users,unverified,two_factor}). Registering\n // top-level views here is the legacy pre-M10.30c pattern — it caused\n // duplicate \"Users\"/\"Roles\"/\"Sessions\" tabs to appear alongside the\n // schema-derived ones, sometimes referencing nonexistent fields\n // (e.g. legacy `users.view` had phone/status/active columns that do\n // not exist on sys_user). Schema-embedded listViews is the single\n // source of truth.\n dashboards: [SystemOverviewDashboard],\n // ADR-0021 — datasets backing the System Overview dashboard's widgets.\n datasets: SystemOverviewDatasets,\n });\n\n ctx.logger.info('Auth Plugin initialized successfully');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Auth Plugin...');\n\n if (!this.authManager) {\n throw new Error('Auth manager not initialized');\n }\n\n // Setup App translations are now loaded by `PlatformObjectsPlugin`\n // (in @objectstack/platform-objects). Translation bundles belong with\n // the package that defines them; auth-plugin no longer piggy-backs on\n // its kernel:ready hook for this.\n\n // Defer HTTP route registration to kernel:ready hook.\n // This ensures all plugins (including HonoServerPlugin) have completed\n // their init and start phases before we attempt to look up the\n // http-server service — making AuthPlugin resilient to plugin\n // loading order.\n if (this.options.registerRoutes) {\n ctx.hook('kernel:ready', async () => {\n // Inject the email service if available so better-auth callbacks\n // (sendResetPassword / sendVerificationEmail / sendInvitationEmail\n // / sendMagicLink) can actually deliver mail. Resolved here on\n // kernel:ready so EmailServicePlugin has had a chance to register.\n if (this.authManager) {\n await this.bindAuthSettings(ctx);\n\n try {\n const emailSvc = ctx.getService<any>('email');\n if (emailSvc) {\n this.authManager.setEmailService(emailSvc);\n ctx.logger.info('Auth: email service wired (transactional mail enabled)');\n }\n } catch {\n ctx.logger.info('Auth: no email service registered — auth callbacks will log instead of sending');\n }\n\n // Bind the email brand name (`{{appName}}`) to the live\n // `branding.workspace_name` setting so the admin UI can rename the\n // product without a redeploy. Only an *explicitly set* value\n // overrides the configured `appName` — when the operator hasn't\n // customised it (resolver returns the manifest default), we clear\n // the override so the deployment's `appName` (e.g. `OS_APP_NAME`)\n // keeps precedence. Mirrors EmailServicePlugin's settings binding.\n try {\n const settings = ctx.getService<any>('settings');\n if (settings && typeof settings.get === 'function') {\n const applyBrand = async () => {\n try {\n const resolved = await settings.get('branding', 'workspace_name', {});\n const explicit = resolved && resolved.source !== 'default'\n ? resolved.value\n : undefined;\n this.authManager?.setAppName(\n typeof explicit === 'string' ? explicit : undefined,\n );\n } catch (err: any) {\n ctx.logger.warn(\n 'Auth: failed to apply branding.workspace_name: ' + (err?.message ?? err),\n );\n }\n };\n await applyBrand();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('branding', () => {\n void applyBrand();\n });\n ctx.logger.info('Auth: bound appName to settings namespace=branding');\n }\n }\n } catch {\n // settings service is optional — keep the configured appName.\n }\n }\n\n let httpServer: IHttpServer | null = null;\n try {\n httpServer = ctx.getService<IHttpServer>('http-server');\n } catch {\n // Service not found — expected in MSW/mock mode\n }\n\n if (httpServer) {\n // Auto-detect the actual server URL when no explicit baseUrl was\n // configured, or when the configured baseUrl uses a different port\n // than the running server (e.g. port 3000 configured but 3002 bound).\n // getPort() is optional on IHttpServer; duck-type check for it.\n const serverWithPort = httpServer as IHttpServer & { getPort?: () => number };\n if (this.authManager && typeof serverWithPort.getPort === 'function') {\n const actualPort = serverWithPort.getPort();\n if (actualPort) {\n const configuredUrl = this.options.baseUrl || 'http://localhost:3000';\n const configuredOrigin = new URL(configuredUrl).origin;\n const actualUrl = `http://localhost:${actualPort}`;\n\n // Only auto-correct the port when the configured URL is already a\n // localhost URL (development mode). In production (Vercel/cloud) the\n // configured baseUrl is the real public hostname — never overwrite it\n // with a localhost URL, which would break OAuth callback URLs.\n const configuredIsLocalhost = configuredOrigin.startsWith('http://localhost');\n if (configuredIsLocalhost && configuredOrigin !== actualUrl) {\n this.authManager.setRuntimeBaseUrl(actualUrl);\n ctx.logger.info(\n `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`,\n );\n }\n }\n }\n\n // Route registration errors should propagate (server misconfiguration)\n this.registerAuthRoutes(httpServer, ctx);\n ctx.logger.info(`Auth routes registered at ${this.options.basePath}`);\n } else {\n ctx.logger.warn(\n 'No HTTP server available — auth routes not registered. ' +\n 'Auth service is still available for MSW/mock environments via HttpDispatcher.'\n );\n }\n });\n }\n\n // Dev-only: provision a known, loginable platform admin on an empty DB.\n // Registered as its own kernel:ready hook (independent of registerRoutes)\n // so it runs whenever the runtime boots in development.\n ctx.hook('kernel:ready', async () => {\n await this.maybeSeedDevAdmin(ctx);\n });\n\n // Register auth middleware on ObjectQL engine (if available)\n try {\n const ql = ctx.getService<any>('objectql');\n if (ql && typeof ql.registerMiddleware === 'function') {\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // If context already has userId or isSystem, skip auth resolution\n if (opCtx.context?.userId || opCtx.context?.isSystem) {\n return next();\n }\n // Future: resolve session from AsyncLocalStorage or request context\n await next();\n });\n ctx.logger.info('Auth middleware registered on ObjectQL engine');\n }\n } catch (_e) {\n ctx.logger.debug('ObjectQL engine not available, skipping auth middleware registration');\n }\n\n ctx.logger.info('Auth Plugin started successfully');\n }\n\n /**\n * Bind the small open-source auth settings namespace to better-auth config.\n *\n * Only explicit settings values (stored or OS_AUTH_* env overrides) affect\n * runtime config. Manifest defaults are UI defaults and do not mask code or\n * deployment configuration.\n */\n private async bindAuthSettings(ctx: PluginContext): Promise<void> {\n if (!this.authManager) return;\n\n let settings: any;\n try {\n settings = ctx.getService<any>('settings');\n } catch {\n return;\n }\n if (!settings || typeof settings.getNamespace !== 'function') return;\n\n const applySettings = async (): Promise<void> => {\n if (!this.authManager) return;\n try {\n const payload = await settings.getNamespace('auth');\n const values: Record<string, unknown> = {};\n const sources: Record<string, string | undefined> = {};\n for (const [key, entry] of Object.entries(payload.values as Record<string, any>)) {\n values[key] = entry?.value;\n sources[key] = entry?.source;\n }\n\n const isExplicit = (key: string) => (sources[key] ?? 'default') !== 'default';\n const asBoolean = (value: unknown, fallback: boolean): boolean => {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') return value.toLowerCase() !== 'false';\n if (typeof value === 'number') return value !== 0;\n return fallback;\n };\n const asTrimmedString = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n };\n const asPositiveInt = (value: unknown): number | undefined => {\n const n = Math.floor(Number(value));\n return Number.isFinite(n) && n > 0 ? n : undefined;\n };\n\n const patch: Partial<AuthManagerOptions> = {};\n const emailAndPassword: Partial<NonNullable<AuthConfig['emailAndPassword']>> = {};\n if (isExplicit('email_password_enabled')) {\n emailAndPassword.enabled = asBoolean(values.email_password_enabled, true);\n }\n if (isExplicit('signup_enabled')) {\n emailAndPassword.disableSignUp = !asBoolean(values.signup_enabled, true);\n }\n if (isExplicit('require_email_verification')) {\n emailAndPassword.requireEmailVerification = asBoolean(\n values.require_email_verification,\n false,\n );\n }\n // Password policy — better-auth enforces these bounds on sign-up and\n // password reset. Ignore malformed/non-positive values (keep the default).\n if (isExplicit('password_min_length')) {\n const n = asPositiveInt(values.password_min_length);\n if (n !== undefined) emailAndPassword.minPasswordLength = n;\n }\n if (isExplicit('password_max_length')) {\n const n = asPositiveInt(values.password_max_length);\n if (n !== undefined) emailAndPassword.maxPasswordLength = n;\n }\n if (Object.keys(emailAndPassword).length > 0) {\n patch.emailAndPassword = emailAndPassword as AuthManagerOptions['emailAndPassword'];\n }\n\n // Session lifetime — days → seconds for better-auth's `session`\n // (`expiresIn` = absolute lifetime; `updateAge` = refresh threshold).\n const session: { expiresIn?: number; updateAge?: number } = {};\n if (isExplicit('session_expiry_days')) {\n const d = asPositiveInt(values.session_expiry_days);\n if (d !== undefined) session.expiresIn = d * 86_400;\n }\n if (isExplicit('session_refresh_days')) {\n const d = asPositiveInt(values.session_refresh_days);\n if (d !== undefined) session.updateAge = d * 86_400;\n }\n if (Object.keys(session).length > 0) {\n patch.session = session as AuthManagerOptions['session'];\n }\n\n if (\n isExplicit('google_enabled') ||\n isExplicit('google_client_id') ||\n isExplicit('google_client_secret')\n ) {\n const socialProviders = {\n ...(this.configuredSocialProviders ?? {}),\n } as NonNullable<SocialProviderConfig>;\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n const googleEnabledFromEnv = env?.OS_AUTH_GOOGLE_ENABLED != null\n ? asBoolean(env.OS_AUTH_GOOGLE_ENABLED, true)\n : undefined;\n const googleClientId = asTrimmedString(values.google_client_id) ?? env?.GOOGLE_CLIENT_ID;\n const googleClientSecret = asTrimmedString(values.google_client_secret) ?? env?.GOOGLE_CLIENT_SECRET;\n if (googleEnabledFromEnv ?? (isExplicit('google_enabled') ? asBoolean(values.google_enabled, true) : true)) {\n if (!socialProviders.google && googleClientId && googleClientSecret) {\n socialProviders.google = {\n clientId: googleClientId,\n clientSecret: googleClientSecret,\n enabled: true,\n };\n }\n } else {\n delete socialProviders.google;\n }\n patch.socialProviders = Object.keys(socialProviders).length > 0\n ? socialProviders\n : undefined;\n }\n\n if (Object.keys(patch).length > 0) {\n this.authManager.applyConfigPatch(patch);\n }\n } catch (err: any) {\n ctx.logger.warn('Auth: failed to apply auth settings: ' + (err?.message ?? err));\n }\n };\n\n await applySettings();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('auth', () => {\n void applySettings();\n });\n ctx.logger.info('Auth: bound to settings namespace=auth');\n }\n }\n\n async destroy(): Promise<void> {\n // Cleanup if needed\n this.authManager = null;\n }\n\n /**\n * Dev-only admin bootstrap.\n *\n * On an EMPTY database (zero users), provision a well-known, loginable\n * admin (admin@objectos.ai / admin123 by default) so backend debugging\n * never blocks on a first-run sign-up wizard. The account is created\n * through better-auth's real server-side `signUpEmail` pipeline (hashed\n * credential + the same hooks the HTTP endpoint runs), so it is fully\n * loginable; plugin-security's first-user middleware then promotes it to\n * platform admin automatically.\n *\n * This replaces two earlier, divergent seeds:\n * • the CLI-side HTTP seed (`os dev`), which POSTed the public sign-up\n * endpoint from the parent process — racing server readiness and\n * targeting a hard-coded port that broke under dev port auto-shift; and\n * • plugin-dev's raw `sys_user` insert, which produced a credential-less,\n * un-loginable row.\n * Running it in-process needs no port and no readiness polling.\n *\n * Idempotent and non-destructive: it only ever acts on a zero-user DB and\n * never touches an existing account, so a custom password is never\n * overwritten.\n *\n * HARD-GATED to development (NODE_ENV==='development'): a known-credential\n * admin can never be provisioned in production. Opt out within dev via\n * OS_SEED_ADMIN=0 (or false/off/no).\n */\n private async maybeSeedDevAdmin(ctx: PluginContext): Promise<void> {\n if (process.env.NODE_ENV !== 'development') return;\n const flag = String(process.env.OS_SEED_ADMIN ?? '').trim().toLowerCase();\n if (['0', 'false', 'off', 'no'].includes(flag)) return;\n\n const email = process.env.OS_SEED_ADMIN_EMAIL?.trim() || 'admin@objectos.ai';\n const password = process.env.OS_SEED_ADMIN_PASSWORD?.trim() || 'admin123';\n const name = process.env.OS_SEED_ADMIN_NAME?.trim() || 'Dev Admin';\n\n let ql: any;\n try { ql = ctx.getService<any>('objectql'); } catch { /* unavailable */ }\n if (!ql || typeof ql.find !== 'function') return;\n\n try {\n // Only seed when no HUMAN user exists yet. A fresh DB still contains\n // the system service account (SystemUserId.SYSTEM, role='system'),\n // which must NOT count — mirror plugin-security's first-user detection\n // so the seed fires on a genuinely empty DB. Any real human user (or a\n // prior sign-up) disables the seed for good; we never touch or\n // overwrite an existing account.\n const rows = await ql\n .find(SystemObjectName.USER, { where: {}, limit: 50 }, { context: { isSystem: true } })\n .catch(() => []);\n const humans = (Array.isArray(rows) ? rows : [])\n .filter((u: any) => u && u.id !== SystemUserId.SYSTEM && u.role !== 'system');\n if (humans.length > 0) {\n ctx.logger.debug('[auth] dev admin seed skipped — a user already exists');\n return;\n }\n\n if (!this.authManager) return;\n const api: any = await this.authManager.getApi();\n if (typeof api?.signUpEmail !== 'function') {\n ctx.logger.warn('[auth] dev admin seed skipped — signUpEmail unavailable');\n return;\n }\n\n // Real auth pipeline: creates sys_user + a hashed `credential` account\n // and runs the sign-up hooks. The dev-mode OS_DISABLE_SIGNUP bypass\n // (auth-manager.ts) lets this through on an empty DB even when sign-up\n // is otherwise disabled.\n await api.signUpEmail({ body: { email, password, name } });\n ctx.logger.info(`🔑 Dev admin seeded: ${email} / ${password}`);\n // Surface the credentials in the `serve` startup banner. The\n // ctx.logger line above is swallowed by serve's boot-quiet window\n // (the seed runs during runtime.start(), before stdout is restored),\n // so the CLI reads this off the `auth` service and prints it after\n // the banner instead.\n this.authManager.devSeedResult = { email, password };\n } catch (err: any) {\n // Best-effort. The common benign case is a race where a real sign-up\n // landed first (unique-email violation) — treat as \"already seeded\".\n ctx.logger.warn(`[auth] dev admin seed skipped: ${err?.message ?? err}`);\n }\n }\n\n /**\n * Register authentication routes with HTTP server\n * \n * Uses better-auth's universal handler for all authentication requests.\n * This forwards all requests under basePath to better-auth, which handles:\n * - Email/password authentication\n * - OAuth providers (Google, GitHub, etc.)\n * - Session management\n * - Password reset\n * - Email verification\n * - 2FA, passkeys, magic links (if enabled)\n */\n private registerAuthRoutes(httpServer: IHttpServer, ctx: PluginContext): void {\n if (!this.authManager) return;\n\n const basePath = this.options.basePath || '/api/v1/auth';\n\n // Get raw Hono app to use native wildcard routing\n // Type assertion is safe here because we explicitly require Hono server as a dependency\n if (!('getRawApp' in httpServer) || typeof (httpServer as any).getRawApp !== 'function') {\n ctx.logger.error('HTTP server does not support getRawApp() - wildcard routing requires Hono server');\n throw new Error(\n 'AuthPlugin requires HonoServerPlugin for wildcard routing support. ' +\n 'Please ensure HonoServerPlugin is loaded before AuthPlugin.'\n );\n }\n\n const rawApp = (httpServer as any).getRawApp();\n\n // Register /config before the wildcard so it takes precedence.\n // better-auth has no /config endpoint, so without this explicit route\n // the wildcard below forwards the request and better-auth returns 404.\n rawApp.get(`${basePath}/config`, (c: any) => {\n try {\n const config = this.authManager!.getPublicConfig();\n return c.json({ success: true, data: config });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({ success: false, error: { code: 'auth_config_error', message: err.message } }, 500);\n }\n });\n\n // Bootstrap status — does an owner exist yet? Used by the Account SPA's\n // root route to decide between rendering /login (normal flow) and\n // /setup (first-run owner creation). Public, unauthenticated; only\n // returns a boolean so it can be polled before the user has any\n // credentials.\n rawApp.get(`${basePath}/bootstrap-status`, async (c: any) => {\n try {\n const dataEngine = this.authManager!.getDataEngine();\n if (!dataEngine) {\n // No data engine wired (e.g. MSW/mock mode) — assume bootstrapped\n // so the SPA falls through to its normal login flow.\n return c.json({ hasOwner: true });\n }\n const count = await dataEngine.count('sys_user', {});\n return c.json({ hasOwner: (count ?? 0) > 0 });\n } catch (error) {\n ctx.logger.warn('[AuthPlugin] bootstrap-status check failed; assuming bootstrapped', error as Error);\n return c.json({ hasOwner: true });\n }\n });\n\n // Device Authorization Grant (RFC 8628) endpoints — `/device/code`,\n // `/device/token`, `/device/approve`, `/device/deny`, `/device` — are\n // provided by better-auth's `device-authorization` plugin and reach\n // the wildcard handler below. Enable via\n // `AuthPluginConfig.deviceAuthorization`.\n\n // Set an INITIAL local password for users who signed in via SSO and\n // have no `credential` account yet. This is the \"Set local password\"\n // affordance on a per-environment kernel — it lets a user that the\n // platform onboarded via the objectstack-cloud OAuth provider sign in\n // with email/password against this environment going forward without\n // needing the SSO round-trip. Requires a valid session (so we know\n // which user is asking) and refuses if a credential already exists\n // (the user should use better-auth's /change-password endpoint in\n // that case so the current password is verified).\n //\n // The body is `runSetInitialPassword` (shared with the cloud\n // AuthProxyPlugin) so both mount points wrap better-auth's server-only\n // `auth.api.setPassword` identically — see set-initial-password.ts.\n rawApp.post(`${basePath}/set-initial-password`, async (c: any) => {\n try {\n const authApi = await this.authManager!.getApi();\n const { status, body } = await runSetInitialPassword(authApi as any, c.req.raw);\n return c.json(body, status);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] set-initial-password failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth admin: toggle the `disabled` flag on a registered OAuth client.\n //\n // Why this lives here (and not as a plain data-layer UPDATE on\n // sys_oauth_application): better-auth 1.6.11's stock admin update\n // endpoint (`/admin/oauth2/update-client`) does NOT accept `disabled`\n // in its Zod body schema, so the field gets silently stripped before\n // it reaches `updateClientEndpoint`. The column exists, the runtime\n // honours it everywhere (introspect, token, authorize, public-client\n // lookup), but no client-facing API can flip it.\n //\n // We close the gap by writing through better-auth's own adapter under\n // the `/api/v1/auth/*` namespace so all OAuth-application mutations\n // remain auth-routed (no generic data-layer bypass for the `oauth_client`\n // model). When upstream adds `disabled` to `adminUpdateOAuthClient`'s\n // schema, this route can be deleted and the sys_oauth_application\n // action retargeted to the stock endpoint.\n //\n // Upstream tracking: https://github.com/better-auth/better-auth\n rawApp.post(`${basePath}/admin/oauth2/toggle-disabled`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const clientId: unknown = body?.client_id;\n const disabled: unknown = body?.disabled;\n if (typeof clientId !== 'string' || clientId.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'client_id is required' } }, 400);\n }\n if (typeof disabled !== 'boolean') {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'disabled must be a boolean' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n // The customSession plugin synthesizes `user.role = 'admin'` for\n // platform admins (admin_full_access permission set) and active-org\n // owners/admins; anyone else is denied.\n if ((session.user as any).role !== 'admin') {\n return c.json({ success: false, error: { code: 'forbidden', message: 'Admin role required' } }, 403);\n }\n\n // Write through the same ObjectQL data engine that better-auth's\n // adapter uses. We target the snake_case table name (`sys_oauth_application`,\n // mapped from better-auth's internal `oauthClient` model via\n // `auth-schema-config.ts`) because `$context.adapter`'s model-lookup\n // helper does not see plugin-provided model names from outside\n // better-auth's own endpoint invocation context. This is the same\n // physical row the better-auth runtime reads at introspect / token\n // / authorize time, so the toggle is fully honoured.\n const dataEngine: any = this.authManager!.getDataEngine();\n if (!dataEngine) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Data engine unavailable' } }, 503);\n }\n\n const existing = await dataEngine.findOne('sys_oauth_application', {\n where: { client_id: clientId },\n });\n if (!existing) {\n return c.json({ success: false, error: { code: 'not_found', message: 'OAuth client not found' } }, 404);\n }\n\n const updated = await dataEngine.update('sys_oauth_application', {\n id: existing.id,\n disabled,\n updated_at: new Date(Math.floor(Date.now() / 1000) * 1000),\n });\n if (!updated) {\n return c.json({ success: false, error: { code: 'internal', message: 'Unable to update OAuth client' } }, 500);\n }\n\n return c.json({\n success: true,\n data: {\n client_id: clientId,\n disabled,\n },\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] toggle-disabled failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth self-service: register an OAuth application for the signed-in\n // user. Thin wrapper over better-auth's `/oauth2/create-client`\n // endpoint (session-required, auto-stamps `user_id` from the session).\n //\n // Why this wrapper exists: the Account-app action surfaces a\n // user-friendly textarea for \"Redirect URLs\" (one per line), but\n // better-auth's Zod body schema requires `redirect_uris: string[]`.\n // The metadata-driven action runner POSTs param values verbatim, so\n // without a translation layer the upstream call fails validation with\n // `Invalid input: expected array, received string`. We split the\n // textarea on newlines, trim, drop empties, and forward to\n // `createOAuthClient` so the row gets persisted with the caller's\n // user_id and shows up in the `mine` listView.\n //\n // Upstream alternative would be enabling `allowDynamicClientRegistration`\n // on `/oauth2/register`, but DCR has additional security implications\n // (rate limiting, scope restriction) we don't want to enable broadly\n // just to fix UX. Keeping the wrapper scoped to the self-service flow.\n rawApp.post(`${basePath}/sys-oauth-application/register`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n\n const name: unknown = body?.name;\n const redirectUrlsInput: unknown = body?.redirectURLs;\n const type: unknown = body?.type;\n\n if (typeof name !== 'string' || name.trim().length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'name is required' } }, 400);\n }\n if (typeof redirectUrlsInput !== 'string' || redirectUrlsInput.trim().length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'redirectURLs is required' } }, 400);\n }\n\n const redirectUris = redirectUrlsInput\n .split(/[\\r\\n]+/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n if (redirectUris.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'redirectURLs must contain at least one URL' } }, 400);\n }\n\n const allowedTypes = new Set(['web', 'native', 'user-agent-based']);\n const safeType = typeof type === 'string' && allowedTypes.has(type) ? type : 'web';\n\n const authApi: any = await this.authManager!.getApi();\n if (!authApi?.createOAuthClient) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'OIDC provider is not enabled on this environment' } }, 503);\n }\n\n // Forward request headers so better-auth can resolve the caller's\n // session (sessionMiddleware on /oauth2/create-client). Without\n // the session the row would lack `user_id` and never appear in\n // the My Applications view.\n let result: any;\n try {\n result = await authApi.createOAuthClient({\n body: {\n client_name: name.trim(),\n redirect_uris: redirectUris,\n type: safeType,\n },\n headers: c.req.raw.headers,\n });\n } catch (err: any) {\n const status = typeof err?.status === 'number' ? err.status : 500;\n const code = err?.body?.error ?? 'oauth_register_failed';\n const message = err?.body?.error_description ?? err?.message ?? 'Unable to register OAuth client';\n return c.json({ success: false, error: { code, message } }, status);\n }\n\n // Mirror the response shape consumed by the action's resultDialog\n // (`client.client_id`, `client.client_secret`).\n return c.json({ success: true, data: { client: result } });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sys-oauth-application/register failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // Register wildcard route to forward all auth requests to better-auth.\n // better-auth is configured with basePath matching our route prefix, so we\n // forward the original request directly — no path rewriting needed.\n rawApp.all(`${basePath}/*`, async (c: any) => {\n try {\n // Forward the original request to better-auth handler\n const response = await this.authManager!.handleRequest(c.req.raw);\n\n // better-auth catches internal errors and returns error Responses\n // without throwing, so the catch block below would never trigger.\n // We proactively log server errors here for observability.\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: ${body}`));\n } catch {\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: (unable to read body)`));\n }\n }\n\n // Public-cache JWKS: it's static JSON that only changes when the\n // signing key rotates (default ~30 days). better-auth doesn't set\n // any Cache-Control header, so every relying party currently\n // re-fetches it on every JWT verification (≈700 ms warm against a\n // Container DO + Neon). Add a conservative public cache so CF's\n // edge can short-circuit repeated fetches. The 5-min freshness +\n // 24 h SWR window is well inside better-auth's default rotation\n // and matches what most IdPs publish (Auth0, Cognito, Google).\n try {\n const url = c.req.url as string;\n if (response.ok && /\\/jwks(\\?|$)/.test(url)) {\n const existing = response.headers.get('cache-control');\n if (!existing) {\n response.headers.set(\n 'cache-control',\n 'public, max-age=300, stale-while-revalidate=86400',\n );\n }\n }\n } catch { /* best-effort header annotation */ }\n\n return response;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('Auth request error:', err);\n \n // Return error response\n return new Response(\n JSON.stringify({\n success: false,\n error: err.message,\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n });\n\n // OIDC / OAuth 2.0 Authorization Server Metadata (RFC 8414) and\n // OpenID Connect Discovery 1.0 require the well-known documents to be\n // served from the **root** of the issuer URL — not under our auth\n // basePath. `@better-auth/oauth-provider` ships dedicated helpers for\n // this case (`oauthProviderAuthServerMetadata` /\n // `oauthProviderOpenIdConfigMetadata`) which we mount here so external\n // OIDC clients can discover the IdP at the canonical paths.\n //\n // Honour the same `OS_OIDC_PROVIDER_ENABLED` env-var override that\n // `AuthManager.buildPlugins()` uses — without this check the\n // discovery routes would NOT mount when an operator flipped the\n // env var on without editing the config file, leaving external\n // OIDC clients unable to discover the IdP.\n const oidcEnv = (globalThis as any)?.process?.env?.OS_OIDC_PROVIDER_ENABLED;\n const oidcFromEnv = oidcEnv != null ? String(oidcEnv).toLowerCase() === 'true' : undefined;\n const oidcEnabled = oidcFromEnv ?? this.options.plugins?.oidcProvider ?? false;\n if (oidcEnabled) {\n void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {\n ctx.logger.error('Failed to register OIDC discovery routes', error as Error);\n });\n }\n\n ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);\n }\n\n /**\n * Mount the OIDC / OAuth 2.0 well-known discovery documents at the root\n * URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the\n * documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`\n * relative to the issuer, not under the auth basePath.\n */\n private async registerOidcDiscoveryRoutes(rawApp: any, ctx: PluginContext): Promise<void> {\n const auth = await this.authManager!.getAuthInstance();\n const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import(\n '@better-auth/oauth-provider'\n );\n\n const authServerHandler = oauthProviderAuthServerMetadata(auth as any);\n const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth as any);\n\n // Cache-Control for OIDC discovery docs. These describe stable issuer\n // configuration (endpoints, supported scopes, signing algs); they\n // change only on app redeploy. CF edge can short-circuit repeated\n // fetches and dramatically cut SSO first-call latency.\n const DISCOVERY_CACHE = 'public, max-age=300, stale-while-revalidate=86400';\n const withDiscoveryCache = async (handler: (req: Request) => Promise<Response> | Response, req: Request): Promise<Response> => {\n const resp = await handler(req);\n try {\n if (resp.ok && !resp.headers.get('cache-control')) {\n resp.headers.set('cache-control', DISCOVERY_CACHE);\n }\n } catch { /* best-effort */ }\n return resp;\n };\n\n rawApp.get('/.well-known/oauth-authorization-server', (c: any) => withDiscoveryCache(authServerHandler, c.req.raw));\n rawApp.get('/.well-known/openid-configuration', (c: any) => withDiscoveryCache(openidConfigHandler, c.req.raw));\n\n ctx.logger.info(\n 'OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}',\n );\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Auth, BetterAuthOptions } from 'better-auth';\n// better-auth value imports (betterAuth + plugins) are deferred via dynamic\n// import() in getOrCreateAuth() / buildPluginList() so that disabled plugins\n// never get loaded into the process. See Stage 2F (RSS investigation).\nimport type {\n AuthConfig,\n EmailAndPasswordConfig,\n AuthPluginConfig,\n OidcProvidersConfig,\n} from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/core';\nimport type { IEmailService } from '@objectstack/spec/contracts';\nimport { readEnvWithDeprecation } from '@objectstack/types';\nimport { createObjectQLAdapterFactory } from './objectql-adapter.js';\nimport {\n AUTH_USER_CONFIG,\n AUTH_SESSION_CONFIG,\n AUTH_ACCOUNT_CONFIG,\n AUTH_VERIFICATION_CONFIG,\n buildOrganizationPluginSchema,\n buildTwoFactorPluginSchema,\n buildOauthProviderPluginSchema,\n buildDeviceAuthorizationPluginSchema,\n buildJwtPluginSchema,\n buildAdminPluginSchema,\n} from './auth-schema-config.js';\n\n/**\n * Detect WebContainer (StackBlitz) environment.\n *\n * WebContainer reports itself as Node.js but runs inside a browser. Several\n * Node APIs are polyfilled with significant behavioural differences — most\n * notably `node:async_hooks.AsyncLocalStorage`, whose `run()` does NOT\n * propagate the store across `await` boundaries the way Node's native\n * implementation does.\n */\nfunction isWebContainerRuntime(): boolean {\n if (typeof globalThis === 'undefined') return false;\n const proc = (globalThis as any).process;\n return (\n Boolean(proc?.versions?.webcontainer) ||\n Boolean(proc?.env?.SHELL?.includes?.('jsh')) ||\n Boolean(proc?.env?.STACKBLITZ)\n );\n}\n\n/**\n * Synchronous AsyncLocalStorage polyfill compatible with better-auth's\n * `requestStateAsyncStorage` slot.\n *\n * Behaviour:\n * - `run(store, fn)` sets the current store synchronously before invoking\n * `fn` and restores the previous store after `fn` (and any promise it\n * returns) settles.\n * - `getStore()` returns the current store.\n *\n * Why a polyfill is needed in WebContainer:\n * - WebContainer's `node:async_hooks` does not propagate ALS context through\n * `await`, so better-auth's `runWithRequestState(map, () => handler(req))`\n * wrap loses the store as soon as the call chain awaits anything (e.g.\n * the inner `customSession` → `getSession()` call). All endpoints that\n * read request-state via `defineRequestState()` then throw\n * \"No request state found\".\n *\n * Single-flight caveat:\n * - This polyfill is process-global, not async-context-local. In a real\n * server it could leak state across concurrent requests. That risk is\n * acceptable here because:\n * 1) It is only installed when WebContainer is detected (dev / preview\n * sandboxes that handle one request at a time).\n * 2) Each request still wraps the entire handler in `runWithRequestState`\n * with a fresh WeakMap, so the in-flight request always sees its own\n * store as long as nothing else mutates the slot mid-flight.\n */\nclass WebContainerRequestStateAsyncLocalStorage<T> {\n private current: T | undefined = undefined;\n\n run<R>(store: T, fn: () => R): R {\n const prev = this.current;\n this.current = store;\n try {\n const result = fn() as unknown;\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).finally(() => {\n this.current = prev;\n }) as unknown as R;\n }\n this.current = prev;\n return result as R;\n } catch (err) {\n this.current = prev;\n throw err;\n }\n }\n\n getStore(): T | undefined {\n return this.current;\n }\n}\n\n/**\n * Pre-populate better-auth's global `requestStateAsyncStorage` slot with the\n * synchronous polyfill when running inside WebContainer.\n *\n * Better-auth caches its AsyncLocalStorage instance on\n * `globalThis[Symbol.for('better-auth:global')].context.requestStateAsyncStorage`\n * the first time `ensureAsyncStorage()` runs (see\n * `@better-auth/core/dist/context/request-state.mjs`). By seeding that slot\n * BEFORE any better-auth code touches it, every call to\n * `runWithRequestState` / `getCurrentRequestState` — including the\n * `@better-auth/core` copy that `plugin-auth` imports directly and the copy\n * bundled with `better-auth` itself — share the same working polyfill.\n *\n * Outside WebContainer this is a no-op so production deployments keep\n * Node's native AsyncLocalStorage.\n */\nfunction installWebContainerRequestStatePolyfill(): void {\n if (!isWebContainerRuntime()) return;\n const sym = Symbol.for('better-auth:global');\n const g = globalThis as any;\n if (!g[sym]) {\n g[sym] = { version: '0.0.0-polyfill', epoch: 0, context: {} };\n }\n if (!g[sym].context) g[sym].context = {};\n if (!g[sym].context.requestStateAsyncStorage) {\n g[sym].context.requestStateAsyncStorage = new WebContainerRequestStateAsyncLocalStorage();\n // eslint-disable-next-line no-console\n console.warn(\n '[AuthManager] WebContainer detected: installed synchronous request-state polyfill ' +\n '(node:async_hooks AsyncLocalStorage does not propagate context across await in WebContainer).',\n );\n }\n}\n\nfunction readBooleanEnv(name: string, legacyName?: string): boolean | undefined {\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n const raw = env?.[name] ?? (legacyName ? env?.[legacyName] : undefined);\n if (raw == null) return undefined;\n const normalized = String(raw).trim().toLowerCase();\n return !['0', 'false', 'off', 'no'].includes(normalized);\n}\n\nfunction readDisableSignUpEnv(): boolean | undefined {\n const signupEnabled = readBooleanEnv('OS_AUTH_SIGNUP_ENABLED');\n if (signupEnabled != null) return !signupEnabled;\n return readBooleanEnv('OS_DISABLE_SIGNUP');\n}\n\n/**\n * Extended options for AuthManager\n */\nexport interface AuthManagerOptions extends Partial<AuthConfig> {\n /**\n * Better-Auth instance (for advanced use cases)\n * If not provided, one will be created from config\n */\n authInstance?: Auth<any>;\n\n /**\n * ObjectQL Data Engine instance\n * Required for database operations using ObjectQL instead of third-party ORMs\n */\n dataEngine?: IDataEngine;\n\n /**\n * Base path for auth routes\n * Forwarded to better-auth's basePath option so it can match incoming\n * request URLs without manual path rewriting.\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * OIDC / Generic OAuth2 providers for enterprise SSO.\n * Each entry is passed to better-auth's genericOAuth plugin.\n */\n oidcProviders?: OidcProvidersConfig;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin. Each name becomes a valid role for invitations and\n * member assignments without going through Better-Auth's default\n * `owner|admin|member` whitelist.\n *\n * The ObjectStack SecurityPlugin handles real RBAC enforcement by matching\n * these role names against `permission` metadata (PermissionSets / Profiles),\n * so Better-Auth only needs to accept them as opaque strings. Each role is\n * registered with the minimum access-control privileges (equivalent to\n * Better-Auth's `member` role) so it cannot inadvertently grant org-level\n * admin capabilities.\n *\n * Typical source: the union of `permission` metadata names that have\n * `isProfile: true`, collected from the loaded stack at CLI boot.\n *\n * @example ['sales_rep', 'sales_manager', 'service_agent']\n */\n additionalOrgRoles?: string[];\n\n /**\n * Optional outbound email service used by better-auth callbacks\n * (`sendResetPassword`, `sendVerificationEmail`, `sendInvitationEmail`,\n * `sendMagicLink`). When omitted, those callbacks degrade to logging\n * the action URL — keeping flows usable in pilots / local dev — but\n * production deployments SHOULD wire one via `setEmailService()`.\n *\n * Resolved lazily through {@link AuthManager.getEmailService}; safe\n * to set after construction. AuthPlugin wires this from the kernel\n * service registry on `kernel:ready`.\n */\n emailService?: IEmailService;\n\n /**\n * Display name used by built-in auth email templates (`{{appName}}`\n * placeholder). Defaults to `'ObjectStack'` when omitted.\n */\n appName?: string;\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. better-auth fires\n * these around its own adapter writes (e.g. when `genericOAuth` creates\n * a JIT user during SSO login), which the kernel-level ObjectQL\n * middleware does NOT observe — better-auth's adapter goes through\n * `dataEngine` directly, bypassing the `ql.registerMiddleware` chain.\n *\n * The platform uses this to attach a `user.create.after` hook that\n * auto-provisions a personal organization for every newly-created user\n * (mirroring what SecurityPlugin's middleware does for direct\n * ObjectQL inserts) so SSO-arriving users don't land on the empty\n * \"create organization\" screen.\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Manager\n *\n * Wraps better-auth and provides authentication services for ObjectStack.\n * Supports multiple authentication methods:\n * - Email/password\n * - OAuth providers (Google, GitHub, etc.)\n * - Magic links\n * - Two-factor authentication\n * - Passkeys\n * - Organization/teams\n */\nexport class AuthManager {\n private auth: Auth<any> | null = null;\n private config: AuthManagerOptions;\n\n /**\n * Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`\n * when it provisions the well-known admin on an empty DB). The `serve`\n * command reads this after boot to surface the credentials in the startup\n * banner. Undefined when no seed ran (production, opt-out, or a DB that\n * already had a user).\n */\n public devSeedResult?: { email: string; password: string };\n\n constructor(config: AuthManagerOptions) {\n this.config = config;\n\n // WebContainer (StackBlitz) compatibility — install a synchronous\n // AsyncLocalStorage polyfill for better-auth's request-state global\n // BEFORE better-auth ever instantiates its own. See the helper for the\n // full rationale.\n installWebContainerRequestStatePolyfill();\n\n // Use provided auth instance\n if (config.authInstance) {\n this.auth = config.authInstance;\n }\n // Don't create auth instance automatically to avoid database initialization errors\n // It will be created lazily when needed\n }\n\n /**\n * Get or create the better-auth instance (lazy initialization)\n */\n private async getOrCreateAuth(): Promise<Auth<any>> {\n if (!this.auth) {\n this.auth = await this.createAuthInstance();\n }\n return this.auth;\n }\n\n /**\n * Create a better-auth instance from configuration\n */\n private async createAuthInstance(): Promise<Auth<any>> {\n const { betterAuth } = await import('better-auth');\n const { createAuthMiddleware } = await import('better-auth/api');\n const plugins = await this.buildPluginList();\n const passwordHasher = await this.resolvePasswordHasher();\n const betterAuthConfig: BetterAuthOptions = {\n // Base configuration\n secret: this.config.secret || this.generateSecret(),\n baseURL: this.config.baseUrl || 'http://localhost:3000',\n basePath: this.config.basePath || '/api/v1/auth',\n\n // Database adapter configuration\n database: this.createDatabaseConfig(),\n\n // Model/field mapping: camelCase (better-auth) → snake_case (ObjectStack)\n // These declarations tell better-auth the actual table/column names used\n // by ObjectStack's protocol layer, enabling automatic transformation via\n // createAdapterFactory.\n user: {\n ...AUTH_USER_CONFIG,\n },\n account: {\n ...AUTH_ACCOUNT_CONFIG,\n // Allow OIDC/OAuth callbacks to implicitly link the incoming\n // identity to a pre-existing local user when the emails match.\n //\n // ObjectStack's platform SSO (\"objectstack-cloud\" provider) is the\n // canonical case: cloud is the IdP for every project, so a user\n // arriving via SSO is — by construction — the same person who was\n // auto-seeded as the project owner when the project was created.\n // Without trusting the provider, better-auth's safety check rejects\n // the link with `error=account_not_linked` because the seeded user\n // row has `emailVerified=false` (no actual verification ever runs\n // in the IdP-mediated flow). See packages/plugins/plugin-auth/\n // node_modules/better-auth/dist/oauth2/link-account.mjs:22.\n //\n // Custom-deployment consumers can extend the trusted set via\n // `config.account.accountLinking.trustedProviders`; we always\n // include `objectstack-cloud` because it is the platform IdP.\n accountLinking: {\n enabled: true,\n // better-auth's account-linking gate has TWO independent clauses\n // (see link-account.mjs:22). Trusting the provider only satisfies\n // the first clause; the second — `requireLocalEmailVerified &&\n // !dbUser.user.emailVerified` — still blocks linking when the\n // pre-existing local user row has `emailVerified=false` (the\n // default for owner-seeded rows). Disabling the local-email gate\n // is safe here because the OAuth side is what we actually trust:\n // the incoming identity was verified by the IdP. Consumers who\n // need the stricter behavior can override via config.\n requireLocalEmailVerified: false,\n ...((this.config as any)?.account?.accountLinking ?? {}),\n trustedProviders: Array.from(new Set([\n 'objectstack-cloud',\n ...((this.config as any)?.account?.accountLinking?.trustedProviders ?? []),\n ])),\n },\n },\n verification: {\n ...AUTH_VERIFICATION_CONFIG,\n },\n\n // Social / OAuth providers\n ...(this.config.socialProviders ? { socialProviders: this.config.socialProviders as any } : {}),\n\n // Email and password configuration.\n // `disableSignUp`: env overrides config/settings so deployments can\n // lock the registration policy without relying on UI state.\n emailAndPassword: (() => {\n const disableSignUpFromEnv = readDisableSignUpEnv();\n const effectiveDisableSignUp = disableSignUpFromEnv ?? this.config.emailAndPassword?.disableSignUp;\n return {\n enabled: this.config.emailAndPassword?.enabled ?? true,\n ...(passwordHasher ? { password: passwordHasher } : {}),\n ...(effectiveDisableSignUp != null\n ? { disableSignUp: effectiveDisableSignUp } : {}),\n ...(this.config.emailAndPassword?.requireEmailVerification != null\n ? { requireEmailVerification: this.config.emailAndPassword.requireEmailVerification } : {}),\n ...(this.config.emailAndPassword?.minPasswordLength != null\n ? { minPasswordLength: this.config.emailAndPassword.minPasswordLength } : {}),\n ...(this.config.emailAndPassword?.maxPasswordLength != null\n ? { maxPasswordLength: this.config.emailAndPassword.maxPasswordLength } : {}),\n ...(this.config.emailAndPassword?.resetPasswordTokenExpiresIn != null\n ? { resetPasswordTokenExpiresIn: this.config.emailAndPassword.resetPasswordTokenExpiresIn } : {}),\n ...(this.config.emailAndPassword?.autoSignIn != null\n ? { autoSignIn: this.config.emailAndPassword.autoSignIn } : {}),\n ...(this.config.emailAndPassword?.revokeSessionsOnPasswordReset != null\n ? { revokeSessionsOnPasswordReset: this.config.emailAndPassword.revokeSessionsOnPasswordReset } : {}),\n sendResetPassword: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Password-reset requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailAndPassword?.resetPasswordTokenExpiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.password_reset',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n resetUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n // Do NOT rethrow: the user account exists; an email-transport failure\n // (missing template, bad credentials, network blip) must not turn\n // the user-facing reset request into a 500. The user can retry via\n // the \"forgot password\" flow.\n console.error(`[AuthManager] sendResetPassword failed (swallowed): ${err?.message ?? err}`);\n }\n },\n };\n })(),\n\n // Email verification\n ...(this.config.emailVerification || this.config.emailService ? {\n emailVerification: {\n ...(this.config.emailVerification?.sendOnSignUp != null\n ? { sendOnSignUp: this.config.emailVerification.sendOnSignUp } : {}),\n ...(this.config.emailVerification?.sendOnSignIn != null\n ? { sendOnSignIn: this.config.emailVerification.sendOnSignIn } : {}),\n ...(this.config.emailVerification?.autoSignInAfterVerification != null\n ? { autoSignInAfterVerification: this.config.emailVerification.autoSignInAfterVerification } : {}),\n ...(this.config.emailVerification?.expiresIn != null\n ? { expiresIn: this.config.emailVerification.expiresIn } : {}),\n sendVerificationEmail: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n console.warn(\n `[AuthManager] Verification email requested for ${user.email} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n const ttlSec = this.config.emailVerification?.expiresIn ?? 60 * 60;\n try {\n await email.sendTemplate({\n template: 'auth.verify_email',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n verificationUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n } catch (err: any) {\n // Do NOT rethrow: the user account exists; an email-transport\n // failure must not turn signup or /send-verification-email into\n // a 500. The \"Resend verification email\" UI lets the user retry.\n console.error(`[AuthManager] sendVerificationEmail failed (swallowed): ${err?.message ?? err}`);\n }\n },\n },\n } : {}),\n\n // Session configuration\n session: {\n ...AUTH_SESSION_CONFIG,\n expiresIn: this.config.session?.expiresIn || 60 * 60 * 24 * 7, // 7 days default\n updateAge: this.config.session?.updateAge || 60 * 60 * 24, // 1 day default\n },\n\n // better-auth plugins — registered based on AuthPluginConfig flags\n plugins,\n\n // Database hooks (fired by better-auth's adapter writes — these run\n // for SSO JIT-provisioning too, unlike kernel-level ObjectQL\n // middleware which better-auth's adapter bypasses).\n ...(this.config.databaseHooks ? { databaseHooks: this.config.databaseHooks } : {}),\n\n // Bootstrap bypass for `disableSignUp`. The first-run owner wizard\n // (`/_account/setup`) calls `POST /auth/sign-up/email` to create\n // the very first user — if `OS_DISABLE_SIGNUP=true` is set on a\n // fresh install we'd lock the operator out of their own instance.\n // Solution: when the request hits `/sign-up/email` AND no users\n // exist yet, temporarily flip `disableSignUp` off for *this*\n // request's context. Once the owner is created the next request\n // sees `userCount > 0` and the toggle is enforced again.\n hooks: {\n before: createAuthMiddleware(async (ctx: any) => {\n if (ctx?.path !== '/sign-up/email') return;\n const ep = ctx?.context?.options?.emailAndPassword;\n if (!ep?.disableSignUp) return;\n try {\n const adapter = ctx.context.adapter;\n const existing = await adapter.findOne({ model: 'user', where: [] });\n if (!existing) {\n ctx.context.__osDisableSignUpOrig = ep.disableSignUp;\n ep.disableSignUp = false;\n }\n } catch {\n // Adapter not ready → keep disableSignUp on.\n }\n }),\n after: createAuthMiddleware(async (ctx: any) => {\n if (ctx?.path !== '/sign-up/email') return;\n const ep = ctx?.context?.options?.emailAndPassword;\n if (ep && ctx.context.__osDisableSignUpOrig !== undefined) {\n ep.disableSignUp = ctx.context.__osDisableSignUpOrig;\n delete ctx.context.__osDisableSignUpOrig;\n }\n }),\n },\n\n // Trusted origins for CSRF protection (supports wildcards like \"https://*.example.com\")\n // Auto-includes origins from OS_CORS_ORIGIN env var so CORS and CSRF stay in sync.\n ...(() => {\n const origins: string[] = [...(this.config.trustedOrigins || [])];\n // Sync with OS_CORS_ORIGIN env var (comma-separated)\n const corsOrigin = readEnvWithDeprecation('OS_CORS_ORIGIN', 'CORS_ORIGIN');\n if (corsOrigin && corsOrigin !== '*') {\n corsOrigin.split(',').map(s => s.trim()).filter(Boolean).forEach(o => {\n if (!origins.includes(o)) origins.push(o);\n });\n }\n // When CORS allows all origins (default) and no explicit trustedOrigins,\n // trust all localhost ports in development for convenience. Also trust\n // `*.localhost` subdomains so per-project tenant subdomains (the dev\n // default root domain — see project-provisioning.ts) pass CSRF checks\n // without operators having to configure trustedOrigins manually.\n if (!origins.length && (!corsOrigin || corsOrigin === '*')) {\n origins.push('http://localhost:*');\n origins.push('http://*.localhost:*');\n origins.push('https://*.localhost:*');\n }\n return origins.length ? { trustedOrigins: origins } : {};\n })(),\n\n // Advanced options (cross-subdomain cookies, secure cookies, CSRF, etc.)\n ...(this.config.advanced ? {\n advanced: {\n ...(this.config.advanced.crossSubDomainCookies\n ? { crossSubDomainCookies: this.config.advanced.crossSubDomainCookies } : {}),\n ...(this.config.advanced.useSecureCookies != null\n ? { useSecureCookies: this.config.advanced.useSecureCookies } : {}),\n ...(this.config.advanced.disableCSRFCheck != null\n ? { disableCSRFCheck: this.config.advanced.disableCSRFCheck } : {}),\n ...(this.config.advanced.cookiePrefix != null\n ? { cookiePrefix: this.config.advanced.cookiePrefix } : {}),\n },\n } : {}),\n };\n\n return betterAuth(betterAuthConfig);\n }\n\n /**\n * Detect WebContainer (StackBlitz) and swap in a pure-JS scrypt hasher.\n *\n * better-auth defaults to `@better-auth/utils/password.node`, which calls\n * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and\n * signup throws `TypeError: y.run is not a function`.\n *\n * We can't dynamic-import `@better-auth/utils/password` because that\n * package's `exports` map gates the pure-JS build behind a non-`\"node\"`\n * condition — Node-the-runtime (which WebContainer reports itself as)\n * always resolves to `password.node.mjs`. So we reimplement the same hash\n * here using `@noble/hashes/scrypt` directly, with byte-identical params\n * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage\n * format. Hashes produced by either implementation verify against the\n * other — no migration needed.\n *\n * Returns `undefined` outside WebContainer so production deployments keep\n * the native (fast) hasher and never load `@noble/hashes`.\n */\n private async resolvePasswordHasher(): Promise<\n { hash: (password: string) => Promise<string>; verify: (args: { hash: string; password: string }) => Promise<boolean> } | undefined\n > {\n if (!isWebContainerRuntime()) return undefined;\n try {\n const { scryptAsync } = await import('@noble/hashes/scrypt.js');\n const PARAMS = { N: 16384, r: 16, p: 1, dkLen: 64, maxmem: 128 * 16384 * 16 * 2 } as const;\n const toHex = (b: Uint8Array): string => {\n let s = '';\n for (let i = 0; i < b.length; i++) s += b[i]!.toString(16).padStart(2, '0');\n return s;\n };\n const generateKey = (password: string, saltHex: string): Promise<Uint8Array> =>\n scryptAsync(password.normalize('NFKC'), saltHex, PARAMS);\n return {\n hash: async (password: string) => {\n const saltBytes = (globalThis as any).crypto.getRandomValues(new Uint8Array(16));\n const saltHex = toHex(saltBytes);\n const key = await generateKey(password, saltHex);\n return `${saltHex}:${toHex(key)}`;\n },\n verify: async ({ hash, password }) => {\n const [saltHex, keyHex] = hash.split(':');\n if (!saltHex || !keyHex) throw new Error('Invalid password hash');\n const target = await generateKey(password, saltHex);\n return toHex(target) === keyHex;\n },\n };\n } catch (err: any) {\n console.warn(\n `[AuthManager] WebContainer detected but pure-JS scrypt unavailable: ${err?.message ?? err}. Falling back to default.`,\n );\n return undefined;\n }\n }\n\n /**\n * Build the list of better-auth plugins based on AuthPluginConfig flags.\n *\n * Each plugin that introduces its own database tables is configured with\n * a `schema` option containing the appropriate snake_case field mappings,\n * so that `createAdapterFactory` transforms them automatically.\n */\n private async buildPluginList(): Promise<any[]> {\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const plugins: any[] = [];\n\n // Defaults — kept in sync with `AuthPluginConfigSchema` in\n // @objectstack/spec/system/auth-config.zod.ts. The frontend AuthProvider\n // (in @object-ui/app-shell) calls `/api/v1/auth/organization/list` on\n // every load; making the org plugin opt-out (default true) avoids\n // 404s and the noisy \"Failed to load organizations\" warning.\n //\n // `OS_OIDC_PROVIDER_ENABLED` lets operators flip the OIDC IdP on\n // without re-deploying with a code change (mirrors the\n // `OS_MULTI_ORG_ENABLED` / `OS_DISABLE_SIGNUP` pattern). When set, the\n // env var WINS over the config-file setting so platform operators can\n // override per-environment without touching the application bundle.\n const oidcEnv = (globalThis as any)?.process?.env?.OS_OIDC_PROVIDER_ENABLED;\n const oidcFromEnv = oidcEnv != null ? String(oidcEnv).toLowerCase() === 'true' : undefined;\n const twoFactorFromEnv = readBooleanEnv('OS_AUTH_TWO_FACTOR');\n const enabled = {\n organization: pluginConfig.organization ?? true,\n twoFactor: twoFactorFromEnv ?? pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n oidcProvider: oidcFromEnv ?? pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? false,\n };\n\n // bearer() — ALWAYS enabled.\n //\n // Enables token-based authentication for cross-origin and mobile clients\n // where third-party cookies are blocked (e.g. Safari ITP, Chrome CHIPS,\n // native apps). The plugin:\n // • Accepts `Authorization: Bearer <token>` on incoming requests and\n // transparently resolves the session as if a cookie had been sent.\n // • Emits a `set-auth-token` response header on sign-in / session-refresh\n // that the client can store (e.g. in `localStorage`) and replay on\n // subsequent requests.\n //\n // This mirrors how Salesforce, Notion, Supabase and first-party mobile\n // SDKs handle auth. Cookie-based auth remains available for same-origin\n // browser deployments; bearer is additive, not a replacement.\n const { bearer } = await import('better-auth/plugins/bearer');\n plugins.push(bearer());\n\n if (enabled.organization) {\n const { organization } = await import('better-auth/plugins/organization');\n // Build a `roles` map that registers each app-supplied org role\n // (e.g. CRM's sales_rep, sales_manager) as a valid Better-Auth role\n // so invitations to those roles aren't rejected with ROLE_NOT_FOUND.\n // Real RBAC enforcement is handled by ObjectStack's SecurityPlugin,\n // which matches the role name against `permission` metadata\n // (PermissionSets). Here we register them with minimum org-plugin\n // capabilities (same as the built-in `member` role) so they cannot\n // inadvertently grant org-level admin powers.\n let customOrgRoles: Record<string, any> | undefined;\n const extra = this.config.additionalOrgRoles;\n if (extra && extra.length > 0) {\n try {\n const accessMod = await import('better-auth/plugins/organization/access');\n const { defaultAc, memberAc, defaultRoles: importedDefaultRoles } = accessMod as any;\n // Better-Auth's `hasPermission` does `{...options.roles || defaultRoles}`\n // (precedence: `||` then spread). When we pass our own `roles`, the\n // built-in owner/admin/member are silently dropped, so even the org\n // owner loses `invitation:create` and every mutation 403s. We must\n // re-include the defaults alongside our extras.\n const defaultRoles = importedDefaultRoles || null;\n if (defaultAc && memberAc && typeof memberAc.statements === 'object') {\n const built: Record<string, any> = defaultRoles ? { ...defaultRoles } : {};\n const stmts = memberAc.statements;\n for (const name of extra) {\n if (!name) continue;\n if (built[name]) continue;\n built[name] = defaultAc.newRole(stmts);\n }\n customOrgRoles = built;\n }\n } catch {\n customOrgRoles = undefined;\n }\n }\n plugins.push(organization({\n schema: buildOrganizationPluginSchema(),\n // Enable the team sub-feature so the framework's `sys_team` /\n // `sys_team_member` tables (already declared in platform-objects)\n // are actually wired up to better-auth's CRUD endpoints\n // (`/organization/{create,update,remove,list}-team[s]` and\n // `/organization/{add,remove,list}-team-member[s]`). The Account\n // portal exposes a Teams page; without this flag those endpoints\n // 404 and the section silently breaks.\n teams: { enabled: true },\n // Without a mailer wired in framework, requiring email verification\n // before accepting invitations dead-ends every invite flow with\n // FORBIDDEN EMAIL_VERIFICATION_REQUIRED…. Default-off here keeps\n // the built-in /accept-invitation route usable for pilots; operators\n // who wire a real mailer can re-enable downstream.\n requireEmailVerificationOnInvitation: false,\n ...(customOrgRoles ? { roles: customOrgRoles } : {}),\n // ── Slug-change guard ─────────────────────────────────────\n // An org's slug is baked into every env hostname at creation\n // time (see service-tenant `project-provisioning.ts`). Renaming\n // it while live envs exist would silently desync the URL from\n // the org identity. Block the change here; the cloud Console\n // surfaces this as an actionable error and points users to\n // `change_hostname` or archiving the env. Org `name` (display\n // label) is unaffected — only `slug` is guarded.\n //\n // We resolve the data engine lazily so non-cloud apps (which\n // never seed `sys_environment`) keep working: any lookup error\n // is treated as \"no envs to protect\".\n organizationHooks: {\n // Gate fresh organization creation behind the multi-org flag.\n // The plugin itself is always installed (so list/update/invite endpoints\n // keep responding); only the `create` operation is denied when the\n // deployment is provisioned in single-org mode. Resolution order:\n // 1. explicit `OS_MULTI_ORG_ENABLED` (wins for backwards compat),\n // 2. else `OS_MULTI_TENANT` (multi-tenant deployments are always\n // multi-org), default `'false'` → single-org / per-env runtime.\n beforeCreateOrganization: async () => {\n const env = (globalThis as any)?.process?.env ?? {};\n const explicit = env.OS_MULTI_ORG_ENABLED;\n const legacy = explicit === undefined\n ? readEnvWithDeprecation('OS_MULTI_ORG_ENABLED', 'OS_MULTI_TENANT')\n : explicit;\n const flag = String(legacy ?? 'false').toLowerCase();\n if (flag === 'false') {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n 'Creating additional organizations is disabled on this deployment.',\n });\n }\n },\n beforeUpdateOrganization: async ({ organization, member }: any) => {\n const newSlug = organization?.slug;\n const orgId = member?.organizationId;\n if (!newSlug || !orgId) return;\n\n const dataEngine = this.config.dataEngine as any;\n if (!dataEngine) return;\n\n let currentSlug: string | undefined;\n try {\n const current = await dataEngine.findOne('sys_organization', {\n where: { id: orgId },\n });\n currentSlug = current?.slug;\n } catch {\n return;\n }\n if (!currentSlug || currentSlug === newSlug) return;\n\n let activeEnvs = 0;\n try {\n const envs = await dataEngine.find('sys_environment', {\n where: { organization_id: orgId },\n });\n activeEnvs = (envs ?? []).filter(\n (e: any) => e?.status !== 'archived' && e?.status !== 'failed',\n ).length;\n } catch {\n return;\n }\n\n if (activeEnvs > 0) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n `Cannot change organization slug while ${activeEnvs} active ` +\n `environment(s) still reference it. Archive those environments ` +\n `or rename their hostnames first.`,\n });\n }\n },\n },\n // No mailer is wired in framework yet — log the accept URL so\n // operators / UI can fall back to copy-paste flows. Replace this\n // with a real mail integration when available.\n sendInvitationEmail: async ({ email: recipientEmail, invitation, organization: org, inviter }) => {\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Invitation email not configured. ` +\n `To: ${recipientEmail} (org: ${org?.name ?? invitation.organizationId}, ` +\n `role: ${invitation.role}, inviter: ${inviter?.user?.email ?? 'unknown'}) ` +\n `URL: ${acceptUrl}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.invitation',\n to: recipientEmail,\n data: {\n inviter: {\n name: inviter?.user?.name ?? inviter?.user?.email ?? 'A teammate',\n email: inviter?.user?.email ?? '',\n },\n organization: { name: org?.name ?? invitation.organizationId },\n role: invitation.role || '',\n acceptUrl,\n appName: this.getAppName(),\n },\n relatedObject: 'sys_invitation',\n relatedId: invitation.id,\n });\n } catch (err: any) {\n // Do NOT rethrow: the invitation row was already persisted by\n // better-auth; an email-transport failure must not turn the\n // invite request into a 500. The admin can resend the invite.\n console.error(`[AuthManager] sendInvitationEmail failed (swallowed): ${err?.message ?? err}`);\n }\n },\n }));\n }\n\n if (enabled.twoFactor) {\n const { twoFactor } = await import('better-auth/plugins/two-factor');\n plugins.push(twoFactor({\n schema: buildTwoFactorPluginSchema(),\n }));\n }\n\n if (enabled.admin) {\n const { admin } = await import('better-auth/plugins/admin');\n // Platform admin: ban/unban, set-password, impersonate, set-role.\n // Schema mapping ensures the plugin's added user/session columns\n // match ObjectStack's snake_case conventions (ban_reason,\n // ban_expires, impersonated_by). `role` and `banned` are already\n // snake_case-compatible.\n plugins.push(admin({\n schema: buildAdminPluginSchema(),\n }));\n }\n\n if (enabled.magicLink) {\n const { magicLink } = await import('better-auth/plugins/magic-link');\n // magic-link reuses the `verification` table — no extra schema mapping needed.\n plugins.push(magicLink({\n sendMagicLink: async ({ email: recipientEmail, url, token }) => {\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Magic-link requested for ${recipientEmail} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.magic_link',\n to: recipientEmail,\n data: {\n magicLinkUrl: url,\n token,\n expiresInMinutes: 10,\n appName: this.getAppName(),\n },\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendMagicLink failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n // OIDC / Generic OAuth2 providers (enterprise SSO via genericOAuth plugin)\n if (this.config.oidcProviders?.length) {\n const { genericOAuth } = await import('better-auth/plugins/generic-oauth');\n plugins.push(genericOAuth({\n config: this.config.oidcProviders.map(p => ({\n providerId: p.providerId,\n ...(p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {}),\n ...(p.issuer ? { issuer: p.issuer } : {}),\n ...(p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {}),\n ...(p.tokenUrl ? { tokenUrl: p.tokenUrl } : {}),\n ...(p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {}),\n clientId: p.clientId,\n clientSecret: p.clientSecret,\n ...(p.scopes ? { scopes: p.scopes } : {}),\n ...(p.pkce != null ? { pkce: p.pkce } : {}),\n })),\n }));\n }\n\n // OAuth/OIDC Provider — turn this server into an OpenID Connect Identity\n // Provider so external apps can SSO via ObjectStack. Adds the\n // `/oauth2/{authorize,token,userinfo,register,consent,endsession}` and\n // `/.well-known/openid-configuration` endpoints under the auth route.\n //\n // Migrated from the deprecated `better-auth/plugins/oidc-provider` to the\n // standalone `@better-auth/oauth-provider` package. The new plugin uses\n // `oauthClient`, `oauthAccessToken`, `oauthRefreshToken`, and `oauthConsent`\n // models — see `buildOauthProviderPluginSchema()` for the snake_case\n // mappings to ObjectStack's `sys_oauth_*` tables.\n if (enabled.oidcProvider) {\n // The new @better-auth/oauth-provider package requires the `jwt`\n // plugin (used to sign id_tokens / JWT access tokens). Register it\n // automatically — it is otherwise an internal implementation detail\n // and forcing every consumer to opt in would be poor DX.\n const { jwt } = await import('better-auth/plugins');\n plugins.push(jwt({ schema: buildJwtPluginSchema() }));\n\n const { oauthProvider } = await import('@better-auth/oauth-provider');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const uiBase = (this.config.uiBasePath ?? '/_console').replace(/\\/$/, '');\n plugins.push(oauthProvider({\n // Console SPA renders both pages (replaces the legacy Account SPA at\n // /_account). Override `uiBasePath` in AuthConfig if Console is\n // mounted elsewhere.\n loginPage: `${baseUrl}${uiBase}/login`,\n consentPage: `${baseUrl}${uiBase}/oauth/consent`,\n schema: buildOauthProviderPluginSchema(),\n }));\n }\n\n // Device Authorization Grant (RFC 8628) — for CLI / TV-style devices.\n // Exposes the standard `/device/{code,token,approve,deny}` endpoints\n // and persists pending requests in `sys_device_code`.\n //\n // The verification URI points at the account portal page that lets a\n // signed-in user approve or deny a pending CLI login. The page reads\n // the `user_code` query parameter that better-auth appends to\n // `verification_uri_complete`.\n if (enabled.deviceAuthorization) {\n const { deviceAuthorization } = await import('better-auth/plugins/device-authorization');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const uiBase = (this.config.uiBasePath ?? '/_console').replace(/\\/$/, '');\n plugins.push(deviceAuthorization({\n verificationUri: `${baseUrl}${uiBase}/auth/device`,\n schema: buildDeviceAuthorizationPluginSchema(),\n }));\n }\n\n // customSession() — augments the session payload with a derived `role`\n // field so frontend gating (e.g. AppShell's `isAdmin = user.role === 'admin'`)\n // works without each consumer having to re-query permission sets.\n //\n // It also returns a `roles: string[]` array: the stored `user.role`\n // string split on commas (the admin plugin stores multi-role users as\n // e.g. `\"admin,manager\"`), with `'admin'` appended (deduplicated) when\n // the user is promoted below. Consumers that match on individual role\n // names (e.g. the Console approvals inbox resolving `role:<name>`\n // approvers) must read `roles` — `user.role` is *replaced* by the\n // literal `'admin'` on promotion, so business roles such as `manager`\n // only survive in the array.\n //\n // Better-auth's `sys_user` table doesn't carry a `role` column. We derive\n // it from two sources:\n //\n // 1. **Platform admin** — a `sys_user_permission_set` row that points at\n // the `admin_full_access` permission set with `organization_id = null`\n // (seeded by `bootstrapPlatformAdmin`).\n // 2. **Organization admin** — a `sys_member` row in the user's *active*\n // organization (`session.activeOrganizationId`) with role `owner` or\n // `admin`. Org owners/admins are entitled to manage org-scoped\n // metadata such as saved list views, dashboards, etc.\n //\n // Either path synthesizes `user.role = 'admin'` so the frontend can gate\n // metadata-edit affordances uniformly. The raw membership role remains\n // available via the `organization` plugin's `member` payload for\n // finer-grained checks.\n const dataEngine = this.config.dataEngine;\n if (dataEngine) {\n const { customSession } = await import('better-auth/plugins/custom-session');\n plugins.push(customSession(async ({ user, session }) => {\n if (!user?.id) return { user, session };\n\n const isPlatformAdmin = async (): Promise<boolean> => {\n try {\n const links = await dataEngine.find('sys_user_permission_set', {\n where: { user_id: user.id },\n limit: 50,\n });\n const platformLinks = (Array.isArray(links) ? links : []).filter(\n (l: any) => !l.organization_id,\n );\n if (platformLinks.length === 0) return false;\n const sets = await dataEngine.find('sys_permission_set', { limit: 50 });\n const adminSet = (Array.isArray(sets) ? sets : []).find(\n (r: any) => r.name === 'admin_full_access',\n );\n if (!adminSet) return false;\n return platformLinks.some(\n (l: any) => l.permission_set_id === adminSet.id,\n );\n } catch {\n return false;\n }\n };\n\n const isActiveOrgAdmin = async (): Promise<boolean> => {\n try {\n const orgId = (session as any)?.activeOrganizationId;\n if (!orgId) return false;\n const members = await dataEngine.find('sys_member', {\n where: { user_id: user.id, organization_id: orgId },\n limit: 5,\n });\n return (Array.isArray(members) ? members : []).some((m: any) => {\n // better-auth org plugin stores roles as either a single string\n // or a comma-separated list (e.g. `\"owner,admin\"`). Treat any\n // membership that includes `owner` or `admin` as administrative.\n const raw = typeof m?.role === 'string' ? m.role : '';\n const roles = raw.split(',').map((s: string) => s.trim().toLowerCase());\n return roles.includes('owner') || roles.includes('admin');\n });\n } catch {\n return false;\n }\n };\n\n const platformAdmin = await isPlatformAdmin();\n const promote = platformAdmin || (await isActiveOrgAdmin());\n const storedRole = typeof (user as any).role === 'string' ? (user as any).role : '';\n const roles = storedRole\n .split(',')\n .map((s: string) => s.trim())\n .filter(Boolean);\n if (promote && !roles.includes('admin')) roles.push('admin');\n if (!promote) return { user: { ...user, roles, isPlatformAdmin: platformAdmin }, session };\n return { user: { ...user, role: 'admin', roles, isPlatformAdmin: platformAdmin }, session };\n }));\n }\n\n return plugins;\n }\n\n /**\n * Create database configuration using ObjectQL adapter\n *\n * better-auth resolves the `database` option as follows:\n * - `undefined` → in-memory adapter\n * - `typeof fn === \"function\"` → treated as `DBAdapterInstance`, called with `(options)`\n * - otherwise → forwarded to Kysely adapter factory (pool/dialect)\n *\n * A raw `CustomAdapter` object would fall into the third branch and fail\n * silently. We therefore wrap the ObjectQL adapter in a factory function\n * so it is correctly recognised as a `DBAdapterInstance`.\n */\n private createDatabaseConfig(): any {\n // Use ObjectQL adapter factory if dataEngine is provided\n if (this.config.dataEngine) {\n // createObjectQLAdapterFactory returns an AdapterFactory\n // (options => DBAdapter) which better-auth invokes via getBaseAdapter().\n // The factory is created by better-auth's createAdapterFactory and\n // automatically applies modelName/fields transformations declared in\n // the betterAuth config above.\n return createObjectQLAdapterFactory(this.config.dataEngine);\n }\n\n // Fallback warning if no dataEngine is provided\n console.warn(\n '⚠️ WARNING: No dataEngine provided to AuthManager! ' +\n 'Using in-memory storage. This is NOT suitable for production. ' +\n 'Please provide a dataEngine instance (e.g., ObjectQL) in AuthManagerOptions.'\n );\n\n // Return a minimal in-memory configuration as fallback\n // This allows the system to work in development/testing without a real database\n return undefined; // better-auth will use its default in-memory adapter\n }\n\n /**\n * Generate a secure secret if not provided\n */\n private generateSecret(): string {\n const envSecret = readEnvWithDeprecation('OS_AUTH_SECRET', ['AUTH_SECRET', 'BETTER_AUTH_SECRET']);\n if (envSecret) return envSecret;\n\n // No secret configured. In production this is FATAL: a predictable\n // `dev-secret-<timestamp>` makes session tokens forgeable (session\n // forgery). Refuse to boot rather than run insecurely.\n if (process.env.NODE_ENV === 'production') {\n throw new Error(\n '[auth] OS_AUTH_SECRET is required in production but is not set. ' +\n 'Refusing to boot with a temporary development secret — session tokens ' +\n 'would be forgeable. Set OS_AUTH_SECRET to a strong random value.'\n );\n }\n\n // Development / test only: fall back to an ephemeral secret, loudly.\n const fallbackSecret = 'dev-secret-' + Date.now();\n console.warn(\n '⚠️ WARNING: No OS_AUTH_SECRET environment variable set! ' +\n 'Using a temporary development secret. ' +\n 'This is NOT secure for production use. ' +\n 'Please set OS_AUTH_SECRET in your environment variables.'\n );\n return fallbackSecret;\n }\n\n /**\n * Update the base URL at runtime.\n *\n * This **must** be called before the first request triggers lazy\n * initialisation of the better-auth instance — typically from a\n * `kernel:ready` hook where the actual server port is known.\n *\n * If the auth instance has already been created this is a no-op and\n * a warning is emitted.\n */\n setRuntimeBaseUrl(url: string): void {\n if (this.auth) {\n console.warn(\n '[AuthManager] setRuntimeBaseUrl() called after the auth instance was already created — ignoring. ' +\n 'Ensure this method is called before the first request.',\n );\n return;\n }\n this.config = { ...this.config, baseUrl: url };\n }\n\n /**\n * Merge runtime configuration into the manager.\n *\n * Settings-backed auth policy can change after the manager is constructed.\n * better-auth itself is created lazily, so changing config before the first\n * request is enough. If an instance already exists, reset it so the next\n * request rebuilds with the new policy.\n */\n applyConfigPatch(patch: Partial<AuthManagerOptions>): void {\n const next: AuthManagerOptions = {\n ...this.config,\n ...patch,\n ...(patch.emailAndPassword\n ? {\n emailAndPassword: {\n ...(this.config.emailAndPassword ?? {}),\n ...patch.emailAndPassword,\n },\n }\n : {}),\n ...(patch.plugins\n ? {\n plugins: {\n ...(this.config.plugins ?? {}),\n ...patch.plugins,\n },\n }\n : {}),\n };\n\n if ('socialProviders' in patch) {\n next.socialProviders = patch.socialProviders;\n }\n\n this.config = next;\n if (this.auth && !patch.authInstance) {\n this.auth = null;\n }\n }\n\n /**\n * Inject (or replace) the outbound email service used by better-auth\n * callbacks. Safe to call after construction but BEFORE the first\n * request hits the auth handler — callbacks read this via\n * {@link getEmailService} when invoked.\n *\n * AuthPlugin calls this on `kernel:ready` once `ctx.getService('email')`\n * resolves. For tests / serverless, callers may invoke directly.\n */\n setEmailService(email: IEmailService | undefined): void {\n this.config.emailService = email;\n }\n\n /** @internal Used by callback closures. */\n private getEmailService(): IEmailService | undefined {\n return this.config.emailService;\n }\n\n /**\n * Override the brand name surfaced in built-in auth emails (`{{appName}}`),\n * sourced from the live `branding.workspace_name` setting.\n *\n * AuthPlugin calls this on `kernel:ready` (and again whenever the setting\n * changes) once the `settings` service resolves. Passing `undefined` clears\n * the override so resolution falls back to the configured `appName`. The\n * value only reflects an *explicitly set* setting — when the operator has\n * not customised it, AuthPlugin passes `undefined` so a deployment's\n * configured `appName` (e.g. `OS_APP_NAME`) keeps precedence.\n */\n setAppName(name: string | undefined): void {\n this.appNameOverride = name?.trim() || undefined;\n }\n private appNameOverride?: string;\n\n /** @internal `{{appName}}` placeholder value for built-in templates. */\n private getAppName(): string {\n return this.appNameOverride ?? this.config.appName ?? 'ObjectStack';\n }\n\n /**\n * Get the underlying better-auth instance\n * Useful for advanced use cases\n */\n getAuthInstance(): Promise<Auth<any>> {\n return this.getOrCreateAuth();\n }\n\n /**\n * Handle an authentication request\n * Forwards the request directly to better-auth's universal handler\n *\n * better-auth catches internal errors (database / adapter / ORM) and\n * returns a 500 Response instead of throwing. We therefore inspect the\n * response status and log server errors so they are not silently swallowed.\n *\n * @param request - Web standard Request object\n * @returns Web standard Response object\n */\n async handleRequest(request: Request): Promise<Response> {\n // Dev DX: better-auth's CSRF protection rejects state-changing requests\n // (e.g. `/sign-in/email`) with a 403 when neither `Origin` nor `Referer`\n // is present. Browsers always send one; non-browser API clients (curl,\n // fetch from a script, integration tests) often don't, so they hit an\n // opaque 403 in local dev. A request with *no* Origin header is never a\n // browser-driven cross-site attack — CSRF is fundamentally cross-origin —\n // so in non-production we synthesize a same-origin `Origin` from the\n // request URL. It matches the dev localhost trusted-origins set, so the\n // CSRF check passes without weakening protection in production (gated on\n // NODE_ENV, the same dev signal used for the fallback auth secret above).\n if (\n process.env.NODE_ENV !== 'production' &&\n !request.headers.get('origin') &&\n !request.headers.get('referer')\n ) {\n try {\n const headers = new Headers(request.headers);\n headers.set('origin', new URL(request.url).origin);\n request = new Request(request, { headers });\n } catch {\n /* malformed URL — leave the request untouched */\n }\n }\n\n const auth = await this.getOrCreateAuth();\n // better-auth's HTTP entrypoint (`createBetterAuth.handler`) wraps execution\n // in `runWithAdapter` but NOT `runWithRequestState`. Endpoints that read\n // request-state via `defineRequestState()` (e.g. `should-session-refresh`,\n // `oauth`) therefore throw \"No request state found\" when reached via HTTP.\n // The `customSession` plugin triggers this by invoking the inner\n // `getSession()` endpoint directly, bypassing `to-auth-endpoints`'\n // auto-wrap. We establish the ALS store here so all downstream endpoint\n // calls inherit a valid request-state WeakMap.\n const { runWithRequestState } = await import('@better-auth/core/context');\n const response = await runWithRequestState(new WeakMap(), () => auth.handler(request));\n\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n console.error('[AuthManager] better-auth returned error:', response.status, body);\n } catch {\n console.error('[AuthManager] better-auth returned error:', response.status, '(unable to read body)');\n }\n }\n\n return response;\n }\n\n /**\n * Get the better-auth API for programmatic access\n * Use this for server-side operations (e.g., creating users, checking sessions)\n */\n async getApi(): Promise<Auth<any>['api']> {\n const auth = await this.getOrCreateAuth();\n return auth.api;\n }\n\n /**\n * Get the underlying better-auth context for low-level operations such as\n * `internalAdapter.createAccount` / `password.hash`.\n *\n * Used by routes that need to write to better-auth's tables outside the\n * normal endpoint surface — currently only `set-initial-password`, which\n * provisions a credential account for SSO-onboarded users so they can\n * sign in with email/password going forward.\n */\n async getAuthContext(): Promise<any> {\n const auth = await this.getOrCreateAuth();\n return (auth as any).$context;\n }\n\n // ---------------------------------------------------------------------------\n // Device Flow (CLI browser-based login)\n //\n // The device authorization flow (RFC 8628) is now handled entirely by\n // better-auth's `device-authorization` plugin. Endpoints are exposed at\n // `${basePath}/device/{code,token,approve,deny}` and persisted in\n // `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in\n // AuthPluginConfig.\n // ---------------------------------------------------------------------------\n\n getPublicConfig() {\n // Extract social providers info (without sensitive data)\n const socialProviders = [];\n if (this.config.socialProviders) {\n for (const [id, providerConfig] of Object.entries(this.config.socialProviders)) {\n if (providerConfig.enabled !== false) {\n // Map provider ID to friendly name\n const nameMap: Record<string, string> = {\n google: 'Google',\n github: 'GitHub',\n microsoft: 'Microsoft',\n apple: 'Apple',\n facebook: 'Facebook',\n twitter: 'Twitter',\n discord: 'Discord',\n gitlab: 'GitLab',\n linkedin: 'LinkedIn',\n };\n\n socialProviders.push({\n id,\n name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),\n enabled: true,\n type: 'social' as const,\n });\n }\n }\n }\n\n // Append OIDC providers\n if (this.config.oidcProviders?.length) {\n for (const p of this.config.oidcProviders) {\n socialProviders.push({\n id: p.providerId,\n name: p.name ?? (p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1)),\n enabled: true,\n type: 'oidc' as const,\n });\n }\n }\n\n // Extract email/password config (safe fields only). Deployment env can\n // lock registration policy; otherwise we fall back to configured/settings\n // `emailAndPassword.disableSignUp` (default `false`).\n const emailPasswordConfig: Partial<EmailAndPasswordConfig> = this.config.emailAndPassword ?? {};\n const disableSignUpFromEnv = readDisableSignUpEnv();\n const emailPassword = {\n enabled: emailPasswordConfig.enabled !== false, // Default to true\n disableSignUp: disableSignUpFromEnv ?? emailPasswordConfig.disableSignUp ?? false,\n requireEmailVerification: emailPasswordConfig.requireEmailVerification ?? false,\n };\n\n // Extract enabled features\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n // Multi-org capability (UI org-switcher, \"create org\" action, etc.).\n // Resolution order: explicit `OS_MULTI_ORG_ENABLED` wins, else fall\n // back to legacy `OS_MULTI_TENANT` (multi-tenant deployments are always\n // multi-org); default `'false'` → single-org / per-env runtime.\n const multiOrgEnv = (globalThis as any)?.process?.env ?? {};\n const multiOrgRaw = multiOrgEnv.OS_MULTI_ORG_ENABLED !== undefined\n ? multiOrgEnv.OS_MULTI_ORG_ENABLED\n : (readEnvWithDeprecation('OS_MULTI_ORG_ENABLED', 'OS_MULTI_TENANT') ?? 'false');\n const multiOrgEnabled = String(multiOrgRaw).toLowerCase() !== 'false';\n\n // Legal links shown beneath the login / register cards. Defaults to\n // the public ObjectStack pages so vanilla deployments don't link to\n // dead `#` anchors; operators who deploy ObjectStack on their own\n // domain typically override these with their own legal docs via the\n // `OS_TERMS_URL` / `OS_PRIVACY_URL` env vars. Set the env var to the\n // empty string to suppress the link entirely.\n const DEFAULT_TERMS_URL = 'https://objectstack.ai/terms';\n const DEFAULT_PRIVACY_URL = 'https://objectstack.ai/privacy';\n const rawTermsUrl = (globalThis as any)?.process?.env?.OS_TERMS_URL;\n const rawPrivacyUrl = (globalThis as any)?.process?.env?.OS_PRIVACY_URL;\n const resolveLegalUrl = (raw: unknown, fallback: string): string | undefined => {\n if (typeof raw !== 'string') return fallback;\n const trimmed = raw.trim();\n // Explicit empty string (`OS_TERMS_URL=`) opts out of the link.\n if (trimmed === '') return undefined;\n return trimmed;\n };\n const termsUrl = resolveLegalUrl(rawTermsUrl, DEFAULT_TERMS_URL);\n const privacyUrl = resolveLegalUrl(rawPrivacyUrl, DEFAULT_PRIVACY_URL);\n\n // OIDC Provider — same env-var override as in `buildPlugins()`. The\n // /auth/config response MUST match what's actually wired, otherwise the\n // frontend will render UI for endpoints that 404.\n const oidcEnv = (globalThis as any)?.process?.env?.OS_OIDC_PROVIDER_ENABLED;\n const oidcFromEnv = oidcEnv != null ? String(oidcEnv).toLowerCase() === 'true' : undefined;\n const twoFactorFromEnv = readBooleanEnv('OS_AUTH_TWO_FACTOR');\n\n const features = {\n twoFactor: twoFactorFromEnv ?? pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n organization: pluginConfig.organization ?? true,\n multiOrgEnabled,\n oidcProvider: oidcFromEnv ?? pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? false,\n ...(termsUrl ? { termsUrl } : {}),\n ...(privacyUrl ? { privacyUrl } : {}),\n };\n\n return {\n emailPassword,\n socialProviders,\n features,\n };\n }\n\n /**\n * Returns the data engine wired into this auth manager. Used by route\n * handlers (e.g. bootstrap-status) that need to query identity tables\n * directly without going through better-auth.\n */\n public getDataEngine(): IDataEngine | undefined {\n return this.config.dataEngine;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IDataEngine } from '@objectstack/core';\nimport { createAdapterFactory } from 'better-auth/adapters';\nimport type { CleanedWhere } from 'better-auth/adapters';\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * Mapping from better-auth model names to ObjectStack protocol object names.\n *\n * better-auth uses hardcoded model names ('user', 'session', 'account', 'verification')\n * while ObjectStack's protocol layer uses `sys_` prefixed names. This map bridges the two.\n */\nexport const AUTH_MODEL_TO_PROTOCOL: Record<string, string> = {\n user: SystemObjectName.USER,\n session: SystemObjectName.SESSION,\n account: SystemObjectName.ACCOUNT,\n verification: SystemObjectName.VERIFICATION,\n};\n\n/**\n * Resolve a better-auth model name to the ObjectStack protocol object name.\n * Falls back to the original model name for custom / non-core models.\n */\nexport function resolveProtocolName(model: string): string {\n return AUTH_MODEL_TO_PROTOCOL[model] ?? model;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth datetime columns (snake_case) per model.\n *\n * When the underlying driver stored these as JavaScript `Date` objects\n * (legacy behaviour), the libsql HTTP transport coerces the value to a REAL\n * column and round-trips it as a string like `\"1779497911249.0\"`. That\n * string is not a valid Date string (it has a trailing `.0`), so\n * `new Date(...)` produces `Invalid Date` and better-auth's client treats\n * the session as expired — causing a login/redirect loop.\n *\n * We normalise these legacy values back to ISO strings on **read** so the\n * factory's `supportsDates: false` parser can turn them into real Date\n * objects. New writes always go through better-auth's own\n * `Date → ISO string` conversion (because we declare `supportsDates: false`\n * below), so no further `.0`-suffixed values will ever be created.\n */\nconst LEGACY_DATETIME_FIELDS_BY_MODEL: Record<string, string[]> = {\n user: ['created_at', 'updated_at'],\n session: ['expires_at', 'created_at', 'updated_at'],\n account: [\n 'access_token_expires_at',\n 'refresh_token_expires_at',\n 'created_at',\n 'updated_at',\n ],\n verification: ['expires_at', 'created_at', 'updated_at'],\n};\n\nconst NUMERIC_STRING_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * If `value` looks like a stringified epoch-ms (optionally with `.0`),\n * convert it to an ISO 8601 string. Otherwise return it unchanged.\n */\nfunction normaliseLegacyDate(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (!NUMERIC_STRING_RE.test(value)) return value;\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return value;\n // Heuristic: epoch milliseconds are at least 10 digits (year 2001+).\n if (Math.abs(n) < 1e10) return value;\n const d = new Date(n);\n if (Number.isNaN(d.getTime())) return value;\n return d.toISOString();\n}\n\n/**\n * Walk a record and rewrite any legacy `.0`-suffixed datetime values\n * into ISO strings. Mutates and returns the record.\n */\nfunction normaliseLegacyDates<T extends Record<string, any> | null | undefined>(\n model: string,\n record: T,\n): T {\n if (!record) return record;\n const cols = LEGACY_DATETIME_FIELDS_BY_MODEL[model];\n if (!cols) return record;\n for (const col of cols) {\n if (col in record) {\n (record as Record<string, unknown>)[col] = normaliseLegacyDate(\n (record as Record<string, unknown>)[col],\n );\n }\n }\n return record;\n}\n\n/**\n * Convert better-auth where clause to ObjectQL query format.\n *\n * Field names in the incoming {@link CleanedWhere} are expected to already be\n * in snake_case (transformed by `createAdapterFactory`).\n */\nfunction convertWhere(where: CleanedWhere[]): Record<string, any> {\n const filter: Record<string, any> = {};\n\n for (const condition of where) {\n const fieldName = condition.field;\n\n if (condition.operator === 'eq') {\n filter[fieldName] = condition.value;\n } else if (condition.operator === 'ne') {\n filter[fieldName] = { $ne: condition.value };\n } else if (condition.operator === 'in') {\n filter[fieldName] = { $in: condition.value };\n } else if (condition.operator === 'gt') {\n filter[fieldName] = { $gt: condition.value };\n } else if (condition.operator === 'gte') {\n filter[fieldName] = { $gte: condition.value };\n } else if (condition.operator === 'lt') {\n filter[fieldName] = { $lt: condition.value };\n } else if (condition.operator === 'lte') {\n filter[fieldName] = { $lte: condition.value };\n } else if (condition.operator === 'contains') {\n filter[fieldName] = { $regex: condition.value };\n }\n }\n\n return filter;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an ObjectQL adapter **factory** for better-auth.\n *\n * Uses better-auth's official `createAdapterFactory` so that model-name and\n * field-name transformations (declared via `modelName` / `fields` in the\n * betterAuth config) are applied **automatically** before any data reaches\n * ObjectQL. This eliminates the need for manual camelCase ↔ snake_case\n * conversion inside the adapter.\n *\n * The returned value is an `AdapterFactory` – a function of type\n * `(options: BetterAuthOptions) => DBAdapter` – which is the shape expected\n * by `betterAuth({ database: … })`.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth AdapterFactory\n */\nexport function createObjectQLAdapterFactory(dataEngine: IDataEngine) {\n return createAdapterFactory({\n config: {\n adapterId: 'objectql',\n // We let better-auth handle Date↔string and boolean↔0/1 conversion so\n // that values land in the underlying SQL driver as primitive strings\n // and integers. Some drivers (e.g. libsql over the HTTP transport)\n // otherwise mangle `Date` objects into `\"<epoch>.0\"` strings that\n // break the client-side session parser.\n supportsBooleans: false,\n supportsDates: false,\n supportsJSON: true,\n },\n adapter: () => ({\n create: async <T extends Record<string, any>>(\n { model, data, select: _select }: { model: string; data: T; select?: string[] },\n ): Promise<T> => {\n const result = await dataEngine.insert(model, data);\n return normaliseLegacyDates(model, result) as T;\n },\n\n findOne: async <T>(\n { model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n const result = await dataEngine.findOne(model, { where: filter, fields: select });\n\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n findMany: async <T>(\n { model, where, limit, offset, sortBy, join: _join }: {\n model: string; where?: CleanedWhere[]; limit: number;\n offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any;\n },\n ): Promise<T[]> => {\n const filter = where ? convertWhere(where) : {};\n\n const orderBy = sortBy\n ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }]\n : undefined;\n\n const results = await dataEngine.find(model, {\n where: filter,\n limit: limit || 100,\n offset,\n orderBy,\n });\n\n return results.map((r) => normaliseLegacyDates(model, r as Record<string, any>)) as T[];\n },\n\n count: async (\n { model, where }: { model: string; where?: CleanedWhere[] },\n ): Promise<number> => {\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(model, { where: filter });\n },\n\n update: async <T>(\n { model, where, update }: { model: string; where: CleanedWhere[]; update: T },\n ): Promise<T | null> => {\n const filter = convertWhere(where);\n\n // ObjectQL requires an ID for updates – find the record first\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return null;\n\n const result = await dataEngine.update(model, { ...(update as any), id: record.id });\n return result ? (normaliseLegacyDates(model, result) as T) : null;\n },\n\n updateMany: async (\n { model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n // Sequential updates: ObjectQL requires an ID per update\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.update(model, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<void> => {\n const filter = convertWhere(where);\n\n const record = await dataEngine.findOne(model, { where: filter });\n if (!record) return;\n\n await dataEngine.delete(model, { where: { id: record.id } });\n },\n\n deleteMany: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<number> => {\n const filter = convertWhere(where);\n\n const records = await dataEngine.find(model, { where: filter });\n for (const record of records) {\n await dataEngine.delete(model, { where: { id: record.id } });\n }\n return records.length;\n },\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Legacy adapter (kept for backward compatibility)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a raw ObjectQL adapter for better-auth (without factory wrapping).\n *\n * > **Prefer {@link createObjectQLAdapterFactory}** for production use.\n * > The factory version leverages `createAdapterFactory` and automatically\n * > handles model-name + field-name transformations declared in the\n * > better-auth config.\n *\n * This function is retained for direct / low-level usage where callers\n * manage field-name conversion themselves.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth CustomAdapter (raw, without factory wrapping)\n */\nexport function createObjectQLAdapter(dataEngine: IDataEngine) {\n return {\n create: async <T extends Record<string, any>>({ model, data, select: _select }: { model: string; data: T; select?: string[] }): Promise<T> => {\n const objectName = resolveProtocolName(model);\n const result = await dataEngine.insert(objectName, data);\n return result as T;\n },\n\n findOne: async <T>({ model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const result = await dataEngine.findOne(objectName, { where: filter, fields: select });\n return result ? result as T : null;\n },\n\n findMany: async <T>({ model, where, limit, offset, sortBy, join: _join }: { model: string; where?: CleanedWhere[]; limit: number; offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any }): Promise<T[]> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n const orderBy = sortBy ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }] : undefined;\n const results = await dataEngine.find(objectName, { where: filter, limit: limit || 100, offset, orderBy });\n return results as T[];\n },\n\n count: async ({ model, where }: { model: string; where?: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(objectName, { where: filter });\n },\n\n update: async <T>({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return null;\n const result = await dataEngine.update(objectName, { ...update, id: record.id });\n return result ? result as T : null;\n },\n\n updateMany: async ({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.update(objectName, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<void> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return;\n await dataEngine.delete(objectName, { where: { id: record.id } });\n },\n\n deleteMany: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.delete(objectName, { where: { id: record.id } });\n }\n return records.length;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * better-auth ↔ ObjectStack Schema Mapping\n *\n * better-auth uses camelCase field names internally (e.g. `emailVerified`, `userId`)\n * while ObjectStack's protocol layer uses snake_case (e.g. `email_verified`, `user_id`).\n *\n * These constants declare the `modelName` and `fields` mappings for each core auth\n * model, following better-auth's official schema customisation API\n * ({@link https://www.better-auth.com/docs/concepts/database}).\n *\n * The mappings serve two purposes:\n * 1. `modelName` — maps the default model name to the ObjectStack protocol name\n * (e.g. `user` → `sys_user`).\n * 2. `fields` — maps camelCase field names to their snake_case database column\n * equivalents. Only fields whose names differ need to be listed; fields that\n * are already identical (e.g. `email`, `name`, `token`) are omitted.\n *\n * These mappings are consumed by:\n * - The `betterAuth()` configuration in {@link AuthManager} so that\n * `getAuthTables()` builds the correct schema.\n * - The ObjectQL adapter factory (via `createAdapterFactory`) which uses the\n * schema to transform data and where-clauses automatically.\n */\n\n// ---------------------------------------------------------------------------\n// User model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `user` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | emailVerified | email_verified |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_USER_CONFIG = {\n modelName: SystemObjectName.USER, // 'sys_user'\n fields: {\n emailVerified: 'email_verified',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Session model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `session` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | ipAddress | ip_address |\n * | userAgent | user_agent |\n */\nexport const AUTH_SESSION_CONFIG = {\n modelName: SystemObjectName.SESSION, // 'sys_session'\n fields: {\n userId: 'user_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n ipAddress: 'ip_address',\n userAgent: 'user_agent',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Account model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `account` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:--------------------------|:-------------------------------|\n * | userId | user_id |\n * | providerId | provider_id |\n * | accountId | account_id |\n * | accessToken | access_token |\n * | refreshToken | refresh_token |\n * | idToken | id_token |\n * | accessTokenExpiresAt | access_token_expires_at |\n * | refreshTokenExpiresAt | refresh_token_expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ACCOUNT_CONFIG = {\n modelName: SystemObjectName.ACCOUNT, // 'sys_account'\n fields: {\n userId: 'user_id',\n providerId: 'provider_id',\n accountId: 'account_id',\n accessToken: 'access_token',\n refreshToken: 'refresh_token',\n idToken: 'id_token',\n accessTokenExpiresAt: 'access_token_expires_at',\n refreshTokenExpiresAt: 'refresh_token_expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Verification model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `verification` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_VERIFICATION_CONFIG = {\n modelName: SystemObjectName.VERIFICATION, // 'sys_verification'\n fields: {\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ===========================================================================\n// Plugin Table Mappings\n// ===========================================================================\n//\n// better-auth plugins (organization, two-factor, etc.) introduce additional\n// tables with their own camelCase field names. The mappings below are passed\n// to the plugin's `schema` option so that `createAdapterFactory` transforms\n// them to snake_case automatically, just like the core models above.\n// ===========================================================================\n\n// ---------------------------------------------------------------------------\n// Organization plugin – organization table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `organization` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ORGANIZATION_SCHEMA = {\n modelName: SystemObjectName.ORGANIZATION, // 'sys_organization'\n fields: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – member table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `member` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_MEMBER_SCHEMA = {\n modelName: SystemObjectName.MEMBER, // 'sys_member'\n fields: {\n organizationId: 'organization_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – invitation table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `invitation` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | inviterId | inviter_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | teamId | team_id |\n */\nexport const AUTH_INVITATION_SCHEMA = {\n modelName: SystemObjectName.INVITATION, // 'sys_invitation'\n fields: {\n organizationId: 'organization_id',\n inviterId: 'inviter_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n teamId: 'team_id',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – session additional fields\n// ---------------------------------------------------------------------------\n\n/**\n * Organization plugin adds `activeOrganizationId` (and optionally\n * `activeTeamId`) to the session model. These field mappings are\n * injected via the organization plugin's `schema.session.fields`.\n */\nexport const AUTH_ORG_SESSION_FIELDS = {\n activeOrganizationId: 'active_organization_id',\n activeTeamId: 'active_team_id',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – team table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `team` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_TEAM_SCHEMA = {\n modelName: SystemObjectName.TEAM, // 'sys_team'\n fields: {\n organizationId: 'organization_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – teamMember table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `teamMember` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | teamId | team_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_TEAM_MEMBER_SCHEMA = {\n modelName: SystemObjectName.TEAM_MEMBER, // 'sys_team_member'\n fields: {\n teamId: 'team_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Two-Factor plugin – twoFactor table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Two-Factor plugin `twoFactor` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | backupCodes | backup_codes |\n * | userId | user_id |\n */\nexport const AUTH_TWO_FACTOR_SCHEMA = {\n modelName: SystemObjectName.TWO_FACTOR, // 'sys_two_factor'\n fields: {\n backupCodes: 'backup_codes',\n userId: 'user_id',\n },\n} as const;\n\n/**\n * Two-Factor plugin adds a `twoFactorEnabled` field to the user model.\n */\nexport const AUTH_TWO_FACTOR_USER_FIELDS = {\n twoFactorEnabled: 'two_factor_enabled',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Admin plugin – user/session field additions\n// ---------------------------------------------------------------------------\n\n/**\n * Admin plugin adds platform-level admin fields to the `user` model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | banReason | ban_reason |\n * | banExpires | ban_expires |\n *\n * `role` and `banned` already have matching snake_case names and are\n * therefore omitted from this mapping (better-auth's database hooks\n * read them by the auto-derived column names).\n */\nexport const AUTH_ADMIN_USER_FIELDS = {\n banReason: 'ban_reason',\n banExpires: 'ban_expires',\n} as const;\n\n/**\n * Admin plugin adds an `impersonatedBy` field to the session model\n * recording the operator user id when an admin impersonates someone.\n */\nexport const AUTH_ADMIN_SESSION_FIELDS = {\n impersonatedBy: 'impersonated_by',\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthClient table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthClient` model mapping.\n *\n * The model name (`oauthClient`) is mapped to the existing\n * `sys_oauth_application` table to preserve data continuity from the\n * deprecated `oidc-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:---------------------------|:--------------------------------|\n * | clientId | client_id |\n * | clientSecret | client_secret |\n * | skipConsent | skip_consent |\n * | enableEndSession | enable_end_session |\n * | subjectType | subject_type |\n * | userId | user_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | redirectUris | redirect_uris |\n * | postLogoutRedirectUris | post_logout_redirect_uris |\n * | tokenEndpointAuthMethod | token_endpoint_auth_method |\n * | grantTypes | grant_types |\n * | responseTypes | response_types |\n * | requirePKCE | require_pkce |\n * | softwareId | software_id |\n * | softwareVersion | software_version |\n * | softwareStatement | software_statement |\n * | referenceId | reference_id |\n */\nexport const AUTH_OAUTH_CLIENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_APPLICATION, // 'sys_oauth_application'\n fields: {\n clientId: 'client_id',\n clientSecret: 'client_secret',\n skipConsent: 'skip_consent',\n enableEndSession: 'enable_end_session',\n subjectType: 'subject_type',\n userId: 'user_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n redirectUris: 'redirect_uris',\n postLogoutRedirectUris: 'post_logout_redirect_uris',\n tokenEndpointAuthMethod: 'token_endpoint_auth_method',\n grantTypes: 'grant_types',\n responseTypes: 'response_types',\n requirePKCE: 'require_pkce',\n softwareId: 'software_id',\n softwareVersion: 'software_version',\n softwareStatement: 'software_statement',\n referenceId: 'reference_id',\n },\n} as const;\n\n/**\n * @deprecated Use {@link AUTH_OAUTH_CLIENT_SCHEMA}. Retained as an alias for\n * historical imports; the new package renamed `oauthApplication` → `oauthClient`.\n */\nexport const AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthAccessToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthAccessToken` model mapping.\n *\n * In the new package, access tokens and refresh tokens are stored in\n * **separate** models. `oauthAccessToken` no longer carries a refresh token;\n * see {@link AUTH_OAUTH_REFRESH_TOKEN_SCHEMA} for the companion model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | refreshId | refresh_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n */\nexport const AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_ACCESS_TOKEN, // 'sys_oauth_access_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n refreshId: 'refresh_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthRefreshToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthRefreshToken` model mapping.\n *\n * Refresh tokens are linked to a session (via `session_id`) and to the\n * issuing client. Each access token rotation produces a new refresh-token\n * row.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | authTime | auth_time |\n */\nexport const AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_REFRESH_TOKEN, // 'sys_oauth_refresh_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n authTime: 'auth_time',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthConsent table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthConsent` model mapping.\n *\n * The new package dropped the boolean `consentGiven` flag — the presence of\n * a row implies consent was given for the listed scopes. A new\n * `referenceId` column was added for client-supplied correlation.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_OAUTH_CONSENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_CONSENT, // 'sys_oauth_consent'\n fields: {\n clientId: 'client_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Device Authorization plugin – deviceCode table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `device-authorization` plugin `deviceCode` model mapping.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant). Stores\n * pending device-flow requests issued via `POST /device/code`, polled at\n * `POST /device/token`, and approved/denied via `POST /device/{approve,deny}`.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | deviceCode | device_code |\n * | userCode | user_code |\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | lastPolledAt | last_polled_at |\n * | pollingInterval | polling_interval |\n * | clientId | client_id |\n */\nexport const AUTH_DEVICE_CODE_SCHEMA = {\n modelName: SystemObjectName.DEVICE_CODE, // 'sys_device_code'\n fields: {\n deviceCode: 'device_code',\n userCode: 'user_code',\n userId: 'user_id',\n expiresAt: 'expires_at',\n lastPolledAt: 'last_polled_at',\n pollingInterval: 'polling_interval',\n clientId: 'client_id',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `twoFactor()` plugin.\n *\n * @returns An object suitable for `twoFactor({ schema: … })`\n */\nexport function buildTwoFactorPluginSchema() {\n return {\n twoFactor: AUTH_TWO_FACTOR_SCHEMA,\n user: {\n fields: AUTH_TWO_FACTOR_USER_FIELDS,\n },\n };\n}\n\n/**\n * Builds the `schema` option for better-auth's `admin()` plugin.\n *\n * The admin plugin extends the user model with `role`/`banned`/`banReason`/\n * `banExpires` and the session model with `impersonatedBy`. Only the\n * snake_case-differing fields are mapped explicitly.\n */\nexport function buildAdminPluginSchema() {\n return {\n user: {\n fields: AUTH_ADMIN_USER_FIELDS,\n },\n session: {\n fields: AUTH_ADMIN_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build organization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `organization()` plugin.\n *\n * The organization plugin accepts a `schema` sub-option that allows\n * customising model names and field names for each table it manages.\n * This helper assembles the correct snake_case mappings from the\n * individual `AUTH_*_SCHEMA` constants above.\n *\n * @returns An object suitable for `organization({ schema: … })`\n */\nexport function buildOrganizationPluginSchema() {\n return {\n organization: AUTH_ORGANIZATION_SCHEMA,\n member: AUTH_MEMBER_SCHEMA,\n invitation: AUTH_INVITATION_SCHEMA,\n team: AUTH_TEAM_SCHEMA,\n teamMember: AUTH_TEAM_MEMBER_SCHEMA,\n session: {\n fields: AUTH_ORG_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JWT plugin – jwks table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `jwt` plugin `jwks` model mapping.\n *\n * The JWT plugin maintains a small set of rotating asymmetric key pairs\n * used to sign and verify issued JWTs (id_tokens for OIDC, JWT access\n * tokens). It is required by the `@better-auth/oauth-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | publicKey | public_key |\n * | privateKey | private_key |\n * | createdAt | created_at |\n * | expiresAt | expires_at |\n */\nexport const AUTH_JWKS_SCHEMA = {\n modelName: SystemObjectName.JWKS, // 'sys_jwks'\n fields: {\n publicKey: 'public_key',\n privateKey: 'private_key',\n createdAt: 'created_at',\n expiresAt: 'expires_at',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `jwt()` plugin.\n *\n * @returns An object suitable for `jwt({ schema: … })`\n */\nexport function buildJwtPluginSchema() {\n return {\n jwks: AUTH_JWKS_SCHEMA,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build OAuth provider plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for `@better-auth/oauth-provider`'s\n * `oauthProvider()` plugin.\n *\n * The plugin manages four tables: `oauthClient` (registered client apps —\n * mapped to ObjectStack's `sys_oauth_application` table for backwards\n * compatibility), `oauthAccessToken` (issued access tokens),\n * `oauthRefreshToken` (issued refresh tokens, linked to a session), and\n * `oauthConsent` (recorded user consents).\n *\n * @returns An object suitable for `oauthProvider({ schema: … })`\n */\nexport function buildOauthProviderPluginSchema() {\n return {\n oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,\n oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,\n oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,\n oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA,\n };\n}\n\n/**\n * @deprecated Use {@link buildOauthProviderPluginSchema}. Retained as an\n * alias for callers that imported the previous name during the migration\n * from the deprecated `better-auth/plugins/oidc-provider` plugin.\n */\nexport const buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;\n\n// ---------------------------------------------------------------------------\n// Helper: build device-authorization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `deviceAuthorization()` plugin.\n *\n * The plugin manages a single `deviceCode` table tracking pending RFC 8628\n * device-flow requests. This helper returns the snake_case mappings that\n * point the plugin at ObjectStack's `sys_device_code` object.\n *\n * @returns An object suitable for `deviceAuthorization({ schema: … })`\n */\nexport function buildDeviceAuthorizationPluginSchema() {\n return {\n deviceCode: AUTH_DEVICE_CODE_SCHEMA,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared `set-initial-password` handler.\n *\n * better-auth ships a `setPassword` operation that does EXACTLY what we want\n * (require a session, enforce min/max length, link a `credential` account if\n * none exists, refuse if one already does). But it is registered with\n * `createAuthEndpoint({ ... })` — note: NO leading path string — which means\n * better-auth deliberately exposes it as a **server-only** `auth.api.setPassword`\n * call and gives it no HTTP route. Setting a password without proving the old\n * one is privilege-sensitive, so it must not be reachable over the wire by\n * default.\n *\n * To let an SSO-onboarded user set an *initial* local password from the\n * browser, we wrap that server API in our own authenticated HTTP route. This\n * helper is the single source of truth for that route body so the two mount\n * points — the full `AuthPlugin` (host kernel) and the cloud `AuthProxyPlugin`\n * (per-environment runtime) — stay in lockstep instead of hand-copying ~50\n * lines of hash/createAccount logic (the original drift that let #1544 ship a\n * route on one path but not the other).\n */\n\n/** Minimal shape of the better-auth server API we depend on. */\nexport interface SetPasswordCapableApi {\n setPassword(opts: { body: { newPassword: string }; headers: Headers }): Promise<unknown>;\n}\n\nexport interface SetInitialPasswordResult {\n /** HTTP status to return to the caller. */\n status: number;\n /** JSON body; mirrors the `{ success, error: { code, message } }` envelope the client parses. */\n body: { success: boolean; error?: { code: string; message: string } };\n}\n\n/**\n * Run set-initial-password against the environment's better-auth API.\n *\n * @param authApi the better-auth server api (`auth.api`, via `AuthManager.getApi()`)\n * @param request the raw Web `Request` — its `headers` carry the session\n * cookie that better-auth's session middleware reads, and its\n * body carries `{ newPassword }`.\n */\nexport async function runSetInitialPassword(\n authApi: SetPasswordCapableApi,\n request: Request,\n): Promise<SetInitialPasswordResult> {\n let parsed: unknown;\n try {\n parsed = await request.json();\n } catch {\n parsed = {};\n }\n const newPassword: unknown = (parsed as { newPassword?: unknown } | null)?.newPassword;\n if (typeof newPassword !== 'string' || newPassword.length === 0) {\n return {\n status: 400,\n body: { success: false, error: { code: 'invalid_request', message: 'newPassword is required' } },\n };\n }\n\n try {\n // better-auth's session middleware reads the session from `headers`;\n // length checks + the \"already set\" guard happen inside setPassword.\n await authApi.setPassword({ body: { newPassword }, headers: request.headers });\n return { status: 200, body: { success: true } };\n } catch (error) {\n return mapSetPasswordError(error);\n }\n}\n\n/**\n * Map a better-auth `APIError` (better-call: `{ statusCode, status, body: { code, message } }`)\n * onto our response envelope. The client only surfaces `error.message`, but we\n * preserve the status code and code string for parity with the change/reset\n * flows. `PASSWORD_ALREADY_SET` is normalised to 409 so callers can tell\n * \"already has a password → use change-password\" apart from validation errors.\n */\nfunction mapSetPasswordError(error: unknown): SetInitialPasswordResult {\n const e = error as {\n statusCode?: number;\n status?: number | string;\n body?: { code?: string; message?: string };\n message?: string;\n } | null;\n\n const code = e?.body?.code ?? 'internal';\n const message = e?.body?.message ?? e?.message ?? 'set-initial-password failed';\n const rawStatus =\n typeof e?.statusCode === 'number' ? e.statusCode : typeof e?.status === 'number' ? e.status : 500;\n const status = code === 'PASSWORD_ALREADY_SET' ? 409 : rawStatus;\n\n return { status, body: { success: false, error: { code, message } } };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-auth manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `auth-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysAccount,\n SysApiKey,\n SysDeviceCode,\n SysInvitation,\n SysMember,\n SysJwks,\n SysOauthAccessToken,\n SysOauthApplication,\n SysOauthConsent,\n SysOauthRefreshToken,\n SysOrganization,\n SysSession,\n SysTeam,\n SysTeamMember,\n SysTwoFactor,\n SysUser,\n SysUserPreference,\n SysVerification,\n} from '@objectstack/platform-objects/identity';\n\nexport const AUTH_PLUGIN_ID = 'com.objectstack.plugin-auth';\nexport const AUTH_PLUGIN_VERSION = '3.0.1';\n\n/** Identity objects owned by plugin-auth. */\nexport const authIdentityObjects: any[] = [\n SysUser,\n SysSession,\n SysAccount,\n SysVerification,\n SysOrganization,\n SysMember,\n SysInvitation,\n SysTeam,\n SysTeamMember,\n SysApiKey,\n SysTwoFactor,\n SysUserPreference,\n SysOauthApplication,\n SysOauthAccessToken,\n SysOauthRefreshToken,\n SysOauthConsent,\n SysJwks,\n SysDeviceCode,\n];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const authPluginManifestHeader = {\n id: AUTH_PLUGIN_ID,\n namespace: 'sys',\n version: AUTH_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Authentication & Identity Plugin',\n description: 'Core authentication objects for ObjectStack (User, Session, Account, Verification)',\n};\n"],"mappings":"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","names":["import_system","import_system","organization","dataEngine","roles"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/auth-plugin.ts","../src/auth-manager.ts","../src/objectql-adapter.ts","../src/auth-schema-config.ts","../src/set-initial-password.ts","../src/register-sso-provider.ts","../src/manifest.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-auth\n * \n * Authentication & Identity Plugin for ObjectStack\n * Powered by better-auth for robust, secure authentication\n * Uses ObjectQL for data persistence (no third-party ORM required)\n */\n\nexport * from './auth-plugin.js';\nexport * from './auth-manager.js';\nexport * from './set-initial-password.js';\nexport * from './register-sso-provider.js';\nexport * from './objectql-adapter.js';\nexport * from './auth-schema-config.js';\nexport type { AuthConfig, AuthProviderConfig, AuthPluginConfig } from '@objectstack/spec/system';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport type { BetterAuthOptions } from 'better-auth';\nimport { AuthConfig, type SocialProviderConfig, SystemObjectName, SystemUserId } from '@objectstack/spec/system';\nimport {\n // ADR-0048 — the Setup/Studio/Account apps moved to their own packages\n // (@objectstack/{setup,studio,account}); plugin-auth no longer registers them.\n SystemOverviewDashboard,\n SystemOverviewDatasets,\n} from '@objectstack/platform-objects/apps';\nimport { SysOrganizationDetailPage, SysUserDetailPage } from '@objectstack/platform-objects/pages';\nimport { AuthManager, type AuthManagerOptions } from './auth-manager.js';\nimport { runSetInitialPassword } from './set-initial-password.js';\nimport { runRegisterSsoProviderFromForm, runRegisterSamlProviderFromForm, runRequestDomainVerification, runVerifyDomain } from './register-sso-provider.js';\nimport {\n authIdentityObjects,\n authPluginManifestHeader,\n} from './manifest.js';\n\n/**\n * Auth Plugin Options\n * Extends AuthConfig from spec with additional runtime options\n */\nexport interface AuthPluginOptions extends Partial<AuthConfig> {\n /**\n * Whether to automatically register auth routes\n * @default true\n */\n registerRoutes?: boolean;\n \n /**\n * Base path for auth routes\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * Override the datasource that owns the identity tables (sys_user,\n * sys_session, …) when AuthPlugin's manifest is registered.\n *\n * Defaults to `'cloud'` (control-plane DB) so the historical\n * single-tenant control-plane behaviour is preserved. Per-project\n * kernels in objectos pass `'default'` so identity tables live in the\n * project's own database — each project owns its own users.\n */\n manifestDatasource?: string;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin so invitations to those roles aren't rejected with\n * ROLE_NOT_FOUND. Forwarded as-is to AuthManager. See\n * {@link AuthManagerOptions.additionalOrgRoles} for details.\n */\n additionalOrgRoles?: string[];\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. Used by\n * platform consumers (objectos kernel) to attach a\n * `user.create.after` hook that auto-provisions a personal\n * organization for JIT-created SSO users — better-auth's adapter\n * bypasses kernel-level ObjectQL middleware, so this is the only\n * hook point that fires for every user creation path (email signup,\n * social/OIDC sign-in, admin-created accounts).\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n}\n\n/**\n * Authentication Plugin\n * \n * Provides authentication and identity services for ObjectStack applications.\n * \n * **Dual-Mode Operation:**\n * - **Server mode** (HonoServerPlugin active): Registers HTTP routes at basePath,\n * forwarding all auth requests to better-auth's universal handler.\n * - **MSW/Mock mode** (no HTTP server): Gracefully skips route registration but\n * still registers the `auth` service, allowing HttpDispatcher.handleAuth() to\n * simulate auth flows (sign-up, sign-in, etc.) for development and testing.\n * \n * Features:\n * - Session management\n * - User registration/login\n * - OAuth providers (Google, GitHub, etc.)\n * - Organization/team support\n * - 2FA, passkeys, magic links\n * \n * This plugin registers:\n * - `auth` service (auth manager instance) — always\n * - `app.com.objectstack.system` service (system object definitions) — always\n * - HTTP routes for authentication endpoints — only when HTTP server is available\n * \n * Integrates with better-auth library to provide comprehensive\n * authentication capabilities including email/password, OAuth, 2FA,\n * magic links, passkeys, and organization support.\n */\nexport class AuthPlugin implements Plugin {\n name = 'com.objectstack.auth';\n type = 'standard';\n version = '1.0.0';\n dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required\n \n private options: AuthPluginOptions;\n private authManager: AuthManager | null = null;\n private configuredSocialProviders: SocialProviderConfig | undefined;\n\n constructor(options: AuthPluginOptions = {}) {\n this.options = {\n registerRoutes: true,\n basePath: '/api/v1/auth',\n ...options\n };\n }\n\n /**\n * Open-source provider fallback: enable Google sign-in from conventional\n * provider env vars when the application did not configure Google itself.\n * Enterprise / product packages can contribute richer provider sets through\n * the `auth:configure` hook below.\n */\n private applyEnvSocialProviderFallbacks(config: AuthManagerOptions & AuthPluginOptions): void {\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n if (String(env?.OS_AUTH_GOOGLE_ENABLED ?? 'true').toLowerCase() === 'false') return;\n const googleClientId = env?.GOOGLE_CLIENT_ID;\n const googleClientSecret = env?.GOOGLE_CLIENT_SECRET;\n if (!googleClientId || !googleClientSecret) return;\n\n const socialProviders = {\n ...(config.socialProviders ?? {}),\n } as NonNullable<AuthPluginOptions['socialProviders']>;\n\n if (!socialProviders.google) {\n socialProviders.google = {\n clientId: googleClientId,\n clientSecret: googleClientSecret,\n enabled: true,\n };\n config.socialProviders = socialProviders;\n }\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Auth Plugin...');\n\n // Validate required configuration\n if (!this.options.secret) {\n throw new Error('AuthPlugin: secret is required');\n }\n\n // Get data engine service for database operations\n const dataEngine = ctx.getService<any>('data');\n if (!dataEngine) {\n ctx.logger.warn('No data engine service found - auth will use in-memory storage');\n }\n\n const authConfig: AuthManagerOptions & AuthPluginOptions = {\n ...this.options,\n dataEngine,\n };\n this.applyEnvSocialProviderFallbacks(authConfig);\n\n // Open extension point for packages that contribute auth providers\n // (enterprise SSO, hosted control-plane SSO, etc.) without forking\n // framework's AuthPlugin. Handlers mutate the draft config in place.\n await ctx.trigger('auth:configure', authConfig, ctx);\n this.configuredSocialProviders = authConfig.socialProviders\n ? { ...authConfig.socialProviders }\n : undefined;\n\n // Initialize auth manager with data engine\n this.authManager = new AuthManager(authConfig);\n\n // Register auth service\n ctx.registerService('auth', this.authManager);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...authPluginManifestHeader,\n ...(this.options.manifestDatasource\n ? { defaultDatasource: this.options.manifestDatasource }\n : {}),\n objects: authIdentityObjects,\n // ADR-0048 — Setup/Studio/Account apps (and the Setup nav contributions)\n // moved to their own one-app packages (@objectstack/{setup,studio,account}),\n // each registering under its own package id so /apps/<packageId> resolves\n // unambiguously. plugin-auth keeps only the auth objects + their pages.\n // Slotted record-detail pages for system objects — currently\n // sys_organization gets a Members / Invitations / Teams tab strip\n // (see SysOrganizationDetailPage for the rationale and the\n // intentionally-omitted OAuth / SSO tabs).\n pages: [SysOrganizationDetailPage, SysUserDetailPage],\n // List views for each Setup-nav object are defined on the schema\n // itself via the canonical `listViews` map (e.g.\n // sys_user.listViews.{all_users,unverified,two_factor}). Registering\n // top-level views here is the legacy pre-M10.30c pattern — it caused\n // duplicate \"Users\"/\"Roles\"/\"Sessions\" tabs to appear alongside the\n // schema-derived ones, sometimes referencing nonexistent fields\n // (e.g. legacy `users.view` had phone/status/active columns that do\n // not exist on sys_user). Schema-embedded listViews is the single\n // source of truth.\n dashboards: [SystemOverviewDashboard],\n // ADR-0021 — datasets backing the System Overview dashboard's widgets.\n datasets: SystemOverviewDatasets,\n // ADR-0024 / cloud#551 — surface \"SSO Providers\" (sys_sso_provider) in the\n // Setup app's Access Control group, but ONLY when the external-IdP RP is\n // wired (self-host `OS_SSO_ENABLED`, or the cloud per-env `planAllowsSso`\n // arriving via `plugins.sso`). Without the gate the entry would render an\n // empty list + a \"Register\" button whose endpoint 404s when SSO is off.\n // Owning-plugin-contributes pattern (ADR-0029 K2), mirroring plugin-security.\n ...(this.authManager.isSsoWired()\n ? {\n navigationContributions: [\n {\n app: 'setup',\n group: 'group_access_control',\n // After Roles/Permission-Sets (100) and Sharing (200), near API Keys (300).\n priority: 250,\n items: [\n {\n id: 'nav_sso_providers',\n type: 'object',\n label: 'SSO Providers',\n objectName: 'sys_sso_provider',\n icon: 'log-in',\n requiredPermissions: ['manage_platform_settings'],\n },\n ],\n },\n ],\n }\n : {}),\n });\n\n ctx.logger.info('Auth Plugin initialized successfully');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Auth Plugin...');\n\n if (!this.authManager) {\n throw new Error('Auth manager not initialized');\n }\n\n // Setup App translations are now loaded by `PlatformObjectsPlugin`\n // (in @objectstack/platform-objects). Translation bundles belong with\n // the package that defines them; auth-plugin no longer piggy-backs on\n // its kernel:ready hook for this.\n\n // Defer HTTP route registration to kernel:ready hook.\n // This ensures all plugins (including HonoServerPlugin) have completed\n // their init and start phases before we attempt to look up the\n // http-server service — making AuthPlugin resilient to plugin\n // loading order.\n if (this.options.registerRoutes) {\n ctx.hook('kernel:ready', async () => {\n // Inject the email service if available so better-auth callbacks\n // (sendResetPassword / sendVerificationEmail / sendInvitationEmail\n // / sendMagicLink) can actually deliver mail. Resolved here on\n // kernel:ready so EmailServicePlugin has had a chance to register.\n if (this.authManager) {\n await this.bindAuthSettings(ctx);\n\n let emailSvc: any;\n try { emailSvc = ctx.getService<any>('email'); } catch { emailSvc = undefined; }\n if (emailSvc) {\n this.authManager.setEmailService(emailSvc);\n ctx.logger.info('Auth: email service wired (transactional mail enabled)');\n } else {\n // No email service. The verification / password-reset callbacks now\n // THROW when invoked without a transport (so an explicit resend\n // reports a real error rather than faking success). If verification\n // is REQUIRED, that means every signup would be stuck — surface the\n // misconfiguration loudly at boot instead of one failure per signup.\n const requiresEmail = !!this.authManager.getPublicConfig?.()?.emailPassword?.requireEmailVerification;\n if (requiresEmail) {\n ctx.logger.error(\n 'Auth: email verification is REQUIRED but NO email service is registered — '\n + 'verification & password-reset emails will FAIL and new users will be locked '\n + 'out at sign-in. Register an email service (e.g. EmailServicePlugin + OS_EMAIL_*) '\n + 'or disable verification (OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false).',\n );\n } else {\n ctx.logger.info('Auth: no email service registered — transactional mail disabled');\n }\n }\n\n // Bind the email brand name (`{{appName}}`) to the live\n // `branding.workspace_name` setting so the admin UI can rename the\n // product without a redeploy. Only an *explicitly set* value\n // overrides the configured `appName` — when the operator hasn't\n // customised it (resolver returns the manifest default), we clear\n // the override so the deployment's `appName` (e.g. `OS_APP_NAME`)\n // keeps precedence. Mirrors EmailServicePlugin's settings binding.\n try {\n const settings = ctx.getService<any>('settings');\n if (settings && typeof settings.get === 'function') {\n const applyBrand = async () => {\n try {\n const resolved = await settings.get('branding', 'workspace_name', {});\n const explicit = resolved && resolved.source !== 'default'\n ? resolved.value\n : undefined;\n this.authManager?.setAppName(\n typeof explicit === 'string' ? explicit : undefined,\n );\n } catch (err: any) {\n ctx.logger.warn(\n 'Auth: failed to apply branding.workspace_name: ' + (err?.message ?? err),\n );\n }\n };\n await applyBrand();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('branding', () => {\n void applyBrand();\n });\n ctx.logger.info('Auth: bound appName to settings namespace=branding');\n }\n }\n } catch {\n // settings service is optional — keep the configured appName.\n }\n }\n\n let httpServer: IHttpServer | null = null;\n try {\n httpServer = ctx.getService<IHttpServer>('http-server');\n } catch {\n // Service not found — expected in MSW/mock mode\n }\n\n if (httpServer) {\n // Auto-detect the actual server URL when no explicit baseUrl was\n // configured, or when the configured baseUrl uses a different port\n // than the running server (e.g. port 3000 configured but 3002 bound).\n // getPort() is optional on IHttpServer; duck-type check for it.\n const serverWithPort = httpServer as IHttpServer & { getPort?: () => number };\n if (this.authManager && typeof serverWithPort.getPort === 'function') {\n const actualPort = serverWithPort.getPort();\n if (actualPort) {\n const configuredUrl = this.options.baseUrl || 'http://localhost:3000';\n const configuredOrigin = new URL(configuredUrl).origin;\n const actualUrl = `http://localhost:${actualPort}`;\n\n // Only auto-correct the port when the configured URL is already a\n // localhost URL (development mode). In production (Vercel/cloud) the\n // configured baseUrl is the real public hostname — never overwrite it\n // with a localhost URL, which would break OAuth callback URLs.\n const configuredIsLocalhost = configuredOrigin.startsWith('http://localhost');\n if (configuredIsLocalhost && configuredOrigin !== actualUrl) {\n this.authManager.setRuntimeBaseUrl(actualUrl);\n ctx.logger.info(\n `Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`,\n );\n }\n }\n }\n\n // Route registration errors should propagate (server misconfiguration)\n this.registerAuthRoutes(httpServer, ctx);\n ctx.logger.info(`Auth routes registered at ${this.options.basePath}`);\n } else {\n ctx.logger.warn(\n 'No HTTP server available — auth routes not registered. ' +\n 'Auth service is still available for MSW/mock environments via HttpDispatcher.'\n );\n }\n });\n }\n\n // Dev-only: provision a known, loginable platform admin on an empty DB.\n // Registered as its own kernel:ready hook (independent of registerRoutes)\n // so it runs whenever the runtime boots in development.\n ctx.hook('kernel:ready', async () => {\n await this.maybeSeedDevAdmin(ctx);\n });\n\n // Identity-source provenance for accounts created OUTSIDE better-auth's\n // `databaseHooks` — @better-auth/scim creates `sys_account` at the adapter\n // level, which BYPASSES `account.create.after` / `stampIdentitySource`. This\n // ObjectQL `afterInsert` hook stamps `source=idp_provisioned` regardless of\n // the creation path, so SCIM-provisioned users are correctly marked as the\n // managed mirror (ADR-0024 D4 / ADR-0071 verification #1). It mirrors the\n // federated branch of `stampIdentitySource`, is idempotent, and never breaks\n // the insert. Complementary to (not a replacement for) the OAuth-path stamp.\n ctx.hook('kernel:ready', async () => {\n try {\n // Use the kernel's ObjectQL engine (available + hookable at kernel:ready);\n // the auth manager's getDataEngine() is not yet wired this early.\n const engine: any = ctx.getService<any>('objectql');\n if (!engine || typeof engine.registerHook !== 'function') return;\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n engine.registerHook('afterInsert', async (hookCtx: any) => {\n try {\n if (hookCtx?.object !== 'sys_account') return;\n const acct: any = hookCtx.result ?? {};\n const providerId = acct.provider_id ?? acct.providerId;\n const userId = acct.user_id ?? acct.userId;\n // Only federated/SCIM accounts mark the user managed; a local\n // password (`credential`) keeps the user env-native.\n if (!userId || !providerId || providerId === 'credential') return;\n // QueryAST options use `where` (not `filter`); a wrong key is silently\n // ignored and counts every row — the bug that shipped env_native.\n const credCount = await engine.count('sys_account', {\n where: { user_id: userId, provider_id: 'credential' }, context: SYSTEM_CTX,\n });\n if (typeof credCount === 'number' && credCount > 0) return;\n const u = await engine.findOne('sys_user', {\n where: { id: userId }, fields: ['id', 'source'], context: SYSTEM_CTX,\n });\n if (u && u.source !== 'idp_provisioned') {\n await engine.update('sys_user', { id: userId, source: 'idp_provisioned' }, { context: SYSTEM_CTX });\n }\n } catch {\n // Provenance must never break account creation.\n }\n }, { packageId: 'com.objectstack.plugin-auth' });\n ctx.logger.info('Identity-source afterInsert stamp registered on sys_account (SCIM-safe)');\n } catch {\n // Engine not available — skip; OAuth path still stamps via databaseHooks.\n }\n });\n\n // Register auth middleware on ObjectQL engine (if available)\n try {\n const ql = ctx.getService<any>('objectql');\n if (ql && typeof ql.registerMiddleware === 'function') {\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // If context already has userId or isSystem, skip auth resolution\n if (opCtx.context?.userId || opCtx.context?.isSystem) {\n return next();\n }\n // Future: resolve session from AsyncLocalStorage or request context\n await next();\n });\n ctx.logger.info('Auth middleware registered on ObjectQL engine');\n }\n } catch (_e) {\n ctx.logger.debug('ObjectQL engine not available, skipping auth middleware registration');\n }\n\n ctx.logger.info('Auth Plugin started successfully');\n }\n\n /**\n * Bind the small open-source auth settings namespace to better-auth config.\n *\n * Only explicit settings values (stored or OS_AUTH_* env overrides) affect\n * runtime config. Manifest defaults are UI defaults and do not mask code or\n * deployment configuration.\n */\n private async bindAuthSettings(ctx: PluginContext): Promise<void> {\n if (!this.authManager) return;\n\n let settings: any;\n try {\n settings = ctx.getService<any>('settings');\n } catch {\n return;\n }\n if (!settings || typeof settings.getNamespace !== 'function') return;\n\n const applySettings = async (): Promise<void> => {\n if (!this.authManager) return;\n try {\n const payload = await settings.getNamespace('auth');\n const values: Record<string, unknown> = {};\n const sources: Record<string, string | undefined> = {};\n for (const [key, entry] of Object.entries(payload.values as Record<string, any>)) {\n values[key] = entry?.value;\n sources[key] = entry?.source;\n }\n\n const isExplicit = (key: string) => (sources[key] ?? 'default') !== 'default';\n const asBoolean = (value: unknown, fallback: boolean): boolean => {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') return value.toLowerCase() !== 'false';\n if (typeof value === 'number') return value !== 0;\n return fallback;\n };\n const asTrimmedString = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n };\n const asPositiveInt = (value: unknown): number | undefined => {\n const n = Math.floor(Number(value));\n return Number.isFinite(n) && n > 0 ? n : undefined;\n };\n\n const patch: Partial<AuthManagerOptions> = {};\n const emailAndPassword: Partial<NonNullable<AuthConfig['emailAndPassword']>> = {};\n if (isExplicit('email_password_enabled')) {\n emailAndPassword.enabled = asBoolean(values.email_password_enabled, true);\n }\n if (isExplicit('signup_enabled')) {\n emailAndPassword.disableSignUp = !asBoolean(values.signup_enabled, true);\n }\n if (isExplicit('require_email_verification')) {\n emailAndPassword.requireEmailVerification = asBoolean(\n values.require_email_verification,\n false,\n );\n }\n // Password policy — better-auth enforces these bounds on sign-up and\n // password reset. Ignore malformed/non-positive values (keep the default).\n if (isExplicit('password_min_length')) {\n const n = asPositiveInt(values.password_min_length);\n if (n !== undefined) emailAndPassword.minPasswordLength = n;\n }\n if (isExplicit('password_max_length')) {\n const n = asPositiveInt(values.password_max_length);\n if (n !== undefined) emailAndPassword.maxPasswordLength = n;\n }\n if (Object.keys(emailAndPassword).length > 0) {\n patch.emailAndPassword = emailAndPassword as AuthManagerOptions['emailAndPassword'];\n }\n\n // Breached-password rejection (ADR-0069 D1) — enables better-auth's\n // native `haveibeenpwned` plugin via the plugin-config gate. Default\n // off; only an explicit toggle applies (manifest defaults must not\n // mask the deployment env var). See buildPluginList() for the seam.\n if (isExplicit('password_reject_breached')) {\n patch.plugins = {\n ...(patch.plugins ?? {}),\n passwordRejectBreached: asBoolean(values.password_reject_breached, false),\n } as AuthManagerOptions['plugins'];\n }\n\n // Password complexity (ADR-0069 D1) — custom validator in the before\n // hook (better-auth only enforces length). Only explicit values apply.\n if (isExplicit('password_require_complexity')) {\n patch.passwordRequireComplexity = asBoolean(values.password_require_complexity, false);\n }\n if (isExplicit('password_min_classes')) {\n const n = asPositiveInt(values.password_min_classes);\n if (n !== undefined) patch.passwordMinClasses = Math.min(4, Math.max(1, n));\n }\n if (isExplicit('password_history_count')) {\n // 0 disables → use a non-negative reader (asPositiveInt rejects 0).\n const n = Math.floor(Number(values.password_history_count));\n if (Number.isFinite(n) && n >= 0) patch.passwordHistoryCount = Math.min(24, n);\n }\n if (isExplicit('password_expiry_days')) {\n // 0 disables expiry → non-negative reader.\n const n = Math.floor(Number(values.password_expiry_days));\n if (Number.isFinite(n) && n >= 0) patch.passwordExpiryDays = Math.min(3650, n);\n }\n\n // Enforced MFA (ADR-0069 D3). Enabling it also turns the twoFactor\n // plugin on so the /two-factor/* enrollment endpoints exist — otherwise\n // gated users would have no way to comply.\n if (isExplicit('mfa_required')) {\n const on = asBoolean(values.mfa_required, false);\n patch.mfaRequired = on;\n if (on) {\n patch.plugins = {\n ...(patch.plugins ?? {}),\n twoFactor: true,\n } as AuthManagerOptions['plugins'];\n }\n }\n if (isExplicit('mfa_grace_period_days')) {\n const n = Math.floor(Number(values.mfa_grace_period_days));\n if (Number.isFinite(n) && n >= 0) patch.mfaGracePeriodDays = Math.min(90, n);\n }\n\n // Session lifetime — days → seconds for better-auth's `session`\n // (`expiresIn` = absolute lifetime; `updateAge` = refresh threshold).\n const session: { expiresIn?: number; updateAge?: number } = {};\n if (isExplicit('session_expiry_days')) {\n const d = asPositiveInt(values.session_expiry_days);\n if (d !== undefined) session.expiresIn = d * 86_400;\n }\n if (isExplicit('session_refresh_days')) {\n const d = asPositiveInt(values.session_refresh_days);\n if (d !== undefined) session.updateAge = d * 86_400;\n }\n if (Object.keys(session).length > 0) {\n patch.session = session as AuthManagerOptions['session'];\n }\n\n // Session controls (ADR-0069 D4) — idle / absolute / concurrent. 0 = off;\n // non-negative reader so an explicit 0 disables.\n const asNonNeg = (v: unknown): number | undefined => {\n const n = Math.floor(Number(v));\n return Number.isFinite(n) && n >= 0 ? n : undefined;\n };\n if (isExplicit('session_idle_timeout_minutes')) {\n const n = asNonNeg(values.session_idle_timeout_minutes);\n if (n !== undefined) patch.sessionIdleTimeoutMinutes = n;\n }\n if (isExplicit('session_absolute_max_hours')) {\n const n = asNonNeg(values.session_absolute_max_hours);\n if (n !== undefined) patch.sessionAbsoluteMaxHours = n;\n }\n if (isExplicit('max_concurrent_sessions_per_user')) {\n const n = asNonNeg(values.max_concurrent_sessions_per_user);\n if (n !== undefined) patch.maxConcurrentSessions = n;\n }\n\n // Network gating (ADR-0069 D5) — parse the CIDR/IP textarea into a list.\n if (isExplicit('allowed_ip_ranges')) {\n const raw = asTrimmedString(values.allowed_ip_ranges) ?? '';\n patch.allowedIpRanges = raw\n .split(/[\\n,]+/)\n .map((r) => r.trim())\n .filter(Boolean);\n }\n\n // Anti-abuse (ADR-0069 D2) — account lockout (custom, per-identity)\n // and rate-limit tuning (better-auth-native, per-IP). `asPositiveInt`\n // rejects 0/malformed; lockout_threshold uses a non-negative reader so\n // an explicit 0 can turn the feature off.\n const asNonNegativeInt = (value: unknown): number | undefined => {\n const n = Math.floor(Number(value));\n return Number.isFinite(n) && n >= 0 ? n : undefined;\n };\n if (isExplicit('lockout_threshold')) {\n const n = asNonNegativeInt(values.lockout_threshold);\n if (n !== undefined) patch.lockoutThreshold = n;\n }\n if (isExplicit('lockout_duration_minutes')) {\n const n = asPositiveInt(values.lockout_duration_minutes);\n if (n !== undefined) patch.lockoutDurationMinutes = n;\n }\n if (isExplicit('rate_limit_max') || isExplicit('rate_limit_window_seconds')) {\n const max = asPositiveInt(values.rate_limit_max) ?? 10;\n const window = asPositiveInt(values.rate_limit_window_seconds) ?? 60;\n // Tighten the auth-mutating endpoints; better-auth keeps its own\n // defaults for everything else. customRules support `*` wildcards.\n patch.rateLimit = {\n enabled: true,\n window,\n max,\n customRules: {\n '/sign-in/email': { window, max },\n '/sign-up/email': { window, max },\n '/request-password-reset': { window, max },\n '/reset-password': { window, max },\n },\n } as AuthManagerOptions['rateLimit'];\n }\n\n if (\n isExplicit('google_enabled') ||\n isExplicit('google_client_id') ||\n isExplicit('google_client_secret')\n ) {\n const socialProviders = {\n ...(this.configuredSocialProviders ?? {}),\n } as NonNullable<SocialProviderConfig>;\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n const googleEnabledFromEnv = env?.OS_AUTH_GOOGLE_ENABLED != null\n ? asBoolean(env.OS_AUTH_GOOGLE_ENABLED, true)\n : undefined;\n const googleClientId = asTrimmedString(values.google_client_id) ?? env?.GOOGLE_CLIENT_ID;\n const googleClientSecret = asTrimmedString(values.google_client_secret) ?? env?.GOOGLE_CLIENT_SECRET;\n if (googleEnabledFromEnv ?? (isExplicit('google_enabled') ? asBoolean(values.google_enabled, true) : true)) {\n if (!socialProviders.google && googleClientId && googleClientSecret) {\n socialProviders.google = {\n clientId: googleClientId,\n clientSecret: googleClientSecret,\n enabled: true,\n };\n }\n } else {\n delete socialProviders.google;\n }\n patch.socialProviders = Object.keys(socialProviders).length > 0\n ? socialProviders\n : undefined;\n }\n\n if (Object.keys(patch).length > 0) {\n this.authManager.applyConfigPatch(patch);\n }\n } catch (err: any) {\n ctx.logger.warn('Auth: failed to apply auth settings: ' + (err?.message ?? err));\n }\n };\n\n await applySettings();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('auth', () => {\n void applySettings();\n });\n ctx.logger.info('Auth: bound to settings namespace=auth');\n }\n }\n\n async destroy(): Promise<void> {\n // Cleanup if needed\n this.authManager = null;\n }\n\n /**\n * Dev-only admin bootstrap.\n *\n * On an EMPTY database (zero users), provision a well-known, loginable\n * admin (admin@objectos.ai / admin123 by default) so backend debugging\n * never blocks on a first-run sign-up wizard. The account is created\n * through better-auth's real server-side `signUpEmail` pipeline (hashed\n * credential + the same hooks the HTTP endpoint runs), so it is fully\n * loginable; plugin-security's first-user middleware then promotes it to\n * platform admin automatically.\n *\n * This replaces two earlier, divergent seeds:\n * • the CLI-side HTTP seed (`os dev`), which POSTed the public sign-up\n * endpoint from the parent process — racing server readiness and\n * targeting a hard-coded port that broke under dev port auto-shift; and\n * • plugin-dev's raw `sys_user` insert, which produced a credential-less,\n * un-loginable row.\n * Running it in-process needs no port and no readiness polling.\n *\n * Idempotent and non-destructive: it only ever acts on a zero-user DB and\n * never touches an existing account, so a custom password is never\n * overwritten.\n *\n * HARD-GATED to development (NODE_ENV==='development'): a known-credential\n * admin can never be provisioned in production. Opt out within dev via\n * OS_SEED_ADMIN=0 (or false/off/no).\n */\n private async maybeSeedDevAdmin(ctx: PluginContext): Promise<void> {\n if (process.env.NODE_ENV !== 'development') return;\n const flag = String(process.env.OS_SEED_ADMIN ?? '').trim().toLowerCase();\n if (['0', 'false', 'off', 'no'].includes(flag)) return;\n\n const email = process.env.OS_SEED_ADMIN_EMAIL?.trim() || 'admin@objectos.ai';\n const password = process.env.OS_SEED_ADMIN_PASSWORD?.trim() || 'admin123';\n const name = process.env.OS_SEED_ADMIN_NAME?.trim() || 'Dev Admin';\n\n let ql: any;\n try { ql = ctx.getService<any>('objectql'); } catch { /* unavailable */ }\n if (!ql || typeof ql.find !== 'function') return;\n\n try {\n // Only seed when no HUMAN user exists yet. A fresh DB still contains\n // the system service account (SystemUserId.SYSTEM, role='system'),\n // which must NOT count — mirror plugin-security's first-user detection\n // so the seed fires on a genuinely empty DB. Any real human user (or a\n // prior sign-up) disables the seed for good; we never touch or\n // overwrite an existing account.\n const rows = await ql\n .find(SystemObjectName.USER, { where: {}, limit: 50 }, { context: { isSystem: true } })\n .catch(() => []);\n const humans = (Array.isArray(rows) ? rows : [])\n .filter((u: any) => u && u.id !== SystemUserId.SYSTEM && u.role !== 'system');\n if (humans.length > 0) {\n ctx.logger.debug('[auth] dev admin seed skipped — a user already exists');\n return;\n }\n\n if (!this.authManager) return;\n const api: any = await this.authManager.getApi();\n if (typeof api?.signUpEmail !== 'function') {\n ctx.logger.warn('[auth] dev admin seed skipped — signUpEmail unavailable');\n return;\n }\n\n // Real auth pipeline: creates sys_user + a hashed `credential` account\n // and runs the sign-up hooks. The dev-mode OS_DISABLE_SIGNUP bypass\n // (auth-manager.ts) lets this through on an empty DB even when sign-up\n // is otherwise disabled.\n await api.signUpEmail({ body: { email, password, name } });\n ctx.logger.info(`🔑 Dev admin seeded: ${email} / ${password}`);\n // Surface the credentials in the `serve` startup banner. The\n // ctx.logger line above is swallowed by serve's boot-quiet window\n // (the seed runs during runtime.start(), before stdout is restored),\n // so the CLI reads this off the `auth` service and prints it after\n // the banner instead.\n this.authManager.devSeedResult = { email, password };\n } catch (err: any) {\n // Best-effort. The common benign case is a race where a real sign-up\n // landed first (unique-email violation) — treat as \"already seeded\".\n ctx.logger.warn(`[auth] dev admin seed skipped: ${err?.message ?? err}`);\n }\n }\n\n /**\n * Register authentication routes with HTTP server\n * \n * Uses better-auth's universal handler for all authentication requests.\n * This forwards all requests under basePath to better-auth, which handles:\n * - Email/password authentication\n * - OAuth providers (Google, GitHub, etc.)\n * - Session management\n * - Password reset\n * - Email verification\n * - 2FA, passkeys, magic links (if enabled)\n */\n private registerAuthRoutes(httpServer: IHttpServer, ctx: PluginContext): void {\n if (!this.authManager) return;\n\n const basePath = this.options.basePath || '/api/v1/auth';\n\n // Get raw Hono app to use native wildcard routing\n // Type assertion is safe here because we explicitly require Hono server as a dependency\n if (!('getRawApp' in httpServer) || typeof (httpServer as any).getRawApp !== 'function') {\n ctx.logger.error('HTTP server does not support getRawApp() - wildcard routing requires Hono server');\n throw new Error(\n 'AuthPlugin requires HonoServerPlugin for wildcard routing support. ' +\n 'Please ensure HonoServerPlugin is loaded before AuthPlugin.'\n );\n }\n\n const rawApp = (httpServer as any).getRawApp();\n\n // ── ADR-0069 D5 — network gating (IP allow-list) ──────────────────────\n // Reject auth requests from a client IP outside the configured ranges,\n // BEFORE they reach better-auth. Registered first so it runs ahead of the\n // routes below. The public render helpers (/config, /bootstrap-status) are\n // exempt so a blocked client still gets a clean login page + error. No-op\n // (and no IP parse) when no ranges are configured.\n if (typeof rawApp.use === 'function') rawApp.use(`${basePath}/*`, async (c: any, next: any) => {\n const mgr = this.authManager;\n if (!mgr || typeof mgr.isClientIpAllowed !== 'function') return next();\n const path: string = c.req.path || '';\n if (path.endsWith('/config') || path.endsWith('/bootstrap-status')) return next();\n const fwd = c.req.header('x-forwarded-for');\n const ip =\n (typeof fwd === 'string' && fwd.split(',')[0].trim()) ||\n c.req.header('cf-connecting-ip') ||\n c.req.header('x-real-ip') ||\n undefined;\n if (!mgr.isClientIpAllowed(ip)) {\n return c.json(\n { success: false, error: { code: 'IP_NOT_ALLOWED', message: 'Sign-in is not allowed from your network.' } },\n 403,\n );\n }\n return next();\n });\n\n // Register /config before the wildcard so it takes precedence.\n // better-auth has no /config endpoint, so without this explicit route\n // the wildcard below forwards the request and better-auth returns 404.\n rawApp.get(`${basePath}/config`, async (c: any) => {\n try {\n const config = this.authManager!.getPublicConfig();\n // Refine the coarse \"SSO wired\" flag to \"SSO usable\" (≥1 provider\n // configured) so the login UI also hides the \"Sign in with SSO\" button\n // when SSO is enabled but no IdP exists yet — not just when it's off.\n // Only queries when wired; falls open on any error (see isSsoUsable).\n if (config.features?.sso) {\n config.features.sso = await this.authManager!.isSsoUsable();\n }\n return c.json({ success: true, data: config });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({ success: false, error: { code: 'auth_config_error', message: err.message } }, 500);\n }\n });\n\n // Bootstrap status — does an owner exist yet? Used by the Account SPA's\n // root route to decide between rendering /login (normal flow) and\n // /setup (first-run owner creation). Public, unauthenticated; only\n // returns a boolean so it can be polled before the user has any\n // credentials.\n rawApp.get(`${basePath}/bootstrap-status`, async (c: any) => {\n try {\n const dataEngine = this.authManager!.getDataEngine();\n if (!dataEngine) {\n // No data engine wired (e.g. MSW/mock mode) — assume bootstrapped\n // so the SPA falls through to its normal login flow.\n return c.json({ hasOwner: true });\n }\n const count = await dataEngine.count('sys_user', {});\n return c.json({ hasOwner: (count ?? 0) > 0 });\n } catch (error) {\n ctx.logger.warn('[AuthPlugin] bootstrap-status check failed; assuming bootstrapped', error as Error);\n return c.json({ hasOwner: true });\n }\n });\n\n // Device Authorization Grant (RFC 8628) endpoints — `/device/code`,\n // `/device/token`, `/device/approve`, `/device/deny`, `/device` — are\n // provided by better-auth's `device-authorization` plugin and reach\n // the wildcard handler below. Enable via\n // `AuthPluginConfig.deviceAuthorization`.\n\n // Set an INITIAL local password for users who signed in via SSO and\n // have no `credential` account yet. This is the \"Set local password\"\n // affordance on a per-environment kernel — it lets a user that the\n // platform onboarded via the objectstack-cloud OAuth provider sign in\n // with email/password against this environment going forward without\n // needing the SSO round-trip. Requires a valid session (so we know\n // which user is asking) and refuses if a credential already exists\n // (the user should use better-auth's /change-password endpoint in\n // that case so the current password is verified).\n //\n // The body is `runSetInitialPassword` (shared with the cloud\n // AuthProxyPlugin) so both mount points wrap better-auth's server-only\n // `auth.api.setPassword` identically — see set-initial-password.ts.\n rawApp.post(`${basePath}/set-initial-password`, async (c: any) => {\n try {\n const authApi = await this.authManager!.getApi();\n const { status, body } = await runSetInitialPassword(authApi as any, c.req.raw);\n return c.json(body, status);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] set-initial-password failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth admin: toggle the `disabled` flag on a registered OAuth client.\n //\n // Why this lives here (and not as a plain data-layer UPDATE on\n // sys_oauth_application): better-auth 1.6.11's stock admin update\n // endpoint (`/admin/oauth2/update-client`) does NOT accept `disabled`\n // in its Zod body schema, so the field gets silently stripped before\n // it reaches `updateClientEndpoint`. The column exists, the runtime\n // honours it everywhere (introspect, token, authorize, public-client\n // lookup), but no client-facing API can flip it.\n //\n // We close the gap by writing through better-auth's own adapter under\n // the `/api/v1/auth/*` namespace so all OAuth-application mutations\n // remain auth-routed (no generic data-layer bypass for the `oauth_client`\n // model). When upstream adds `disabled` to `adminUpdateOAuthClient`'s\n // schema, this route can be deleted and the sys_oauth_application\n // action retargeted to the stock endpoint.\n //\n // Upstream tracking: https://github.com/better-auth/better-auth\n rawApp.post(`${basePath}/admin/oauth2/toggle-disabled`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const clientId: unknown = body?.client_id;\n const disabled: unknown = body?.disabled;\n if (typeof clientId !== 'string' || clientId.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'client_id is required' } }, 400);\n }\n if (typeof disabled !== 'boolean') {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'disabled must be a boolean' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n // The customSession plugin synthesizes `user.role = 'admin'` for\n // platform admins (admin_full_access permission set) and active-org\n // owners/admins; anyone else is denied.\n if ((session.user as any).role !== 'admin') {\n return c.json({ success: false, error: { code: 'forbidden', message: 'Admin role required' } }, 403);\n }\n\n // Write through the same ObjectQL data engine that better-auth's\n // adapter uses. We target the snake_case table name (`sys_oauth_application`,\n // mapped from better-auth's internal `oauthClient` model via\n // `auth-schema-config.ts`) because `$context.adapter`'s model-lookup\n // helper does not see plugin-provided model names from outside\n // better-auth's own endpoint invocation context. This is the same\n // physical row the better-auth runtime reads at introspect / token\n // / authorize time, so the toggle is fully honoured.\n const dataEngine: any = this.authManager!.getDataEngine();\n if (!dataEngine) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'Data engine unavailable' } }, 503);\n }\n\n const existing = await dataEngine.findOne('sys_oauth_application', {\n where: { client_id: clientId },\n });\n if (!existing) {\n return c.json({ success: false, error: { code: 'not_found', message: 'OAuth client not found' } }, 404);\n }\n\n const updated = await dataEngine.update('sys_oauth_application', {\n id: existing.id,\n disabled,\n updated_at: new Date(Math.floor(Date.now() / 1000) * 1000),\n });\n if (!updated) {\n return c.json({ success: false, error: { code: 'internal', message: 'Unable to update OAuth client' } }, 500);\n }\n\n return c.json({\n success: true,\n data: {\n client_id: clientId,\n disabled,\n },\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] toggle-disabled failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // SSO admin: register an external OIDC IdP from the flat metadata form\n // (ADR-0024). `@better-auth/sso`'s POST /sso/register expects the protocol\n // fields NESTED under `oidcConfig` ({ clientId, clientSecret,\n // discoveryEndpoint, scopes, mapping }). The `sys_sso_provider`\n // `register_sso_provider` action collects FLAT form fields (the action\n // param schema has no nested-path support), so posting them straight to\n // /sso/register lands them at the top level where better-auth's Zod schema\n // strips them → a provider with `oidc_config = null` that can never\n // complete a login. This thin bridge reshapes the flat form body into the\n // nested shape and RE-DISPATCHES it through the real /sso/register endpoint\n // (via the better-auth handler) so the admin gate, the public-routable\n // trustedOrigins allowance, discovery hydration, and secret handling all\n // still run. No bespoke persistence. Retire when the action framework\n // gains nested-param support.\n rawApp.post(`${basePath}/admin/sso/register`, async (c: any) => {\n try {\n const { status, body } = await runRegisterSsoProviderFromForm(\n (req) => this.authManager!.handleRequest(req),\n c.req.raw,\n );\n return c.json(body, status as any);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sso/register bridge failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // ADR-0069 D2 — admin: clear a brute-force lockout on an account.\n // Lockout (`sys_user.locked_until` / `failed_login_count`) is a custom,\n // per-identity mechanism with no better-auth endpoint, so this route owns\n // the \"Unlock\" affordance (sys_user `unlock_user` action). Admin-guarded\n // server-side; mirrors the toggle-disabled route's session+role check.\n rawApp.post(`${basePath}/admin/unlock-user`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n const userId: unknown = body?.userId ?? body?.user_id;\n if (typeof userId !== 'string' || userId.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'userId is required' } }, 400);\n }\n\n const authApi = await this.authManager!.getApi();\n const session = await authApi.getSession({ headers: c.req.raw.headers });\n if (!session?.user?.id) {\n return c.json({ success: false, error: { code: 'unauthorized', message: 'Sign in first' } }, 401);\n }\n // Platform-admin gate. Accept any of the equivalent signals the\n // customSession plugin may carry (ADR-0068): the derived\n // `isPlatformAdmin` alias, the canonical `platform_admin` in roles[],\n // or the legacy admin-plugin `role` scalar.\n const u: any = session.user;\n const isAdmin =\n u?.isPlatformAdmin === true ||\n (Array.isArray(u?.roles) && u.roles.includes('platform_admin')) ||\n u?.role === 'admin';\n if (!isAdmin) {\n return c.json({ success: false, error: { code: 'forbidden', message: 'Admin role required' } }, 403);\n }\n\n const ok = await this.authManager!.unlockUser(userId);\n if (!ok) {\n return c.json({ success: false, error: { code: 'not_found', message: 'User not found or data engine unavailable' } }, 404);\n }\n return c.json({ success: true, data: { userId } });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] unlock-user failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // ADR-0069 P3 — register a SAML 2.0 IdP. Mirrors the OIDC bridge above:\n // the metadata `register_saml_provider` action posts FLAT fields; the shared\n // helper reshapes them into better-auth's nested `samlConfig` (deriving the\n // per-provider ACS URL) and re-dispatches through /sso/register so the\n // admin gate + provisioning all run. Returns SP ACS + metadata URLs.\n rawApp.post(`${basePath}/admin/sso/register-saml`, async (c: any) => {\n try {\n const { status, body } = await runRegisterSamlProviderFromForm(\n (req) => this.authManager!.handleRequest(req),\n c.req.raw,\n );\n return c.json(body, status as any);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sso/register-saml bridge failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // SSO domain verification (ADR-0024 ②, opt-in OS_SSO_DOMAIN_VERIFICATION).\n // Re-dispatch through @better-auth/sso's /sso/{request-domain-verification,\n // verify-domain} (so the per-provider admin gate runs) and reshape into the\n // `{ success, data }` envelope the action `resultDialog` / toast reads:\n // request returns the ready-to-paste DNS TXT record, verify returns a clear\n // success/error. A 404 from the inner endpoint = feature OFF for this env.\n rawApp.post(`${basePath}/admin/sso/request-domain-verification`, async (c: any) => {\n try {\n const { status, body } = await runRequestDomainVerification(\n (req) => this.authManager!.handleRequest(req),\n c.req.raw,\n );\n return c.json(body, status as any);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sso/request-domain-verification bridge failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n rawApp.post(`${basePath}/admin/sso/verify-domain`, async (c: any) => {\n try {\n const { status, body } = await runVerifyDomain(\n (req) => this.authManager!.handleRequest(req),\n c.req.raw,\n );\n return c.json(body, status as any);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sso/verify-domain bridge failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // ────────────────────────────────────────────────────────────────────\n // OAuth self-service: register an OAuth application for the signed-in\n // user. Thin wrapper over better-auth's `/oauth2/create-client`\n // endpoint (session-required, auto-stamps `user_id` from the session).\n //\n // Why this wrapper exists: the Account-app action surfaces a\n // user-friendly textarea for \"Redirect URLs\" (one per line), but\n // better-auth's Zod body schema requires `redirect_uris: string[]`.\n // The metadata-driven action runner POSTs param values verbatim, so\n // without a translation layer the upstream call fails validation with\n // `Invalid input: expected array, received string`. We split the\n // textarea on newlines, trim, drop empties, and forward to\n // `createOAuthClient` so the row gets persisted with the caller's\n // user_id and shows up in the `mine` listView.\n //\n // Upstream alternative would be enabling `allowDynamicClientRegistration`\n // on `/oauth2/register`, but DCR has additional security implications\n // (rate limiting, scope restriction) we don't want to enable broadly\n // just to fix UX. Keeping the wrapper scoped to the self-service flow.\n rawApp.post(`${basePath}/sys-oauth-application/register`, async (c: any) => {\n try {\n let body: any = {};\n try { body = await c.req.json(); } catch { body = {}; }\n\n const name: unknown = body?.name;\n const redirectUrlsInput: unknown = body?.redirectURLs;\n const type: unknown = body?.type;\n\n if (typeof name !== 'string' || name.trim().length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'name is required' } }, 400);\n }\n if (typeof redirectUrlsInput !== 'string' || redirectUrlsInput.trim().length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'redirectURLs is required' } }, 400);\n }\n\n const redirectUris = redirectUrlsInput\n .split(/[\\r\\n]+/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n if (redirectUris.length === 0) {\n return c.json({ success: false, error: { code: 'invalid_request', message: 'redirectURLs must contain at least one URL' } }, 400);\n }\n\n const allowedTypes = new Set(['web', 'native', 'user-agent-based']);\n const safeType = typeof type === 'string' && allowedTypes.has(type) ? type : 'web';\n\n const authApi: any = await this.authManager!.getApi();\n if (!authApi?.createOAuthClient) {\n return c.json({ success: false, error: { code: 'unavailable', message: 'OIDC provider is not enabled on this environment' } }, 503);\n }\n\n // Forward request headers so better-auth can resolve the caller's\n // session (sessionMiddleware on /oauth2/create-client). Without\n // the session the row would lack `user_id` and never appear in\n // the My Applications view.\n let result: any;\n try {\n result = await authApi.createOAuthClient({\n body: {\n client_name: name.trim(),\n redirect_uris: redirectUris,\n type: safeType,\n },\n headers: c.req.raw.headers,\n });\n } catch (err: any) {\n const status = typeof err?.status === 'number' ? err.status : 500;\n const code = err?.body?.error ?? 'oauth_register_failed';\n const message = err?.body?.error_description ?? err?.message ?? 'Unable to register OAuth client';\n return c.json({ success: false, error: { code, message } }, status);\n }\n\n // Mirror the response shape consumed by the action's resultDialog\n // (`client.client_id`, `client.client_secret`).\n return c.json({ success: true, data: { client: result } });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('[AuthPlugin] sys-oauth-application/register failed', err);\n return c.json({ success: false, error: { code: 'internal', message: err.message } }, 500);\n }\n });\n\n // Register wildcard route to forward all auth requests to better-auth.\n // better-auth is configured with basePath matching our route prefix, so we\n // forward the original request directly — no path rewriting needed.\n rawApp.all(`${basePath}/*`, async (c: any) => {\n try {\n // Forward the original request to better-auth handler\n const response = await this.authManager!.handleRequest(c.req.raw);\n\n // better-auth catches internal errors and returns error Responses\n // without throwing, so the catch block below would never trigger.\n // We proactively log server errors here for observability.\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: ${body}`));\n } catch {\n ctx.logger.error('[AuthPlugin] better-auth returned server error', new Error(`HTTP ${response.status}: (unable to read body)`));\n }\n }\n\n // Public-cache JWKS: it's static JSON that only changes when the\n // signing key rotates (default ~30 days). better-auth doesn't set\n // any Cache-Control header, so every relying party currently\n // re-fetches it on every JWT verification (≈700 ms warm against a\n // Container DO + Neon). Add a conservative public cache so CF's\n // edge can short-circuit repeated fetches. The 5-min freshness +\n // 24 h SWR window is well inside better-auth's default rotation\n // and matches what most IdPs publish (Auth0, Cognito, Google).\n try {\n const url = c.req.url as string;\n if (response.ok && /\\/jwks(\\?|$)/.test(url)) {\n const existing = response.headers.get('cache-control');\n if (!existing) {\n response.headers.set(\n 'cache-control',\n 'public, max-age=300, stale-while-revalidate=86400',\n );\n }\n }\n } catch { /* best-effort header annotation */ }\n\n return response;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.logger.error('Auth request error:', err);\n \n // Return error response\n return new Response(\n JSON.stringify({\n success: false,\n error: err.message,\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n });\n\n // OIDC / OAuth 2.0 Authorization Server Metadata (RFC 8414) and\n // OpenID Connect Discovery 1.0 require the well-known documents to be\n // served from the **root** of the issuer URL — not under our auth\n // basePath. `@better-auth/oauth-provider` ships dedicated helpers for\n // this case (`oauthProviderAuthServerMetadata` /\n // `oauthProviderOpenIdConfigMetadata`) which we mount here so external\n // OIDC clients can discover the IdP at the canonical paths.\n //\n // Honour the same `OS_OIDC_PROVIDER_ENABLED` env-var override that\n // `AuthManager.buildPlugins()` uses — without this check the\n // discovery routes would NOT mount when an operator flipped the\n // env var on without editing the config file, leaving external\n // OIDC clients unable to discover the IdP.\n const oidcEnv = (globalThis as any)?.process?.env?.OS_OIDC_PROVIDER_ENABLED;\n const oidcFromEnv = oidcEnv != null ? String(oidcEnv).toLowerCase() === 'true' : undefined;\n const oidcEnabled = oidcFromEnv ?? this.options.plugins?.oidcProvider ?? false;\n if (oidcEnabled) {\n void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {\n ctx.logger.error('Failed to register OIDC discovery routes', error as Error);\n });\n }\n\n ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);\n }\n\n /**\n * Mount the OIDC / OAuth 2.0 well-known discovery documents at the root\n * URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the\n * documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`\n * relative to the issuer, not under the auth basePath.\n */\n private async registerOidcDiscoveryRoutes(rawApp: any, ctx: PluginContext): Promise<void> {\n const auth = await this.authManager!.getAuthInstance();\n const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import(\n '@better-auth/oauth-provider'\n );\n\n const authServerHandler = oauthProviderAuthServerMetadata(auth as any);\n const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth as any);\n\n // Cache-Control for OIDC discovery docs. These describe stable issuer\n // configuration (endpoints, supported scopes, signing algs); they\n // change only on app redeploy. CF edge can short-circuit repeated\n // fetches and dramatically cut SSO first-call latency.\n const DISCOVERY_CACHE = 'public, max-age=300, stale-while-revalidate=86400';\n const withDiscoveryCache = async (handler: (req: Request) => Promise<Response> | Response, req: Request): Promise<Response> => {\n const resp = await handler(req);\n try {\n if (resp.ok && !resp.headers.get('cache-control')) {\n resp.headers.set('cache-control', DISCOVERY_CACHE);\n }\n } catch { /* best-effort */ }\n return resp;\n };\n\n rawApp.get('/.well-known/oauth-authorization-server', (c: any) => withDiscoveryCache(authServerHandler, c.req.raw));\n rawApp.get('/.well-known/openid-configuration', (c: any) => withDiscoveryCache(openidConfigHandler, c.req.raw));\n\n ctx.logger.info(\n 'OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}',\n );\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Auth, BetterAuthOptions } from 'better-auth';\n// better-auth value imports (betterAuth + plugins) are deferred via dynamic\n// import() in getOrCreateAuth() / buildPluginList() so that disabled plugins\n// never get loaded into the process. See Stage 2F (RSS investigation).\nimport type {\n AuthConfig,\n EmailAndPasswordConfig,\n AuthPluginConfig,\n OidcProvidersConfig,\n} from '@objectstack/spec/system';\nimport type { IDataEngine } from '@objectstack/core';\nimport type { IEmailService } from '@objectstack/spec/contracts';\nimport { readEnvWithDeprecation, resolveMultiOrgEnabled, resolveOrgLimit } from '@objectstack/types';\nimport { mapMembershipRole, BUILTIN_ROLE_PLATFORM_ADMIN } from '@objectstack/spec';\nimport { createObjectQLAdapterFactory, withSystemReadContext } from './objectql-adapter.js';\nimport {\n AUTH_USER_CONFIG,\n AUTH_SESSION_CONFIG,\n AUTH_ACCOUNT_CONFIG,\n AUTH_VERIFICATION_CONFIG,\n buildOrganizationPluginSchema,\n buildTwoFactorPluginSchema,\n buildOauthProviderPluginSchema,\n buildDeviceAuthorizationPluginSchema,\n buildJwtPluginSchema,\n buildAdminPluginSchema,\n} from './auth-schema-config.js';\n\n/**\n * Detect WebContainer (StackBlitz) environment.\n *\n * WebContainer reports itself as Node.js but runs inside a browser. Several\n * Node APIs are polyfilled with significant behavioural differences — most\n * notably `node:async_hooks.AsyncLocalStorage`, whose `run()` does NOT\n * propagate the store across `await` boundaries the way Node's native\n * implementation does.\n */\nfunction isWebContainerRuntime(): boolean {\n if (typeof globalThis === 'undefined') return false;\n const proc = (globalThis as any).process;\n return (\n Boolean(proc?.versions?.webcontainer) ||\n Boolean(proc?.env?.SHELL?.includes?.('jsh')) ||\n Boolean(proc?.env?.STACKBLITZ)\n );\n}\n\n/**\n * Synchronous AsyncLocalStorage polyfill compatible with better-auth's\n * `requestStateAsyncStorage` slot.\n *\n * Behaviour:\n * - `run(store, fn)` sets the current store synchronously before invoking\n * `fn` and restores the previous store after `fn` (and any promise it\n * returns) settles.\n * - `getStore()` returns the current store.\n *\n * Why a polyfill is needed in WebContainer:\n * - WebContainer's `node:async_hooks` does not propagate ALS context through\n * `await`, so better-auth's `runWithRequestState(map, () => handler(req))`\n * wrap loses the store as soon as the call chain awaits anything (e.g.\n * the inner `customSession` → `getSession()` call). All endpoints that\n * read request-state via `defineRequestState()` then throw\n * \"No request state found\".\n *\n * Single-flight caveat:\n * - This polyfill is process-global, not async-context-local. In a real\n * server it could leak state across concurrent requests. That risk is\n * acceptable here because:\n * 1) It is only installed when WebContainer is detected (dev / preview\n * sandboxes that handle one request at a time).\n * 2) Each request still wraps the entire handler in `runWithRequestState`\n * with a fresh WeakMap, so the in-flight request always sees its own\n * store as long as nothing else mutates the slot mid-flight.\n */\nclass WebContainerRequestStateAsyncLocalStorage<T> {\n private current: T | undefined = undefined;\n\n run<R>(store: T, fn: () => R): R {\n const prev = this.current;\n this.current = store;\n try {\n const result = fn() as unknown;\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).finally(() => {\n this.current = prev;\n }) as unknown as R;\n }\n this.current = prev;\n return result as R;\n } catch (err) {\n this.current = prev;\n throw err;\n }\n }\n\n getStore(): T | undefined {\n return this.current;\n }\n}\n\n/**\n * Pre-populate better-auth's global `requestStateAsyncStorage` slot with the\n * synchronous polyfill when running inside WebContainer.\n *\n * Better-auth caches its AsyncLocalStorage instance on\n * `globalThis[Symbol.for('better-auth:global')].context.requestStateAsyncStorage`\n * the first time `ensureAsyncStorage()` runs (see\n * `@better-auth/core/dist/context/request-state.mjs`). By seeding that slot\n * BEFORE any better-auth code touches it, every call to\n * `runWithRequestState` / `getCurrentRequestState` — including the\n * `@better-auth/core` copy that `plugin-auth` imports directly and the copy\n * bundled with `better-auth` itself — share the same working polyfill.\n *\n * Outside WebContainer this is a no-op so production deployments keep\n * Node's native AsyncLocalStorage.\n */\nfunction installWebContainerRequestStatePolyfill(): void {\n if (!isWebContainerRuntime()) return;\n const sym = Symbol.for('better-auth:global');\n const g = globalThis as any;\n if (!g[sym]) {\n g[sym] = { version: '0.0.0-polyfill', epoch: 0, context: {} };\n }\n if (!g[sym].context) g[sym].context = {};\n if (!g[sym].context.requestStateAsyncStorage) {\n g[sym].context.requestStateAsyncStorage = new WebContainerRequestStateAsyncLocalStorage();\n // eslint-disable-next-line no-console\n console.warn(\n '[AuthManager] WebContainer detected: installed synchronous request-state polyfill ' +\n '(node:async_hooks AsyncLocalStorage does not propagate context across await in WebContainer).',\n );\n }\n}\n\nfunction readBooleanEnv(name: string, legacyName?: string): boolean | undefined {\n const env = (globalThis as any)?.process?.env as Record<string, string | undefined> | undefined;\n const raw = env?.[name] ?? (legacyName ? env?.[legacyName] : undefined);\n if (raw == null) return undefined;\n const normalized = String(raw).trim().toLowerCase();\n return !['0', 'false', 'off', 'no'].includes(normalized);\n}\n\nfunction readDisableSignUpEnv(): boolean | undefined {\n const signupEnabled = readBooleanEnv('OS_AUTH_SIGNUP_ENABLED');\n if (signupEnabled != null) return !signupEnabled;\n return readBooleanEnv('OS_DISABLE_SIGNUP');\n}\n\n/**\n * SSO-only (\"enforced\") login mode from the deployment env. Self-host ops set\n * `OS_AUTH_SSO_ONLY=true` to lock the team to the configured IdP (parity with\n * the `OS_DISABLE_SIGNUP` self-host knob). The cloud runtime drives the same\n * behaviour per-env via the `ssoOnlyMode` config field instead.\n */\nfunction readSsoOnlyEnv(): boolean | undefined {\n return readBooleanEnv('OS_AUTH_SSO_ONLY');\n}\n\n/**\n * Extended options for AuthManager\n */\nexport interface AuthManagerOptions extends Partial<AuthConfig> {\n /**\n * Better-Auth instance (for advanced use cases)\n * If not provided, one will be created from config\n */\n authInstance?: Auth<any>;\n\n /**\n * ObjectQL Data Engine instance\n * Required for database operations using ObjectQL instead of third-party ORMs\n */\n dataEngine?: IDataEngine;\n\n /**\n * Optional callback invoked AFTER an organization is created via better-auth's\n * `createOrganization` (the org-plugin `afterCreateOrganization` hook). Lets a\n * host stack run org-creation side effects that core `databaseHooks` can't —\n * better-auth's org-plugin models (`organization`/`member`) do NOT fire those.\n * The cloud control plane uses it to provision an org's born-with production\n * environment. Failure-isolated: org creation is never rolled back.\n */\n onOrganizationCreated?: (data: {\n organizationId: string;\n userId?: string;\n name?: string;\n slug?: string;\n }) => void | Promise<void>;\n\n /**\n * D5.1 — OIDC OP authorization gate (cloud-as-IdP app-assignment).\n * When set, it is called for an AUTHENTICATED subject on\n * `/oauth2/authorize` before an authorization code is issued, with the\n * subject + the requesting `clientId`. Return `false` to DENY (no code).\n * The cloud control plane uses it to require org-membership: a cloud user\n * may only obtain a code for an env client (`project_<envId>`) of an org\n * they belong to. Unset (open editions / self-host, where the OP is not a\n * multi-tenant issuer) = allow. Host is expected to fail CLOSED on error.\n */\n oidcAuthorizeGate?: (params: { userId: string; clientId: string }) => boolean | Promise<boolean>;\n\n /**\n * Base path for auth routes\n * Forwarded to better-auth's basePath option so it can match incoming\n * request URLs without manual path rewriting.\n * @default '/api/v1/auth'\n */\n basePath?: string;\n\n /**\n * OIDC / Generic OAuth2 providers for enterprise SSO.\n * Each entry is passed to better-auth's genericOAuth plugin.\n */\n oidcProviders?: OidcProvidersConfig;\n\n /**\n * Application-specific organization roles to register with Better-Auth's\n * organization plugin. Each name becomes a valid role for invitations and\n * member assignments without going through Better-Auth's default\n * `owner|admin|member` whitelist.\n *\n * The ObjectStack SecurityPlugin handles real RBAC enforcement by matching\n * these role names against `permission` metadata (PermissionSets / Profiles),\n * so Better-Auth only needs to accept them as opaque strings. Each role is\n * registered with the minimum access-control privileges (equivalent to\n * Better-Auth's `member` role) so it cannot inadvertently grant org-level\n * admin capabilities.\n *\n * Typical source: the union of `permission` metadata names that have\n * `isProfile: true`, collected from the loaded stack at CLI boot.\n *\n * @example ['sales_rep', 'sales_manager', 'service_agent']\n */\n additionalOrgRoles?: string[];\n\n /**\n * Optional outbound email service used by better-auth callbacks\n * (`sendResetPassword`, `sendVerificationEmail`, `sendInvitationEmail`,\n * `sendMagicLink`). When omitted, those callbacks degrade to logging\n * the action URL — keeping flows usable in pilots / local dev — but\n * production deployments SHOULD wire one via `setEmailService()`.\n *\n * Resolved lazily through {@link AuthManager.getEmailService}; safe\n * to set after construction. AuthPlugin wires this from the kernel\n * service registry on `kernel:ready`.\n */\n emailService?: IEmailService;\n\n /**\n * Display name used by built-in auth email templates (`{{appName}}`\n * placeholder). Defaults to `'ObjectStack'` when omitted.\n */\n appName?: string;\n\n /**\n * Pass-through to better-auth's `databaseHooks` option. better-auth fires\n * these around its own adapter writes (e.g. when `genericOAuth` creates\n * a JIT user during SSO login), which the kernel-level ObjectQL\n * middleware does NOT observe — better-auth's adapter goes through\n * `dataEngine` directly, bypassing the `ql.registerMiddleware` chain.\n *\n * The platform uses this to attach a `user.create.after` hook that\n * auto-provisions a personal organization for every newly-created user\n * (mirroring what SecurityPlugin's middleware does for direct\n * ObjectQL inserts) so SSO-arriving users don't land on the empty\n * \"create organization\" screen.\n */\n databaseHooks?: BetterAuthOptions['databaseHooks'];\n\n /**\n * ADR-0069 D2 — account lockout (anti-brute-force). After this many\n * consecutive failed sign-ins the account is locked for\n * {@link lockoutDurationMinutes}. `0` (default) disables lockout.\n * Enforced per-identity in the `/sign-in/email` before/after hooks\n * (survives IP rotation, unlike the per-IP {@link rateLimit}).\n */\n lockoutThreshold?: number;\n\n /** Minutes an account stays locked once the threshold is crossed. Default 15. */\n lockoutDurationMinutes?: number;\n\n /**\n * ADR-0069 D1 — password complexity. When `passwordRequireComplexity` is on,\n * a new password must contain at least `passwordMinClasses` (1-4) of the\n * character classes upper / lower / digit / symbol. Enforced by a validator\n * in the `/sign-up/email`, `/reset-password`, `/change-password` before hook\n * (better-auth only enforces min/max length natively).\n */\n passwordRequireComplexity?: boolean;\n\n /** Minimum distinct character classes required (1-4). Default 3. */\n passwordMinClasses?: number;\n\n /**\n * ADR-0069 D1 — password history depth. When > 0, a password change/reset is\n * rejected if the new password matches the current or any of the last\n * `passwordHistoryCount` hashes (`sys_account.previous_password_hashes`).\n * Reuses better-auth's native hash/verify — no bespoke crypto.\n */\n passwordHistoryCount?: number;\n\n /**\n * ADR-0069 D1 — password expiry (days). When > 0, an authenticated user whose\n * `sys_user.password_changed_at` is older than this is gated out of protected\n * resources (`PASSWORD_EXPIRED`) until they change their password. Computed in\n * `customSession` (→ `user.authGate`) and enforced at the transport seam. 0 =\n * off. A null `password_changed_at` never expires (existing users on upgrade).\n */\n passwordExpiryDays?: number;\n\n /**\n * ADR-0069 D3 — enforced MFA. When true, an authenticated user without TOTP\n * enrolled (`sys_user.two_factor_enabled`) is gated out of protected resources\n * (`MFA_REQUIRED`) once their grace window elapses, until they enroll. Shares\n * the `customSession` → `user.authGate` seam with password expiry.\n */\n mfaRequired?: boolean;\n\n /** Days a user may defer MFA enrollment before the hard block. Default 7. */\n mfaGracePeriodDays?: number;\n\n /**\n * ADR-0069 D4 — session controls. Enforced in `customSession` (idle/absolute)\n * and the sign-in hook (concurrent). 0 = off for each. A revoked session is\n * expired in place (`sys_session.expires_at` past + `revoked_at`/`revoke_reason`)\n * so better-auth returns no session on the next request (→ 401 → re-login).\n */\n sessionIdleTimeoutMinutes?: number;\n sessionAbsoluteMaxHours?: number;\n maxConcurrentSessions?: number;\n\n /**\n * ADR-0069 D5 — network gating. When non-empty, auth requests (sign-in,\n * session) from a client IP outside these CIDR / exact ranges are rejected\n * with `IP_NOT_ALLOWED` at the auth-route middleware. Requires a trusted proxy\n * to set `x-forwarded-for` / `cf-connecting-ip`; fails OPEN when the client IP\n * can't be determined (so a missing proxy header is a no-op, not a lockout).\n */\n allowedIpRanges?: string[];\n\n /**\n * ADR-0069 D2 — better-auth-native per-IP rate limiting, passed through to\n * better-auth's core `rateLimit`. The settings bind tightens `customRules`\n * for the auth endpoints (`/sign-in/email`, `/sign-up/email`,\n * `/reset-password`). Multi-node deployments need a shared `storage`.\n */\n rateLimit?: BetterAuthOptions['rateLimit'];\n}\n\n/**\n * Authentication Manager\n *\n * Wraps better-auth and provides authentication services for ObjectStack.\n * Supports multiple authentication methods:\n * - Email/password\n * - OAuth providers (Google, GitHub, etc.)\n * - Magic links\n * - Two-factor authentication\n * - Passkeys\n * - Organization/teams\n */\n/** ADR-0069 D5 — parse a dotted-quad IPv4 to a uint32, or null when not IPv4. */\nfunction ipv4ToInt(ip: string): number | null {\n const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(ip.trim());\n if (!m) return null;\n const p = [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];\n if (p.some((n) => n > 255)) return null;\n return (((p[0] << 24) >>> 0) + (p[1] << 16) + (p[2] << 8) + p[3]) >>> 0;\n}\n\n/** ADR-0069 D5 — does `ip` match `range` (IPv4 CIDR `a.b.c.d/n`, or exact IP)? */\nexport function ipMatchesRange(ip: string, range: string): boolean {\n const r = (range || '').trim();\n if (!r) return false;\n if (r.includes('/')) {\n const [base, bitsStr] = r.split('/');\n const bits = Number(bitsStr);\n const ipInt = ipv4ToInt(ip);\n const baseInt = ipv4ToInt(base);\n if (ipInt === null || baseInt === null || !(bits >= 0 && bits <= 32)) {\n return ip.trim() === base.trim(); // non-IPv4-CIDR → exact-match fallback\n }\n const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0;\n return ((ipInt & mask) >>> 0) === ((baseInt & mask) >>> 0);\n }\n return ip.trim() === r;\n}\n\nexport class AuthManager {\n private auth: Auth<any> | null = null;\n private config: AuthManagerOptions;\n // ADR-0069 — cached \"does any org require MFA\" flag (per-org tightening).\n // Refreshed lazily with a TTL so isAuthGateActive() stays synchronous + cheap.\n private _orgMfaCache: { value: boolean; at: number } = { value: false, at: 0 };\n private _orgMfaRefreshing = false;\n\n /**\n * Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`\n * when it provisions the well-known admin on an empty DB). The `serve`\n * command reads this after boot to surface the credentials in the startup\n * banner. Undefined when no seed ran (production, opt-out, or a DB that\n * already had a user).\n */\n public devSeedResult?: { email: string; password: string };\n\n constructor(config: AuthManagerOptions) {\n this.config = config;\n\n // WebContainer (StackBlitz) compatibility — install a synchronous\n // AsyncLocalStorage polyfill for better-auth's request-state global\n // BEFORE better-auth ever instantiates its own. See the helper for the\n // full rationale.\n installWebContainerRequestStatePolyfill();\n\n // Use provided auth instance\n if (config.authInstance) {\n this.auth = config.authInstance;\n }\n // Don't create auth instance automatically to avoid database initialization errors\n // It will be created lazily when needed\n }\n\n /**\n * Get or create the better-auth instance (lazy initialization)\n */\n private async getOrCreateAuth(): Promise<Auth<any>> {\n if (!this.auth) {\n this.auth = await this.createAuthInstance();\n }\n return this.auth;\n }\n\n /**\n * Create a better-auth instance from configuration\n */\n private async createAuthInstance(): Promise<Auth<any>> {\n const { betterAuth } = await import('better-auth');\n const { createAuthMiddleware } = await import('better-auth/api');\n const plugins = await this.buildPluginList();\n const passwordHasher = await this.resolvePasswordHasher();\n const betterAuthConfig: BetterAuthOptions = {\n // Base configuration\n secret: this.config.secret || this.generateSecret(),\n baseURL: this.config.baseUrl || 'http://localhost:3000',\n basePath: this.config.basePath || '/api/v1/auth',\n\n // Database adapter configuration\n database: this.createDatabaseConfig(),\n\n // Model/field mapping: camelCase (better-auth) → snake_case (ObjectStack)\n // These declarations tell better-auth the actual table/column names used\n // by ObjectStack's protocol layer, enabling automatic transformation via\n // createAdapterFactory.\n user: {\n ...AUTH_USER_CONFIG,\n // NOTE: the env-side AI-seat marker `sys_user.ai_access` is deliberately\n // NOT declared as a better-auth additionalField. sys_user is a\n // better-auth-MANAGED table and better-auth SELECTs explicit columns, so\n // declaring it here would make getSession query a column that may not\n // exist on every env yet → broken auth. Instead the column is owned by\n // the objectql `SysUser` object def (provisioned by boot schema-sync)\n // and read by a GUARDED system query in resolveCtx (can only no-op,\n // never break auth). better-auth stays oblivious to the extra column.\n },\n account: {\n ...AUTH_ACCOUNT_CONFIG,\n // Allow OIDC/OAuth callbacks to implicitly link the incoming\n // identity to a pre-existing local user when the emails match.\n //\n // ObjectStack's platform SSO (\"objectstack-cloud\" provider) is the\n // canonical case: cloud is the IdP for every project, so a user\n // arriving via SSO is — by construction — the same person who was\n // auto-seeded as the project owner when the project was created.\n // Without trusting the provider, better-auth's safety check rejects\n // the link with `error=account_not_linked` because the seeded user\n // row has `emailVerified=false` (no actual verification ever runs\n // in the IdP-mediated flow). See packages/plugins/plugin-auth/\n // node_modules/better-auth/dist/oauth2/link-account.mjs:22.\n //\n // Custom-deployment consumers can extend the trusted set via\n // `config.account.accountLinking.trustedProviders`; we always\n // include `objectstack-cloud` because it is the platform IdP.\n accountLinking: {\n enabled: true,\n // better-auth's account-linking gate has TWO independent clauses\n // (see link-account.mjs:22). Trusting the provider only satisfies\n // the first clause; the second — `requireLocalEmailVerified &&\n // !dbUser.user.emailVerified` — still blocks linking when the\n // pre-existing local user row has `emailVerified=false` (the\n // default for owner-seeded rows). Disabling the local-email gate\n // is safe here because the OAuth side is what we actually trust:\n // the incoming identity was verified by the IdP. Consumers who\n // need the stricter behavior can override via config.\n requireLocalEmailVerified: false,\n ...((this.config as any)?.account?.accountLinking ?? {}),\n trustedProviders: Array.from(new Set([\n 'objectstack-cloud',\n ...((this.config as any)?.account?.accountLinking?.trustedProviders ?? []),\n ])),\n },\n },\n verification: {\n ...AUTH_VERIFICATION_CONFIG,\n },\n\n // Social / OAuth providers\n ...(this.config.socialProviders ? { socialProviders: this.config.socialProviders as any } : {}),\n\n // Email and password configuration.\n // `disableSignUp`: env overrides config/settings so deployments can\n // lock the registration policy without relying on UI state.\n emailAndPassword: (() => {\n const disableSignUpFromEnv = readDisableSignUpEnv();\n // SSO-only (\"enforced\") forces self-registration off (the managed team\n // signs in via the IdP). `enabled` stays true so the break-glass\n // password endpoint keeps working for the env owner / local admin.\n const effectiveDisableSignUp = this.resolveSsoOnly()\n ? true\n : (disableSignUpFromEnv ?? this.config.emailAndPassword?.disableSignUp);\n return {\n enabled: this.config.emailAndPassword?.enabled ?? true,\n ...(passwordHasher ? { password: passwordHasher } : {}),\n ...(effectiveDisableSignUp != null\n ? { disableSignUp: effectiveDisableSignUp } : {}),\n ...(this.config.emailAndPassword?.requireEmailVerification != null\n ? { requireEmailVerification: this.config.emailAndPassword.requireEmailVerification } : {}),\n ...(this.config.emailAndPassword?.minPasswordLength != null\n ? { minPasswordLength: this.config.emailAndPassword.minPasswordLength } : {}),\n ...(this.config.emailAndPassword?.maxPasswordLength != null\n ? { maxPasswordLength: this.config.emailAndPassword.maxPasswordLength } : {}),\n ...(this.config.emailAndPassword?.resetPasswordTokenExpiresIn != null\n ? { resetPasswordTokenExpiresIn: this.config.emailAndPassword.resetPasswordTokenExpiresIn } : {}),\n ...(this.config.emailAndPassword?.autoSignIn != null\n ? { autoSignIn: this.config.emailAndPassword.autoSignIn } : {}),\n ...(this.config.emailAndPassword?.revokeSessionsOnPasswordReset != null\n ? { revokeSessionsOnPasswordReset: this.config.emailAndPassword.revokeSessionsOnPasswordReset } : {}),\n sendResetPassword: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n // No transport wired but password reset is enabled — a\n // misconfiguration. THROW (don't silently drop): better-auth\n // invokes this via `runInBackgroundOrAwait` and the forget-password\n // route always returns `{status:true}`, so this never leaks whether\n // an address exists AND never turns the request into a 500 — it just\n // surfaces the failure in the logs instead of vanishing.\n throw new Error(\n `Password-reset email could not be sent to ${user.email}: no email service is configured for this deployment.`,\n );\n }\n const ttlSec = this.config.emailAndPassword?.resetPasswordTokenExpiresIn ?? 60 * 60;\n // Surface both template-resolution throws and transport failures\n // (status:'failed'); resilience is preserved by better-auth's\n // background-task handling (see sendVerificationEmail) and the\n // forget-password route always returns {status:true}, so this never\n // leaks whether an address exists nor turns the request into a 500.\n const result = await email.sendTemplate({\n template: 'auth.password_reset',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n resetUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n if (result?.status === 'failed') {\n throw new Error(\n `Password-reset email could not be sent to ${user.email}: ${result.error ?? 'delivery failed'}`,\n );\n }\n },\n };\n })(),\n\n // Email verification\n ...(this.config.emailVerification || this.config.emailService ? {\n emailVerification: {\n ...(this.config.emailVerification?.sendOnSignUp != null\n ? { sendOnSignUp: this.config.emailVerification.sendOnSignUp } : {}),\n ...(this.config.emailVerification?.sendOnSignIn != null\n ? { sendOnSignIn: this.config.emailVerification.sendOnSignIn } : {}),\n ...(this.config.emailVerification?.autoSignInAfterVerification != null\n ? { autoSignInAfterVerification: this.config.emailVerification.autoSignInAfterVerification } : {}),\n ...(this.config.emailVerification?.expiresIn != null\n ? { expiresIn: this.config.emailVerification.expiresIn } : {}),\n sendVerificationEmail: async ({ user, url, token }: { user: { id: string; email: string; name?: string }; url: string; token: string }) => {\n const email = this.getEmailService();\n if (!email) {\n // Verification is enabled (this callback only exists when it is)\n // but no email transport is wired — a misconfiguration, not a\n // transient blip. THROW so the explicit `/send-verification-email`\n // resend endpoint (which awaits this) surfaces a real error\n // instead of a false \"email sent\" success. Sign-up stays\n // resilient regardless: better-auth runs the sendOnSignUp call\n // through `runInBackgroundOrAwait`, which logs (never rethrows)\n // a failure, so the account is still created and the user lands\n // on the verify screen (where an honest resend now reports the\n // problem). Previously this was swallowed, leaving every user\n // permanently stuck with no signal and no resend that could work.\n throw new Error(\n `Verification email could not be sent to ${user.email}: no email service is configured for this deployment.`,\n );\n }\n const ttlSec = this.config.emailVerification?.expiresIn ?? 60 * 60;\n // Let send failures propagate (see above): sendTemplate THROWS on\n // template/loader errors, and returns status:'failed' on transport\n // errors — surface both so resend is honest and signup stays\n // resilient via better-auth's background-task error handling.\n const result = await email.sendTemplate({\n template: 'auth.verify_email',\n to: { address: user.email, ...(user.name ? { name: user.name } : {}) },\n data: {\n user: { name: user.name || user.email, email: user.email, id: user.id },\n verificationUrl: url,\n token,\n expiresInMinutes: Math.round(ttlSec / 60),\n appName: this.getAppName(),\n },\n relatedObject: 'sys_user',\n relatedId: user.id,\n });\n if (result?.status === 'failed') {\n throw new Error(\n `Verification email could not be sent to ${user.email}: ${result.error ?? 'delivery failed'}`,\n );\n }\n },\n },\n } : {}),\n\n // Session configuration\n session: {\n ...AUTH_SESSION_CONFIG,\n expiresIn: this.config.session?.expiresIn || 60 * 60 * 24 * 7, // 7 days default\n updateAge: this.config.session?.updateAge || 60 * 60 * 24, // 1 day default\n },\n\n // ADR-0069 D2 — per-IP rate limiting (native). Only set when configured\n // so better-auth keeps its own defaults otherwise. The settings bind\n // supplies stricter `customRules` for the auth endpoints.\n ...(this.config.rateLimit ? { rateLimit: this.config.rateLimit } : {}),\n\n // better-auth plugins — registered based on AuthPluginConfig flags\n plugins,\n\n // Database hooks (fired by better-auth's adapter writes — these run\n // for SSO JIT-provisioning too, unlike kernel-level ObjectQL\n // middleware which better-auth's adapter bypasses). The framework's\n // identity-source stamp (`account.create.after`) is always composed in,\n // preserving any host-supplied hooks.\n databaseHooks: this.composeDatabaseHooks(this.config.databaseHooks),\n\n // Bootstrap bypass for `disableSignUp`. The first-run owner wizard\n // (`/_account/setup`) calls `POST /auth/sign-up/email` to create\n // the very first user — if `OS_DISABLE_SIGNUP=true` is set on a\n // fresh install we'd lock the operator out of their own instance.\n // Solution: when the request hits `/sign-up/email` AND no users\n // exist yet, temporarily flip `disableSignUp` off for *this*\n // request's context. Once the owner is created the next request\n // sees `userCount > 0` and the toggle is enforced again.\n hooks: {\n before: createAuthMiddleware(async (ctx: any) => {\n // ── ADR-0069 D1: password complexity (validator) ────────────\n // better-auth enforces only min/max length; class-mix is custom.\n // Runs on the password-mutating endpoints; reads the candidate from\n // the path-appropriate body field (sign-up: `password`; reset /\n // change: `newPassword`).\n if (\n ctx?.path === '/sign-up/email' ||\n ctx?.path === '/reset-password' ||\n ctx?.path === '/change-password'\n ) {\n const candidate =\n (typeof ctx?.body?.password === 'string' && ctx.body.password) ||\n (typeof ctx?.body?.newPassword === 'string' && ctx.body.newPassword) ||\n '';\n if (candidate) await this.assertPasswordComplexity(candidate);\n\n // ── ADR-0069 D1: password history (reject reuse) ────────────\n // change/reset only (sign-up has no prior history). Reuses\n // better-auth's native password.verify — no bespoke crypto. Stashes\n // the old hash so the after-hook appends it to the bounded ring on\n // success.\n if (\n candidate &&\n (ctx?.path === '/reset-password' || ctx?.path === '/change-password')\n ) {\n const userId = await this.resolvePasswordChangeUserId(ctx).catch(() => undefined);\n if (userId) {\n // Stash for the after-hook (password_changed_at stamp), regardless\n // of whether history is enabled.\n ctx.context.__osPwChangeUserId = userId;\n const pw = ctx?.context?.password;\n const verify = typeof pw?.verify === 'function' ? pw.verify.bind(pw) : undefined;\n const oldHash = await this.assertPasswordNotReused(userId, candidate, verify);\n if (oldHash !== undefined) ctx.context.__osPwHistory = { userId, oldHash };\n }\n }\n // fall through to the path's own handling below\n }\n\n // ── ADR-0024: admin-gate self-service SSO provider registration ──\n // `@better-auth/sso`'s POST /sso/register only checks org-admin when\n // `body.organizationId` is present (index.mjs: `if (ctx.body\n // .organizationId) { … hasOrgAdminRole … }`). A GLOBAL (org-less)\n // provider therefore passes with nothing but a valid session — so any\n // authenticated member can register an env-wide external IdP, a JIT-\n // provisioning / login-routing vector. Require the caller to be a\n // platform admin OR an owner/admin of their active org, regardless of\n // whether `organizationId` is supplied. Unauthenticated requests fall\n // through to better-auth's `sessionMiddleware` (→ 401). Fail-CLOSED:\n // an unverifiable actor is denied. (D5.1's `/oauth2/authorize` gate is\n // a different surface — the OP issuing codes, not the env's RP config.)\n if (ctx?.path === '/sso/register') {\n const actor = await this.resolveActor(ctx);\n if (actor?.userId) {\n const ok = await this.isOrgOrPlatformAdmin(actor.userId, actor.activeOrgId);\n if (!ok) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n 'Only an organization owner/admin or a platform admin can ' +\n 'register an SSO provider.',\n code: 'SSO_REGISTER_FORBIDDEN',\n });\n }\n }\n return;\n }\n\n // ── D5.1: cloud-as-IdP authorization gate ───────────────────\n // On the OIDC OP's /oauth2/authorize, when a host gate is set\n // (cloud control plane), an AUTHENTICATED subject must be\n // authorized for the requesting client (env) before a code is\n // issued — this enforces org-membership (app-assignment). Unset\n // (open editions / self-host) → no gate. Unauthenticated → fall\n // through so the OP redirects to login; the gate runs on the\n // return pass (or immediately for a bearer/cookie session).\n if (ctx?.path === '/oauth2/authorize' && this.config.oidcAuthorizeGate) {\n const clientId = ctx?.query?.client_id;\n if (clientId) {\n let gateUserId: string | undefined;\n // (a) standard resolver — handles the cookie session.\n try {\n const { getSessionFromCtx } = await import('better-auth/api');\n const s: any = await getSessionFromCtx(ctx as any);\n gateUserId = s?.user?.id ?? s?.session?.userId;\n } catch { /* fall through to explicit resolution */ }\n // (b) explicit token resolution — hook-order-independent. The\n // bearer plugin may convert `Authorization: Bearer` to a session\n // AFTER this global before-hook, so getSessionFromCtx can miss a\n // bearer (or non-default cookie) request here. Resolve the token\n // (bearer or the session cookie's token part) and look it up.\n if (!gateUserId) {\n try {\n const hdr = (k: string): string =>\n ((ctx?.headers?.get?.(k) ?? ctx?.request?.headers?.get?.(k)) as string) || '';\n let token: string | undefined;\n const bm = /^Bearer\\s+(.+)$/i.exec(hdr('authorization'));\n if (bm?.[1]) token = bm[1].trim();\n if (!token) {\n const cm = /(?:^|;\\s*)(?:__Secure-|__Host-)?better-auth\\.session_token=([^;]+)/.exec(hdr('cookie'));\n if (cm?.[1]) token = decodeURIComponent(cm[1]).split('.')[0];\n }\n if (token) {\n const sess: any = await (ctx as any).context.adapter.findOne({\n model: 'session',\n where: [{ field: 'token', value: token }],\n });\n const exp = sess?.expiresAt ?? sess?.expires_at;\n if (sess && (!exp || new Date(exp).getTime() > Date.now())) {\n gateUserId = String(sess.userId ?? sess.user_id ?? '') || undefined;\n }\n }\n } catch { /* unresolved → fall through, OP handles auth */ }\n }\n if (gateUserId) {\n const allowed = await this.config.oidcAuthorizeGate({\n userId: gateUserId,\n clientId: String(clientId),\n });\n if (!allowed) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message: 'You are not authorized to sign in to this environment.',\n code: 'ENV_ACCESS_DENIED',\n });\n }\n }\n }\n return;\n }\n\n // ── Break-glass: never remove the LAST local-password login ──────\n // Under enforced SSO the managed team holds no local credential; the\n // env owner / a local admin keeps one as the break-glass escape hatch\n // so an IdP outage can never lock the org out. Refuse to delete or\n // ban the last user holding a `credential` account. Generic over the\n // IdP. Managed (credential-less) users are unaffected. Fail-open on\n // lookup hiccups (a transient query error must not block legit ops).\n if (\n ctx?.path === '/delete-user' ||\n ctx?.path === '/admin/remove-user' ||\n ctx?.path === '/admin/ban-user'\n ) {\n let isLastLocalCredential = false;\n try {\n const adapter = ctx.context.adapter;\n let targetId: string | undefined = ctx?.body?.userId ?? ctx?.body?.user_id;\n if (!targetId && ctx.path === '/delete-user') {\n const { getSessionFromCtx } = await import('better-auth/api');\n const s: any = await getSessionFromCtx(ctx as any).catch(() => null);\n targetId = s?.user?.id ?? s?.session?.userId;\n }\n if (targetId) {\n // Only guard when the target actually holds a local credential —\n // removing a credential-less (managed) user can't cause lockout.\n const targetCred = await adapter.findOne({\n model: 'account',\n where: [\n { field: 'userId', value: targetId },\n { field: 'providerId', value: 'credential' },\n ],\n });\n if (targetCred) {\n const creds: any[] = await adapter.findMany({\n model: 'account',\n where: [{ field: 'providerId', value: 'credential' }],\n });\n const otherHolders = new Set(\n (creds ?? [])\n .map((a: any) => a?.userId ?? a?.user_id)\n .filter((id: any) => id && id !== targetId),\n );\n isLastLocalCredential = otherHolders.size === 0;\n }\n }\n } catch {\n // Fail-open — never block a legitimate op on a lookup error.\n }\n if (isLastLocalCredential) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('CONFLICT', {\n message:\n 'Cannot remove the last local password login. At least one ' +\n 'break-glass account with a password must remain so an identity-' +\n 'provider outage can never lock the organization out. Add another ' +\n 'local password first, then retry.',\n code: 'LAST_LOCAL_CREDENTIAL',\n });\n }\n // fall through to better-auth's own handler\n }\n\n // ── ADR-0069 D2: account lockout (gate) ─────────────────────\n // Reject a sign-in for a locked identity BEFORE better-auth checks\n // the password — a lock must hold even against the correct password.\n if (ctx?.path === '/sign-in/email') {\n const email = typeof ctx?.body?.email === 'string' ? ctx.body.email : '';\n if (email) await this.assertAccountNotLocked(email);\n return;\n }\n\n if (ctx?.path !== '/sign-up/email') return;\n const ep = ctx?.context?.options?.emailAndPassword;\n if (!ep?.disableSignUp) return;\n try {\n const adapter = ctx.context.adapter;\n const existing = await adapter.findOne({ model: 'user', where: [] });\n if (!existing) {\n ctx.context.__osDisableSignUpOrig = ep.disableSignUp;\n ep.disableSignUp = false;\n }\n } catch {\n // Adapter not ready → keep disableSignUp on.\n }\n }),\n after: createAuthMiddleware(async (ctx: any) => {\n // ── ADR-0069 D2: account lockout (counter) ──────────────────\n // better-auth catches an INVALID_EMAIL_OR_PASSWORD APIError and runs\n // the after-hook with it on `ctx.context.returned`; a success leaves\n // the session payload there. Count failures, reset on success.\n if (ctx?.path === '/sign-in/email') {\n const email = typeof ctx?.body?.email === 'string' ? ctx.body.email : '';\n if (email) {\n let succeeded = true;\n try {\n const { isAPIError } = await import('better-auth/api');\n succeeded = !isAPIError(ctx?.context?.returned);\n } catch {\n succeeded = !(ctx?.context?.returned instanceof Error);\n }\n await this.recordSignInOutcome(email, succeeded);\n if (succeeded) {\n const uid = ctx?.context?.returned?.user?.id;\n if (typeof uid === 'string') await this.enforceConcurrentCap(uid);\n }\n }\n return;\n }\n\n // ── ADR-0069 D1: on a successful change/reset — stamp\n // password_changed_at (expiry) and commit password history.\n if (ctx?.path === '/change-password' || ctx?.path === '/reset-password') {\n let succeeded: boolean;\n try {\n const { isAPIError } = await import('better-auth/api');\n succeeded = !isAPIError(ctx?.context?.returned);\n } catch {\n succeeded = !(ctx?.context?.returned instanceof Error);\n }\n if (succeeded) {\n const stampId = ctx?.context?.__osPwChangeUserId;\n if (stampId) await this.stampPasswordChangedAt(stampId);\n const stash = ctx?.context?.__osPwHistory;\n if (stash?.userId) await this.recordPasswordHistory(stash.userId, stash.oldHash);\n }\n delete ctx.context.__osPwChangeUserId;\n delete ctx.context.__osPwHistory;\n return;\n }\n\n if (ctx?.path !== '/sign-up/email') return;\n // ADR-0069 D1 — stamp password_changed_at for a newly-created local\n // user (expiry clock starts at sign-up). Best-effort.\n {\n const newUserId = ctx?.context?.returned?.user?.id;\n let signupOk: boolean;\n try {\n const { isAPIError } = await import('better-auth/api');\n signupOk = !isAPIError(ctx?.context?.returned);\n } catch {\n signupOk = !(ctx?.context?.returned instanceof Error);\n }\n if (signupOk && typeof newUserId === 'string') await this.stampPasswordChangedAt(newUserId);\n }\n const ep = ctx?.context?.options?.emailAndPassword;\n if (ep && ctx.context.__osDisableSignUpOrig !== undefined) {\n ep.disableSignUp = ctx.context.__osDisableSignUpOrig;\n delete ctx.context.__osDisableSignUpOrig;\n }\n }),\n },\n\n // Trusted origins for CSRF protection (supports wildcards like \"https://*.example.com\")\n // Auto-includes origins from OS_CORS_ORIGIN env var so CORS and CSRF stay in sync.\n ...(() => {\n const origins: string[] = [...(this.config.trustedOrigins || [])];\n // Sync with OS_CORS_ORIGIN env var (comma-separated)\n const corsOrigin = readEnvWithDeprecation('OS_CORS_ORIGIN', 'CORS_ORIGIN', { silent: true });\n if (corsOrigin && corsOrigin !== '*') {\n corsOrigin.split(',').map(s => s.trim()).filter(Boolean).forEach(o => {\n if (!origins.includes(o)) origins.push(o);\n });\n }\n // When CORS allows all origins (default) and no explicit trustedOrigins,\n // trust all localhost ports in development for convenience. Also trust\n // `*.localhost` subdomains so per-project tenant subdomains (the dev\n // default root domain — see project-provisioning.ts) pass CSRF checks\n // without operators having to configure trustedOrigins manually.\n if (!origins.length && (!corsOrigin || corsOrigin === '*')) {\n origins.push('http://localhost:*');\n origins.push('http://*.localhost:*');\n origins.push('https://*.localhost:*');\n }\n // ── ADR-0024: runtime self-service external-IdP registration ───────\n // `@better-auth/sso`'s `validateDiscoveryUrl` requires the IdP's\n // *discovery* origin to be in `trustedOrigins` — even for a publicly-\n // routable IdP (stricter than its own sub-endpoint check, which allows\n // any public host). Without help that breaks ADR-0024's \"register your\n // IdP at runtime, no boot config\" promise for every real IdP\n // (Okta/Entra/Google). When the SSO RP is enabled, expose\n // `trustedOrigins` as a per-request FUNCTION that, for a\n // `/sso/register` | `/sso/update-provider` POST, additionally trusts the\n // PUBLIC-ROUTABLE issuer / discovery origins declared in the request\n // body. Private / internal hosts are never auto-trusted — they still\n // require explicit `trustedOrigins` config (the documented SSRF escape\n // hatch), and better-auth's own DNS-resolution checks still apply.\n if (this.isSsoWired()) {\n return {\n trustedOrigins: async (request?: Request) => {\n const base = [...origins];\n try {\n for (const o of await this.ssoDiscoveryTrustedOrigins(request)) {\n if (!base.includes(o)) base.push(o);\n }\n } catch { /* never let trust resolution throw */ }\n return base;\n },\n };\n }\n return origins.length ? { trustedOrigins: origins } : {};\n })(),\n\n // Advanced options (cross-subdomain cookies, secure cookies, CSRF, etc.)\n ...(this.config.advanced ? {\n advanced: {\n ...(this.config.advanced.crossSubDomainCookies\n ? { crossSubDomainCookies: this.config.advanced.crossSubDomainCookies } : {}),\n ...(this.config.advanced.useSecureCookies != null\n ? { useSecureCookies: this.config.advanced.useSecureCookies } : {}),\n ...(this.config.advanced.disableCSRFCheck != null\n ? { disableCSRFCheck: this.config.advanced.disableCSRFCheck } : {}),\n ...(this.config.advanced.cookiePrefix != null\n ? { cookiePrefix: this.config.advanced.cookiePrefix } : {}),\n },\n } : {}),\n };\n\n return betterAuth(betterAuthConfig);\n }\n\n /**\n * Detect WebContainer (StackBlitz) and swap in a pure-JS scrypt hasher.\n *\n * better-auth defaults to `@better-auth/utils/password.node`, which calls\n * `node:crypto.scrypt`. WebContainer polyfills that API incompletely and\n * signup throws `TypeError: y.run is not a function`.\n *\n * We can't dynamic-import `@better-auth/utils/password` because that\n * package's `exports` map gates the pure-JS build behind a non-`\"node\"`\n * condition — Node-the-runtime (which WebContainer reports itself as)\n * always resolves to `password.node.mjs`. So we reimplement the same hash\n * here using `@noble/hashes/scrypt` directly, with byte-identical params\n * (N=16384, r=16, p=1, dkLen=64) and the same `{saltHex}:{keyHex}` storage\n * format. Hashes produced by either implementation verify against the\n * other — no migration needed.\n *\n * Returns `undefined` outside WebContainer so production deployments keep\n * the native (fast) hasher and never load `@noble/hashes`.\n */\n private async resolvePasswordHasher(): Promise<\n { hash: (password: string) => Promise<string>; verify: (args: { hash: string; password: string }) => Promise<boolean> } | undefined\n > {\n if (!isWebContainerRuntime()) return undefined;\n try {\n const { scryptAsync } = await import('@noble/hashes/scrypt.js');\n const PARAMS = { N: 16384, r: 16, p: 1, dkLen: 64, maxmem: 128 * 16384 * 16 * 2 } as const;\n const toHex = (b: Uint8Array): string => {\n let s = '';\n for (let i = 0; i < b.length; i++) s += b[i]!.toString(16).padStart(2, '0');\n return s;\n };\n const generateKey = (password: string, saltHex: string): Promise<Uint8Array> =>\n scryptAsync(password.normalize('NFKC'), saltHex, PARAMS);\n return {\n hash: async (password: string) => {\n const saltBytes = (globalThis as any).crypto.getRandomValues(new Uint8Array(16));\n const saltHex = toHex(saltBytes);\n const key = await generateKey(password, saltHex);\n return `${saltHex}:${toHex(key)}`;\n },\n verify: async ({ hash, password }) => {\n const [saltHex, keyHex] = hash.split(':');\n if (!saltHex || !keyHex) throw new Error('Invalid password hash');\n const target = await generateKey(password, saltHex);\n return toHex(target) === keyHex;\n },\n };\n } catch (err: any) {\n console.warn(\n `[AuthManager] WebContainer detected but pure-JS scrypt unavailable: ${err?.message ?? err}. Falling back to default.`,\n );\n return undefined;\n }\n }\n\n /**\n * Build the list of better-auth plugins based on AuthPluginConfig flags.\n *\n * Each plugin that introduces its own database tables is configured with\n * a `schema` option containing the appropriate snake_case field mappings,\n * so that `createAdapterFactory` transforms them automatically.\n */\n private async buildPluginList(): Promise<any[]> {\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n const plugins: any[] = [];\n\n // Defaults — kept in sync with `AuthPluginConfigSchema` in\n // @objectstack/spec/system/auth-config.zod.ts. The frontend AuthProvider\n // (in @object-ui/app-shell) calls `/api/v1/auth/organization/list` on\n // every load; making the org plugin opt-out (default true) avoids\n // 404s and the noisy \"Failed to load organizations\" warning.\n //\n // `OS_OIDC_PROVIDER_ENABLED` lets operators flip the OIDC IdP on\n // without re-deploying with a code change (mirrors the\n // `OS_MULTI_ORG_ENABLED` / `OS_DISABLE_SIGNUP` pattern). When set, the\n // env var WINS over the config-file setting so platform operators can\n // override per-environment without touching the application bundle.\n // Use the shared `readBooleanEnv` parser (same as OS_AUTH_TWO_FACTOR /\n // OS_AUTH_PASSWORD_REJECT_BREACHED / OS_DISABLE_SIGNUP) so these accept the\n // platform-standard truthy set (`true`/`1`/`yes`/`on`, case-insensitive)\n // instead of only the literal string `'true'` — a repeated operator footgun\n // (`OS_SSO_ENABLED=1` silently parsed as disabled).\n const oidcFromEnv = readBooleanEnv('OS_OIDC_PROVIDER_ENABLED');\n const ssoFromEnv = readBooleanEnv('OS_SSO_ENABLED');\n const scimFromEnv = readBooleanEnv('OS_SCIM_ENABLED');\n // Opt-in DNS domain-verification for external SSO providers (ADR-0024 ②).\n // OFF by default → today's behavior exactly (register → login immediately).\n // ON → @better-auth/sso mounts /sso/{request-domain-verification,verify-domain}\n // AND enforces a HARD login gate: a provider whose domain is not DNS-verified\n // rejects logins (\"Provider domain has not been verified\"). The two are\n // coupled in @better-auth/sso (the endpoints only register when\n // `domainVerification.enabled`), so this single flag governs both. Requires\n // `OS_SSO_ENABLED` (the sso plugin must be loaded to honor it).\n const ssoDomainVerifyFromEnv = readBooleanEnv('OS_SSO_DOMAIN_VERIFICATION');\n // @better-auth/scim's `active:false` → ban runs through the admin plugin,\n // and org-scoped tokens need the organization plugin — so enabling SCIM\n // forces `admin` on (organization already defaults on). See ADR-0071.\n const scimEffective = scimFromEnv ?? (pluginConfig as any).scim ?? false;\n const twoFactorFromEnv = readBooleanEnv('OS_AUTH_TWO_FACTOR');\n const hibpFromEnv = readBooleanEnv('OS_AUTH_PASSWORD_REJECT_BREACHED');\n const enabled = {\n organization: pluginConfig.organization ?? true,\n twoFactor: twoFactorFromEnv ?? pluginConfig.twoFactor ?? false,\n passwordRejectBreached: hibpFromEnv ?? pluginConfig.passwordRejectBreached ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n oidcProvider: oidcFromEnv ?? pluginConfig.oidcProvider ?? false,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? scimEffective,\n sso: ssoFromEnv ?? (pluginConfig as any).sso ?? false,\n ssoDomainVerification: ssoDomainVerifyFromEnv ?? (pluginConfig as any).ssoDomainVerification ?? false,\n scim: scimEffective,\n };\n\n // bearer() — ALWAYS enabled.\n //\n // Enables token-based authentication for cross-origin and mobile clients\n // where third-party cookies are blocked (e.g. Safari ITP, Chrome CHIPS,\n // native apps). The plugin:\n // • Accepts `Authorization: Bearer <token>` on incoming requests and\n // transparently resolves the session as if a cookie had been sent.\n // • Emits a `set-auth-token` response header on sign-in / session-refresh\n // that the client can store (e.g. in `localStorage`) and replay on\n // subsequent requests.\n //\n // This mirrors how Salesforce, Notion, Supabase and first-party mobile\n // SDKs handle auth. Cookie-based auth remains available for same-origin\n // browser deployments; bearer is additive, not a replacement.\n const { bearer } = await import('better-auth/plugins/bearer');\n plugins.push(bearer());\n\n if (enabled.organization) {\n const { organization } = await import('better-auth/plugins/organization');\n // Build a `roles` map that registers each app-supplied org role\n // (e.g. CRM's sales_rep, sales_manager) as a valid Better-Auth role\n // so invitations to those roles aren't rejected with ROLE_NOT_FOUND.\n // Real RBAC enforcement is handled by ObjectStack's SecurityPlugin,\n // which matches the role name against `permission` metadata\n // (PermissionSets). Here we register them with minimum org-plugin\n // capabilities (same as the built-in `member` role) so they cannot\n // inadvertently grant org-level admin powers.\n let customOrgRoles: Record<string, any> | undefined;\n const extra = this.config.additionalOrgRoles;\n if (extra && extra.length > 0) {\n try {\n const accessMod = await import('better-auth/plugins/organization/access');\n const { defaultAc, memberAc, defaultRoles: importedDefaultRoles } = accessMod as any;\n // Better-Auth's `hasPermission` does `{...options.roles || defaultRoles}`\n // (precedence: `||` then spread). When we pass our own `roles`, the\n // built-in owner/admin/member are silently dropped, so even the org\n // owner loses `invitation:create` and every mutation 403s. We must\n // re-include the defaults alongside our extras.\n const defaultRoles = importedDefaultRoles || null;\n if (defaultAc && memberAc && typeof memberAc.statements === 'object') {\n const built: Record<string, any> = defaultRoles ? { ...defaultRoles } : {};\n const stmts = memberAc.statements;\n for (const name of extra) {\n if (!name) continue;\n if (built[name]) continue;\n built[name] = defaultAc.newRole(stmts);\n }\n customOrgRoles = built;\n }\n } catch {\n customOrgRoles = undefined;\n }\n }\n plugins.push(organization({\n schema: buildOrganizationPluginSchema(),\n // Enable the team sub-feature so the framework's `sys_team` /\n // `sys_team_member` tables (already declared in platform-objects)\n // are actually wired up to better-auth's CRUD endpoints\n // (`/organization/{create,update,remove,list}-team[s]` and\n // `/organization/{add,remove,list}-team-member[s]`). The Account\n // portal exposes a Teams page; without this flag those endpoints\n // 404 and the section silently breaks.\n teams: { enabled: true },\n // Without a mailer wired in framework, requiring email verification\n // before accepting invitations dead-ends every invite flow with\n // FORBIDDEN EMAIL_VERIFICATION_REQUIRED…. Default-off here keeps\n // the built-in /accept-invitation route usable for pilots; operators\n // who wire a real mailer can re-enable downstream.\n requireEmailVerificationOnInvitation: false,\n // Cap how many orgs a user can CREATE (OS_ORG_LIMIT). Counts only orgs\n // the user OWNS (role=owner) — never orgs they were merely invited into —\n // so a generous cap stops scripted org/free-env spam (each new org can\n // auto-provision a free environment on the cloud control plane) WITHOUT\n // ever blocking a collaborator who belongs to many orgs. Unset → no\n // limit (self-host default). Fail-open: if the count can't be taken we\n // allow creation rather than block a legitimate user on an infra hiccup.\n organizationLimit: async (user: { id?: string }) => {\n const limit = resolveOrgLimit();\n if (limit == null) return false;\n const engine = this.config.dataEngine;\n const uid = typeof user?.id === 'string' ? user.id : '';\n if (!engine || !uid) return false;\n try {\n // `sys_member` is tenant-scoped (organization_id). We need to count\n // the user's owned orgs ACROSS tenants, so read with the system\n // context (isSystem) to bypass org-scoping — otherwise the query\n // returns nothing and the limit never fires.\n const owned = await withSystemReadContext(engine).count('sys_member', {\n where: { user_id: uid, role: 'owner' },\n });\n return (owned ?? 0) >= limit;\n } catch {\n return false;\n }\n },\n ...(customOrgRoles ? { roles: customOrgRoles } : {}),\n // ── Slug-change guard ─────────────────────────────────────\n // An org's slug is baked into every env hostname at creation\n // time (see service-tenant `project-provisioning.ts`). Renaming\n // it while live envs exist would silently desync the URL from\n // the org identity. Block the change here; the cloud Console\n // surfaces this as an actionable error and points users to\n // `change_hostname` or archiving the env. Org `name` (display\n // label) is unaffected — only `slug` is guarded.\n //\n // We resolve the data engine lazily so non-cloud apps (which\n // never seed `sys_environment`) keep working: any lookup error\n // is treated as \"no envs to protect\".\n organizationHooks: {\n // Gate fresh organization creation behind the multi-org flag.\n // The plugin itself is always installed (so list/update/invite endpoints\n // keep responding); only the `create` operation is denied when the\n // deployment is provisioned in single-org mode. Resolution order:\n // `OS_MULTI_ORG_ENABLED` (default `'false'` → single-org /\n // per-env runtime).\n beforeCreateOrganization: async () => {\n if (!resolveMultiOrgEnabled()) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n 'Creating additional organizations is disabled on this deployment.',\n });\n }\n },\n // Run host-provided org-creation side effects (e.g. the cloud control\n // plane provisions the org's born-with production environment). The\n // org-plugin's models don't fire core databaseHooks, so this is the\n // only server-side seam for \"every org is born with its prod env\".\n // Failure-isolated: org creation must not roll back on a side-effect miss.\n afterCreateOrganization: async ({ organization, member, user }: any) => {\n const cb = this.config.onOrganizationCreated;\n if (typeof cb !== 'function') return;\n try {\n await cb({\n organizationId: organization?.id,\n userId: user?.id ?? member?.userId,\n name: organization?.name,\n slug: organization?.slug,\n });\n } catch (err: any) {\n console.warn('[auth] onOrganizationCreated callback failed:', err?.message ?? String(err));\n }\n },\n beforeUpdateOrganization: async ({ organization, member }: any) => {\n const newSlug = organization?.slug;\n const orgId = member?.organizationId;\n if (!newSlug || !orgId) return;\n\n const dataEngine = this.config.dataEngine as any;\n if (!dataEngine) return;\n\n let currentSlug: string | undefined;\n try {\n const current = await dataEngine.findOne('sys_organization', {\n where: { id: orgId },\n });\n currentSlug = current?.slug;\n } catch {\n return;\n }\n if (!currentSlug || currentSlug === newSlug) return;\n\n let activeEnvs = 0;\n try {\n const envs = await dataEngine.find('sys_environment', {\n where: { organization_id: orgId },\n });\n activeEnvs = (envs ?? []).filter(\n (e: any) => e?.status !== 'archived' && e?.status !== 'failed',\n ).length;\n } catch {\n return;\n }\n\n if (activeEnvs > 0) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n `Cannot change organization slug while ${activeEnvs} active ` +\n `environment(s) still reference it. Archive those environments ` +\n `or rename their hostnames first.`,\n });\n }\n },\n },\n // No mailer is wired in framework yet — log the accept URL so\n // operators / UI can fall back to copy-paste flows. Replace this\n // with a real mail integration when available.\n sendInvitationEmail: async ({ email: recipientEmail, invitation, organization: org, inviter }) => {\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Invitation email not configured. ` +\n `To: ${recipientEmail} (org: ${org?.name ?? invitation.organizationId}, ` +\n `role: ${invitation.role}, inviter: ${inviter?.user?.email ?? 'unknown'}) ` +\n `URL: ${acceptUrl}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.invitation',\n to: recipientEmail,\n data: {\n inviter: {\n name: inviter?.user?.name ?? inviter?.user?.email ?? 'A teammate',\n email: inviter?.user?.email ?? '',\n },\n organization: { name: org?.name ?? invitation.organizationId },\n role: invitation.role || '',\n acceptUrl,\n appName: this.getAppName(),\n },\n relatedObject: 'sys_invitation',\n relatedId: invitation.id,\n });\n } catch (err: any) {\n // Do NOT rethrow: the invitation row was already persisted by\n // better-auth; an email-transport failure must not turn the\n // invite request into a 500. The admin can resend the invite.\n console.error(`[AuthManager] sendInvitationEmail failed (swallowed): ${err?.message ?? err}`);\n }\n },\n }));\n }\n\n if (enabled.twoFactor) {\n const { twoFactor } = await import('better-auth/plugins/two-factor');\n plugins.push(twoFactor({\n schema: buildTwoFactorPluginSchema(),\n }));\n }\n\n // Breached-password rejection (ADR-0069 D1). Native, stateless: a\n // k-anonymity range check against Have I Been Pwned on better-auth's\n // password-mutating endpoints (sign-up / change / reset — the plugin's\n // defaults). The plaintext password is never sent; only the first 5 SHA-1\n // hex chars leave the process. Rejects with PASSWORD_COMPROMISED.\n if (enabled.passwordRejectBreached) {\n const { haveIBeenPwned } = await import('better-auth/plugins/haveibeenpwned');\n plugins.push(haveIBeenPwned({\n customPasswordCompromisedMessage:\n 'This password has appeared in a known data breach. Please choose a different one.',\n }));\n }\n\n if (enabled.admin) {\n const { admin } = await import('better-auth/plugins/admin');\n // Platform admin: ban/unban, set-password, impersonate, set-role.\n // Schema mapping ensures the plugin's added user/session columns\n // match ObjectStack's snake_case conventions (ban_reason,\n // ban_expires, impersonated_by). `role` and `banned` are already\n // snake_case-compatible.\n plugins.push(admin({\n schema: buildAdminPluginSchema(),\n }));\n }\n\n if (enabled.magicLink) {\n const { magicLink } = await import('better-auth/plugins/magic-link');\n // magic-link reuses the `verification` table — no extra schema mapping needed.\n plugins.push(magicLink({\n sendMagicLink: async ({ email: recipientEmail, url, token }) => {\n const emailService = this.getEmailService();\n if (!emailService) {\n console.warn(\n `[AuthManager] Magic-link requested for ${recipientEmail} but no email service is wired. URL: ${url}`,\n );\n return;\n }\n try {\n await emailService.sendTemplate({\n template: 'auth.magic_link',\n to: recipientEmail,\n data: {\n magicLinkUrl: url,\n token,\n expiresInMinutes: 10,\n appName: this.getAppName(),\n },\n });\n } catch (err: any) {\n console.error(`[AuthManager] sendMagicLink failed: ${err?.message ?? err}`);\n throw err;\n }\n },\n }));\n }\n\n // OIDC / Generic OAuth2 providers (enterprise SSO via genericOAuth plugin)\n if (this.config.oidcProviders?.length) {\n const { genericOAuth } = await import('better-auth/plugins/generic-oauth');\n plugins.push(genericOAuth({\n config: this.config.oidcProviders.map(p => ({\n providerId: p.providerId,\n ...(p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {}),\n ...(p.issuer ? { issuer: p.issuer } : {}),\n ...(p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {}),\n ...(p.tokenUrl ? { tokenUrl: p.tokenUrl } : {}),\n ...(p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {}),\n clientId: p.clientId,\n clientSecret: p.clientSecret,\n ...(p.scopes ? { scopes: p.scopes } : {}),\n ...(p.pkce != null ? { pkce: p.pkce } : {}),\n })),\n }));\n }\n\n // OAuth/OIDC Provider — turn this server into an OpenID Connect Identity\n // Provider so external apps can SSO via ObjectStack. Adds the\n // `/oauth2/{authorize,token,userinfo,register,consent,endsession}` and\n // `/.well-known/openid-configuration` endpoints under the auth route.\n //\n // Migrated from the deprecated `better-auth/plugins/oidc-provider` to the\n // standalone `@better-auth/oauth-provider` package. The new plugin uses\n // `oauthClient`, `oauthAccessToken`, `oauthRefreshToken`, and `oauthConsent`\n // models — see `buildOauthProviderPluginSchema()` for the snake_case\n // mappings to ObjectStack's `sys_oauth_*` tables.\n if (enabled.oidcProvider) {\n // The new @better-auth/oauth-provider package requires the `jwt`\n // plugin (used to sign id_tokens / JWT access tokens). Register it\n // automatically — it is otherwise an internal implementation detail\n // and forcing every consumer to opt in would be poor DX.\n const { jwt } = await import('better-auth/plugins');\n plugins.push(jwt({ schema: buildJwtPluginSchema() }));\n\n const { oauthProvider } = await import('@better-auth/oauth-provider');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const uiBase = (this.config.uiBasePath ?? '/_console').replace(/\\/$/, '');\n plugins.push(oauthProvider({\n // Console SPA renders both pages (replaces the legacy Account SPA at\n // /_account). Override `uiBasePath` in AuthConfig if Console is\n // mounted elsewhere.\n loginPage: `${baseUrl}${uiBase}/login`,\n consentPage: `${baseUrl}${uiBase}/oauth/consent`,\n schema: buildOauthProviderPluginSchema(),\n }));\n }\n\n // External SSO (OIDC / SAML) relying-party — lets this environment federate\n // login to a customer's own IdP (Okta / Entra / Google …). Per-env, runtime-\n // registered providers live in `sys_sso_provider` (ADR-0024: the OPEN SSO\n // mechanism — cloud-free for self-host). Endpoints mount under\n // /api/v1/auth/sso/{register,providers,delete-provider,callback,…}.\n //\n // Toggle with `OS_SSO_ENABLED` (mirrors `OS_OIDC_PROVIDER_ENABLED`).\n if (enabled.sso) {\n const { sso } = await import('@better-auth/sso');\n // NOTE: unlike `oauthProvider`, @better-auth/sso hardcodes its `ssoProvider`\n // model and accepts NO `schema` option (verified against 1.6.20 — no\n // mergeSchema, runtime never reads options.schema). Its table mapping to\n // `sys_sso_provider` must therefore be resolved by the better-auth adapter\n // / a global model map, not per-plugin here (see AUTH_SSO_PROVIDER_SCHEMA).\n //\n // `organizationProvisioning.defaultRole` (ADR-0024 V1): a first-time\n // federated login is JIT-provisioned into the user's domain-matched org\n // with this role, so a member who arrives via an external IdP lands with\n // an explicit default role (belt-and-suspenders over SecurityPlugin's\n // `member_default` fallback, which already grants baseline access to any\n // authenticated user). Requires the `organization` plugin — on by default.\n // `domainVerification.enabled` (ADR-0024 ②, opt-in via OS_SSO_DOMAIN_VERIFICATION):\n // when on, @better-auth/sso mounts /sso/request-domain-verification +\n // /sso/verify-domain (DNS TXT proof-of-ownership) AND enforces that an\n // external IdP's email domain be DNS-verified before it may complete a\n // login — preventing an org admin from registering a provider for a domain\n // they don't control. Off by default to preserve the register→login flow.\n plugins.push(sso({\n organizationProvisioning: { defaultRole: 'member' },\n ...(enabled.ssoDomainVerification ? { domainVerification: { enabled: true } } : {}),\n }));\n }\n\n // External SCIM 2.0 Service Provider (@better-auth/scim, MIT) — lets an\n // external IdP (Okta / Entra) auto-provision / deprovision THIS env's users\n // (the paid Identity lifecycle, ADR-0071). The env is the SCIM Service\n // Provider; endpoints mount under /api/v1/auth/scim/v2/{Users,…} (SCIM 2.0)\n // and /api/v1/auth/scim/{generate-token,…} (management). `active:false` →\n // ban + session revoke (needs the admin plugin, forced on above); org-scoped\n // tokens need the organization plugin. Like @better-auth/sso it hardcodes\n // its `scimProvider` model (no schema option) — bridged to `sys_scim_provider`\n // via AUTH_MODEL_TO_PROTOCOL. Toggle with `OS_SCIM_ENABLED`.\n //\n // storeSCIMToken: 'hashed' — never persist the bearer in cleartext; the\n // plaintext is returned exactly once from generate-token (for the IdP admin).\n if (enabled.scim) {\n const { scim } = await import('@better-auth/scim');\n plugins.push(scim({ storeSCIMToken: 'hashed' }));\n }\n\n // Device Authorization Grant (RFC 8628) — for CLI / TV-style devices.\n // Exposes the standard `/device/{code,token,approve,deny}` endpoints\n // and persists pending requests in `sys_device_code`.\n //\n // The verification URI points at the account portal page that lets a\n // signed-in user approve or deny a pending CLI login. The page reads\n // the `user_code` query parameter that better-auth appends to\n // `verification_uri_complete`.\n if (enabled.deviceAuthorization) {\n const { deviceAuthorization } = await import('better-auth/plugins/device-authorization');\n const baseUrl = (this.config.baseUrl ?? '').replace(/\\/$/, '');\n const uiBase = (this.config.uiBasePath ?? '/_console').replace(/\\/$/, '');\n plugins.push(deviceAuthorization({\n verificationUri: `${baseUrl}${uiBase}/auth/device`,\n schema: buildDeviceAuthorizationPluginSchema(),\n }));\n }\n\n // customSession() — augments the session payload with a derived `role`\n // field so frontend gating (e.g. AppShell's `isAdmin = user.role === 'admin'`)\n // works without each consumer having to re-query permission sets.\n //\n // It also returns a `roles: string[]` array: the stored `user.role`\n // string split on commas (the admin plugin stores multi-role users as\n // e.g. `\"admin,manager\"`), with `'admin'` appended (deduplicated) when\n // the user is promoted below. Consumers that match on individual role\n // names (e.g. the Console approvals inbox resolving `role:<name>`\n // approvers) must read `roles` — `user.role` is *replaced* by the\n // literal `'admin'` on promotion, so business roles such as `manager`\n // only survive in the array.\n //\n // Better-auth's `sys_user` table doesn't carry a `role` column. We derive\n // it from two sources:\n //\n // 1. **Platform admin** — a `sys_user_permission_set` row that points at\n // the `admin_full_access` permission set with `organization_id = null`\n // (seeded by `bootstrapPlatformAdmin`).\n // 2. **Organization admin** — a `sys_member` row in the user's *active*\n // organization (`session.activeOrganizationId`) with role `owner` or\n // `admin`. Org owners/admins are entitled to manage org-scoped\n // metadata such as saved list views, dashboards, etc.\n //\n // ADR-0068 D2: rather than synthesizing `user.role = 'admin'`, both paths now\n // contribute CANONICAL role names to `user.roles` (platform_admin / org_*),\n // and `user.isPlatformAdmin` is a derived alias. The raw membership role\n // remains available via the `organization` plugin's `member` payload.\n const dataEngine = this.config.dataEngine;\n if (dataEngine) {\n const { customSession } = await import('better-auth/plugins/custom-session');\n plugins.push(customSession(async ({ user, session }) => {\n if (!user?.id) return { user, session };\n\n const isPlatformAdmin = async (): Promise<boolean> => {\n try {\n const links = await dataEngine.find('sys_user_permission_set', {\n where: { user_id: user.id },\n limit: 50,\n });\n const platformLinks = (Array.isArray(links) ? links : []).filter(\n (l: any) => !l.organization_id,\n );\n if (platformLinks.length === 0) return false;\n const sets = await dataEngine.find('sys_permission_set', { limit: 50 });\n const adminSet = (Array.isArray(sets) ? sets : []).find(\n (r: any) => r.name === 'admin_full_access',\n );\n if (!adminSet) return false;\n return platformLinks.some(\n (l: any) => l.permission_set_id === adminSet.id,\n );\n } catch {\n return false;\n }\n };\n\n // ADR-0068 D2 — surface CANONICAL org_* role names (not a boolean flag):\n // a membership owner/admin/member maps to org_owner/org_admin/org_member.\n const activeOrgRoles = async (): Promise<string[]> => {\n try {\n const orgId = (session as any)?.activeOrganizationId;\n if (!orgId) return [];\n const members = await dataEngine.find('sys_member', {\n where: { user_id: user.id, organization_id: orgId },\n limit: 5,\n });\n const out: string[] = [];\n for (const m of (Array.isArray(members) ? members : [])) {\n const raw = typeof m?.role === 'string' ? m.role : '';\n for (const r of raw.split(',').map((s: string) => s.trim()).filter(Boolean)) {\n const mapped = mapMembershipRole(r);\n if (!out.includes(mapped)) out.push(mapped);\n }\n }\n return out;\n } catch {\n return [];\n }\n };\n\n // ADR-0068 D1/D2 — emit ONE canonical roles[] (identities-as-roles), with\n // NO `role:'admin'` overwrite. isPlatformAdmin is a DERIVED alias of\n // `'platform_admin' in roles`, retained for back-compat clients.\n const platformAdmin = await isPlatformAdmin();\n const orgRoles = await activeOrgRoles();\n const storedRole = typeof (user as any).role === 'string' ? (user as any).role : '';\n const roles = Array.from(new Set([\n ...storedRole.split(',').map((s: string) => s.trim()).filter(Boolean),\n ...orgRoles,\n ...(platformAdmin ? [BUILTIN_ROLE_PLATFORM_ADMIN] : []),\n ]));\n\n // ADR-0069 — authentication-policy gate posture (password expiry,\n // enforced MFA). Computed only when a gate feature is enabled (else\n // zero extra reads on the hot path); surfaced as `user.authGate` for\n // the transport seams to enforce. See computeAuthGate().\n // ADR-0069 D4 — session controls (idle / absolute). Best-effort;\n // revokes the session in place when exceeded (next request → 401).\n await this.enforceSessionControls((session as any)?.id, (session as any)?.createdAt);\n\n const authGate = await this.computeAuthGate(\n user.id,\n (session as any)?.activeOrganizationId,\n (user as any)?.twoFactorEnabled === true,\n );\n\n return {\n user: { ...user, roles, isPlatformAdmin: platformAdmin, ...(authGate ? { authGate } : {}) },\n session,\n };\n }));\n }\n\n return plugins;\n }\n\n /**\n * Create database configuration using ObjectQL adapter\n *\n * better-auth resolves the `database` option as follows:\n * - `undefined` → in-memory adapter\n * - `typeof fn === \"function\"` → treated as `DBAdapterInstance`, called with `(options)`\n * - otherwise → forwarded to Kysely adapter factory (pool/dialect)\n *\n * A raw `CustomAdapter` object would fall into the third branch and fail\n * silently. We therefore wrap the ObjectQL adapter in a factory function\n * so it is correctly recognised as a `DBAdapterInstance`.\n */\n private createDatabaseConfig(): any {\n // Use ObjectQL adapter factory if dataEngine is provided\n if (this.config.dataEngine) {\n // createObjectQLAdapterFactory returns an AdapterFactory\n // (options => DBAdapter) which better-auth invokes via getBaseAdapter().\n // The factory is created by better-auth's createAdapterFactory and\n // automatically applies modelName/fields transformations declared in\n // the betterAuth config above.\n return createObjectQLAdapterFactory(this.config.dataEngine);\n }\n\n // Fallback warning if no dataEngine is provided\n console.warn(\n '⚠️ WARNING: No dataEngine provided to AuthManager! ' +\n 'Using in-memory storage. This is NOT suitable for production. ' +\n 'Please provide a dataEngine instance (e.g., ObjectQL) in AuthManagerOptions.'\n );\n\n // Return a minimal in-memory configuration as fallback\n // This allows the system to work in development/testing without a real database\n return undefined; // better-auth will use its default in-memory adapter\n }\n\n /**\n * Generate a secure secret if not provided\n */\n private generateSecret(): string {\n const envSecret = readEnvWithDeprecation('OS_AUTH_SECRET', ['AUTH_SECRET', 'BETTER_AUTH_SECRET'], { silent: true });\n if (envSecret) return envSecret;\n\n // No secret configured. In production this is FATAL: a predictable\n // `dev-secret-<timestamp>` makes session tokens forgeable (session\n // forgery). Refuse to boot rather than run insecurely.\n if (process.env.NODE_ENV === 'production') {\n throw new Error(\n '[auth] OS_AUTH_SECRET is required in production but is not set. ' +\n 'Refusing to boot with a temporary development secret — session tokens ' +\n 'would be forgeable. Set OS_AUTH_SECRET to a strong random value.'\n );\n }\n\n // Development / test only: fall back to an ephemeral secret, loudly.\n const fallbackSecret = 'dev-secret-' + Date.now();\n console.warn(\n '⚠️ WARNING: No OS_AUTH_SECRET environment variable set! ' +\n 'Using a temporary development secret. ' +\n 'This is NOT secure for production use. ' +\n 'Please set OS_AUTH_SECRET in your environment variables.'\n );\n return fallbackSecret;\n }\n\n /**\n * Update the base URL at runtime.\n *\n * This **must** be called before the first request triggers lazy\n * initialisation of the better-auth instance — typically from a\n * `kernel:ready` hook where the actual server port is known.\n *\n * If the auth instance has already been created this is a no-op and\n * a warning is emitted.\n */\n setRuntimeBaseUrl(url: string): void {\n if (this.auth) {\n console.warn(\n '[AuthManager] setRuntimeBaseUrl() called after the auth instance was already created — ignoring. ' +\n 'Ensure this method is called before the first request.',\n );\n return;\n }\n this.config = { ...this.config, baseUrl: url };\n }\n\n /**\n * Merge runtime configuration into the manager.\n *\n * Settings-backed auth policy can change after the manager is constructed.\n * better-auth itself is created lazily, so changing config before the first\n * request is enough. If an instance already exists, reset it so the next\n * request rebuilds with the new policy.\n */\n applyConfigPatch(patch: Partial<AuthManagerOptions>): void {\n const next: AuthManagerOptions = {\n ...this.config,\n ...patch,\n ...(patch.emailAndPassword\n ? {\n emailAndPassword: {\n ...(this.config.emailAndPassword ?? {}),\n ...patch.emailAndPassword,\n },\n }\n : {}),\n ...(patch.plugins\n ? {\n plugins: {\n ...(this.config.plugins ?? {}),\n ...patch.plugins,\n },\n }\n : {}),\n };\n\n if ('socialProviders' in patch) {\n next.socialProviders = patch.socialProviders;\n }\n\n this.config = next;\n if (this.auth && !patch.authInstance) {\n this.auth = null;\n }\n }\n\n /**\n * Inject (or replace) the outbound email service used by better-auth\n * callbacks. Safe to call after construction but BEFORE the first\n * request hits the auth handler — callbacks read this via\n * {@link getEmailService} when invoked.\n *\n * AuthPlugin calls this on `kernel:ready` once `ctx.getService('email')`\n * resolves. For tests / serverless, callers may invoke directly.\n */\n setEmailService(email: IEmailService | undefined): void {\n this.config.emailService = email;\n }\n\n /** @internal Used by callback closures. */\n private getEmailService(): IEmailService | undefined {\n return this.config.emailService;\n }\n\n /**\n * Override the brand name surfaced in built-in auth emails (`{{appName}}`),\n * sourced from the live `branding.workspace_name` setting.\n *\n * AuthPlugin calls this on `kernel:ready` (and again whenever the setting\n * changes) once the `settings` service resolves. Passing `undefined` clears\n * the override so resolution falls back to the configured `appName`. The\n * value only reflects an *explicitly set* setting — when the operator has\n * not customised it, AuthPlugin passes `undefined` so a deployment's\n * configured `appName` (e.g. `OS_APP_NAME`) keeps precedence.\n */\n setAppName(name: string | undefined): void {\n this.appNameOverride = name?.trim() || undefined;\n }\n private appNameOverride?: string;\n\n /** @internal `{{appName}}` placeholder value for built-in templates. */\n private getAppName(): string {\n return this.appNameOverride ?? this.config.appName ?? 'ObjectStack';\n }\n\n /**\n * Get the underlying better-auth instance\n * Useful for advanced use cases\n */\n getAuthInstance(): Promise<Auth<any>> {\n return this.getOrCreateAuth();\n }\n\n /**\n * Handle an authentication request\n * Forwards the request directly to better-auth's universal handler\n *\n * better-auth catches internal errors (database / adapter / ORM) and\n * returns a 500 Response instead of throwing. We therefore inspect the\n * response status and log server errors so they are not silently swallowed.\n *\n * @param request - Web standard Request object\n * @returns Web standard Response object\n */\n async handleRequest(request: Request): Promise<Response> {\n // Dev DX: better-auth's CSRF protection rejects state-changing requests\n // (e.g. `/sign-in/email`) with a 403 when neither `Origin` nor `Referer`\n // is present. Browsers always send one; non-browser API clients (curl,\n // fetch from a script, integration tests) often don't, so they hit an\n // opaque 403 in local dev. A request with *no* Origin header is never a\n // browser-driven cross-site attack — CSRF is fundamentally cross-origin —\n // so in non-production we synthesize a same-origin `Origin` from the\n // request URL. It matches the dev localhost trusted-origins set, so the\n // CSRF check passes without weakening protection in production (gated on\n // NODE_ENV, the same dev signal used for the fallback auth secret above).\n if (\n process.env.NODE_ENV !== 'production' &&\n !request.headers.get('origin') &&\n !request.headers.get('referer')\n ) {\n try {\n const headers = new Headers(request.headers);\n headers.set('origin', new URL(request.url).origin);\n request = new Request(request, { headers });\n } catch {\n /* malformed URL — leave the request untouched */\n }\n }\n\n const auth = await this.getOrCreateAuth();\n // better-auth's HTTP entrypoint (`createBetterAuth.handler`) wraps execution\n // in `runWithAdapter` but NOT `runWithRequestState`. Endpoints that read\n // request-state via `defineRequestState()` (e.g. `should-session-refresh`,\n // `oauth`) therefore throw \"No request state found\" when reached via HTTP.\n // The `customSession` plugin triggers this by invoking the inner\n // `getSession()` endpoint directly, bypassing `to-auth-endpoints`'\n // auto-wrap. We establish the ALS store here so all downstream endpoint\n // calls inherit a valid request-state WeakMap.\n const { runWithRequestState } = await import('@better-auth/core/context');\n const response = await runWithRequestState(new WeakMap(), () => auth.handler(request));\n\n if (response.status >= 500) {\n try {\n const body = await response.clone().text();\n console.error('[AuthManager] better-auth returned error:', response.status, body);\n } catch {\n console.error('[AuthManager] better-auth returned error:', response.status, '(unable to read body)');\n }\n }\n\n return response;\n }\n\n /**\n * Get the better-auth API for programmatic access\n * Use this for server-side operations (e.g., creating users, checking sessions)\n */\n async getApi(): Promise<Auth<any>['api']> {\n const auth = await this.getOrCreateAuth();\n return auth.api;\n }\n\n /**\n * Get the underlying better-auth context for low-level operations such as\n * `internalAdapter.createAccount` / `password.hash`.\n *\n * Used by routes that need to write to better-auth's tables outside the\n * normal endpoint surface — currently only `set-initial-password`, which\n * provisions a credential account for SSO-onboarded users so they can\n * sign in with email/password going forward.\n */\n async getAuthContext(): Promise<any> {\n const auth = await this.getOrCreateAuth();\n return (auth as any).$context;\n }\n\n // ---------------------------------------------------------------------------\n // Device Flow (CLI browser-based login)\n //\n // The device authorization flow (RFC 8628) is now handled entirely by\n // better-auth's `device-authorization` plugin. Endpoints are exposed at\n // `${basePath}/device/{code,token,approve,deny}` and persisted in\n // `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in\n // AuthPluginConfig.\n // ---------------------------------------------------------------------------\n\n /**\n * SSO-only (\"enforced\") login mode: the login UI hides the local password\n * form + self-registration so the team signs in via the IdP only.\n * `OS_AUTH_SSO_ONLY` (when set) wins over the `ssoOnlyMode` config knob —\n * parity with the `disableSignUp` env override — so a deployment can force\n * it regardless of the per-env/config value. Break-glass is preserved: this\n * NEVER disables `emailAndPassword.enabled`; it only forces `disableSignUp`\n * and signals the UI to hide the password form. Generic over the IdP.\n */\n private resolveSsoOnly(): boolean {\n return readSsoOnlyEnv() ?? (this.config.ssoOnlyMode ?? false);\n }\n\n getPublicConfig() {\n // Extract social providers info (without sensitive data)\n const socialProviders = [];\n if (this.config.socialProviders) {\n for (const [id, providerConfig] of Object.entries(this.config.socialProviders)) {\n if (providerConfig.enabled !== false) {\n // Map provider ID to friendly name\n const nameMap: Record<string, string> = {\n google: 'Google',\n github: 'GitHub',\n microsoft: 'Microsoft',\n apple: 'Apple',\n facebook: 'Facebook',\n twitter: 'Twitter',\n discord: 'Discord',\n gitlab: 'GitLab',\n linkedin: 'LinkedIn',\n };\n\n socialProviders.push({\n id,\n name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),\n enabled: true,\n type: 'social' as const,\n });\n }\n }\n }\n\n // Append OIDC providers\n if (this.config.oidcProviders?.length) {\n for (const p of this.config.oidcProviders) {\n socialProviders.push({\n id: p.providerId,\n name: p.name ?? (p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1)),\n enabled: true,\n type: 'oidc' as const,\n });\n }\n }\n\n // Extract email/password config (safe fields only). Deployment env can\n // lock registration policy; otherwise we fall back to configured/settings\n // `emailAndPassword.disableSignUp` (default `false`).\n const emailPasswordConfig: Partial<EmailAndPasswordConfig> = this.config.emailAndPassword ?? {};\n const disableSignUpFromEnv = readDisableSignUpEnv();\n // SSO-only (\"enforced\") hides the local password form + self-registration.\n // `enabled` stays true (break-glass), but signup is forced off and the UI\n // suppresses the password form via `features.ssoEnforced` below.\n const ssoOnly = this.resolveSsoOnly();\n const emailPassword = {\n enabled: emailPasswordConfig.enabled !== false, // Default to true\n disableSignUp: ssoOnly ? true : (disableSignUpFromEnv ?? emailPasswordConfig.disableSignUp ?? false),\n requireEmailVerification: emailPasswordConfig.requireEmailVerification ?? false,\n };\n\n // Extract enabled features\n const pluginConfig: Partial<AuthPluginConfig> = this.config.plugins ?? {};\n // Multi-org capability (UI org-switcher, \"create org\" action, etc.).\n // `OS_MULTI_ORG_ENABLED` (default `'false'` → single-org / per-env runtime).\n const multiOrgEnabled = resolveMultiOrgEnabled();\n\n // Legal links shown beneath the login / register cards. Defaults to\n // the public ObjectStack pages so vanilla deployments don't link to\n // dead `#` anchors; operators who deploy ObjectStack on their own\n // domain typically override these with their own legal docs via the\n // `OS_TERMS_URL` / `OS_PRIVACY_URL` env vars. Set the env var to the\n // empty string to suppress the link entirely.\n const DEFAULT_TERMS_URL = 'https://objectstack.ai/terms';\n const DEFAULT_PRIVACY_URL = 'https://objectstack.ai/privacy';\n const rawTermsUrl = (globalThis as any)?.process?.env?.OS_TERMS_URL;\n const rawPrivacyUrl = (globalThis as any)?.process?.env?.OS_PRIVACY_URL;\n const resolveLegalUrl = (raw: unknown, fallback: string): string | undefined => {\n if (typeof raw !== 'string') return fallback;\n const trimmed = raw.trim();\n // Explicit empty string (`OS_TERMS_URL=`) opts out of the link.\n if (trimmed === '') return undefined;\n return trimmed;\n };\n const termsUrl = resolveLegalUrl(rawTermsUrl, DEFAULT_TERMS_URL);\n const privacyUrl = resolveLegalUrl(rawPrivacyUrl, DEFAULT_PRIVACY_URL);\n\n // OIDC Provider — same env-var override as in `buildPlugins()`. The\n // /auth/config response MUST match what's actually wired, otherwise the\n // frontend will render UI for endpoints that 404.\n const oidcEnv = (globalThis as any)?.process?.env?.OS_OIDC_PROVIDER_ENABLED;\n const oidcFromEnv = oidcEnv != null ? String(oidcEnv).toLowerCase() === 'true' : undefined;\n const twoFactorFromEnv = readBooleanEnv('OS_AUTH_TWO_FACTOR');\n\n const features = {\n twoFactor: twoFactorFromEnv ?? pluginConfig.twoFactor ?? false,\n passkeys: pluginConfig.passkeys ?? false,\n magicLink: pluginConfig.magicLink ?? false,\n organization: pluginConfig.organization ?? true,\n multiOrgEnabled,\n oidcProvider: oidcFromEnv ?? pluginConfig.oidcProvider ?? false,\n // Coarse \"is the @better-auth/sso plugin wired\" flag. The `/auth/config`\n // route refines this to \"usable\" (≥1 provider configured) via\n // `isSsoUsable()` so the login UI can hide the \"Sign in with SSO\" button\n // both when SSO is off AND when it's on but no IdP exists yet.\n sso: this.isSsoWired(),\n // SSO-only (\"enforced\"): tell the login UI to hide the local password\n // form + self-registration. A break-glass \"use a password\" link remains\n // for the env owner / local admin. Driven by `ssoOnlyMode` / `OS_AUTH_SSO_ONLY`.\n ssoEnforced: ssoOnly,\n deviceAuthorization: pluginConfig.deviceAuthorization ?? false,\n admin: pluginConfig.admin ?? false,\n ...(termsUrl ? { termsUrl } : {}),\n ...(privacyUrl ? { privacyUrl } : {}),\n };\n\n return {\n emailPassword,\n socialProviders,\n features,\n };\n }\n\n /**\n * Coarse \"is the domain-routed `@better-auth/sso` plugin wired\" flag.\n * Resolved with the EXACT logic that decides whether the plugin is mounted\n * in `buildPlugins()` (`ssoFromEnv ?? pluginConfig.sso ?? false`) so the\n * advertised capability can never disagree with the actual `/sign-in/sso`\n * route. `OS_SSO_ENABLED` (when set) wins over the config-file setting.\n * Public so `AuthPlugin` can gate the Setup-nav \"SSO Providers\" entry on it\n * (captures both self-host `OS_SSO_ENABLED` and the cloud per-env\n * `planAllowsSso` config, since that arrives via `plugins.sso`).\n */\n public isSsoWired(): boolean {\n // Same parser as `buildPluginList` (`readBooleanEnv`) so the advertised\n // capability can never disagree with the actually-mounted route.\n const ssoFromEnv = readBooleanEnv('OS_SSO_ENABLED');\n return ssoFromEnv ?? (this.config.plugins as any)?.sso ?? false;\n }\n\n /**\n * Whether opt-in DNS domain-verification (ADR-0024 ②) is wired — i.e. the\n * `/sso/request-domain-verification` + `/sso/verify-domain` endpoints are\n * mounted (and the hard \"domain must be verified to log in\" gate is active).\n * Resolved with the EXACT logic `buildPluginList` uses for the `sso()`\n * `domainVerification.enabled` option, so the bridge can return a clear\n * \"not enabled for this environment\" instead of a bare 404 when off.\n * Implies `isSsoWired()` (the sso plugin must be loaded to honor it).\n */\n public isSsoDomainVerificationEnabled(): boolean {\n if (!this.isSsoWired()) return false;\n const fromEnv = readBooleanEnv('OS_SSO_DOMAIN_VERIFICATION');\n return fromEnv ?? (this.config.plugins as any)?.ssoDomainVerification ?? false;\n }\n\n /**\n * Whether enterprise SSO is actually *usable*, not merely wired: the plugin\n * is on AND at least one `sys_sso_provider` row exists. Per-email domain→IdP\n * matching still happens at `/sign-in/sso`; this answers the coarser \"is\n * there any point showing the SSO button at all\", so a freshly-enabled but\n * unconfigured SSO setup doesn't advertise a button that errors for everyone.\n *\n * Fails OPEN to the wired flag when providers can't be counted (no data\n * engine, query error) — a config-introspection hiccup must never make the\n * login page hide a button that genuinely works.\n */\n public async isSsoUsable(): Promise<boolean> {\n if (!this.isSsoWired()) return false;\n const engine = this.getDataEngine();\n if (!engine) return true; // wired but can't verify — fall open\n try {\n const count = await withSystemReadContext(engine).count('sys_sso_provider');\n return typeof count === 'number' ? count > 0 : true;\n } catch {\n return true; // provider introspection failed — keep the wired behaviour\n }\n }\n\n /**\n * Extra `trustedOrigins` entries derived from an external-SSO registration\n * request. For a `POST /sso/register` | `/sso/update-provider`, parse the\n * (cloned) body and return the PUBLIC-ROUTABLE origins of the declared\n * `issuer` / `oidcConfig` endpoints so `@better-auth/sso`'s discovery\n * validation accepts a customer IdP registered at runtime (ADR-0024) without\n * the operator pre-listing it in boot config. Only public-routable hosts are\n * returned — private / internal / loopback hosts are never auto-trusted\n * (better-auth's `isPublicRoutableHost`, the same predicate its own\n * sub-endpoint check uses). Best-effort: any parse error yields `[]`.\n */\n private async ssoDiscoveryTrustedOrigins(request: unknown): Promise<string[]> {\n try {\n const req = request as { url?: string; method?: string; clone?: () => Request } | undefined;\n if (!req || typeof req.clone !== 'function' || !req.url) return [];\n if ((req.method ?? 'GET').toUpperCase() !== 'POST') return [];\n const path = new URL(req.url).pathname;\n if (!/\\/sso\\/(register|update-provider)$/.test(path)) return [];\n const body = await req.clone().json().catch(() => null);\n if (!body || typeof body !== 'object') return [];\n const oidc = (body as any).oidcConfig ?? {};\n const candidates = [\n (body as any).issuer,\n oidc.discoveryEndpoint,\n oidc.authorizationEndpoint,\n oidc.tokenEndpoint,\n oidc.jwksEndpoint,\n oidc.userInfoEndpoint,\n ].filter((v): v is string => typeof v === 'string' && v.length > 0);\n if (!candidates.length) return [];\n const { isPublicRoutableHost } = await import('@better-auth/core/utils/host');\n const out: string[] = [];\n for (const c of candidates) {\n try {\n const u = new URL(c);\n if (isPublicRoutableHost(u.hostname) && !out.includes(u.origin)) out.push(u.origin);\n } catch { /* skip malformed URL */ }\n }\n return out;\n } catch {\n return [];\n }\n }\n\n /**\n * Resolve the acting user (+ their active org) for a before-hook gate,\n * hook-order-independent. Tries the standard cookie session first, then falls\n * back to explicit token resolution (bearer or the session cookie's token\n * part) — the bearer plugin may convert `Authorization: Bearer` to a session\n * AFTER this global before-hook runs. Returns `null` when no valid session\n * can be resolved (→ caller lets `sessionMiddleware` issue the 401).\n */\n private async resolveActor(\n ctx: any,\n ): Promise<{ userId: string; activeOrgId?: string } | null> {\n try {\n const { getSessionFromCtx } = await import('better-auth/api');\n const s: any = await getSessionFromCtx(ctx as any);\n const userId = s?.user?.id ?? s?.session?.userId;\n if (userId) {\n return {\n userId: String(userId),\n activeOrgId:\n s?.session?.activeOrganizationId ?? s?.activeOrganizationId ?? undefined,\n };\n }\n } catch { /* fall through to explicit token resolution */ }\n try {\n const hdr = (k: string): string =>\n ((ctx?.headers?.get?.(k) ?? ctx?.request?.headers?.get?.(k)) as string) || '';\n let token: string | undefined;\n const bm = /^Bearer\\s+(.+)$/i.exec(hdr('authorization'));\n if (bm?.[1]) token = bm[1].trim();\n if (!token) {\n const cm = /(?:^|;\\s*)(?:__Secure-|__Host-)?better-auth\\.session_token=([^;]+)/.exec(hdr('cookie'));\n if (cm?.[1]) token = decodeURIComponent(cm[1]).split('.')[0];\n }\n if (token) {\n const sess: any = await (ctx as any).context.adapter.findOne({\n model: 'session',\n where: [{ field: 'token', value: token }],\n });\n const exp = sess?.expiresAt ?? sess?.expires_at;\n if (sess && (!exp || new Date(exp).getTime() > Date.now())) {\n const userId = String(sess.userId ?? sess.user_id ?? '');\n if (userId) {\n return {\n userId,\n activeOrgId:\n sess.activeOrganizationId ?? sess.active_organization_id ?? undefined,\n };\n }\n }\n }\n } catch { /* unresolved → null */ }\n return null;\n }\n\n /**\n * True when `userId` is a platform admin (a `sys_user_permission_set` row\n * pointing at `admin_full_access` with `organization_id = null`) OR an\n * owner/admin member of `activeOrgId` (any org membership with role\n * owner/admin when no active org is set). Mirrors the role-derivation in\n * `customSession`; reads through `withSystemReadContext` so the lookups are\n * not themselves RLS-scoped to the acting (possibly non-privileged) user.\n * Fails CLOSED (returns false) on any lookup error — this backs a security\n * gate, so an unverifiable actor must never pass.\n */\n private async isOrgOrPlatformAdmin(\n userId: string,\n activeOrgId?: string,\n ): Promise<boolean> {\n const engine = this.getDataEngine();\n if (!engine) return false;\n const sys = withSystemReadContext(engine);\n try {\n // 1) platform admin — admin_full_access permission set, org-less link.\n const links = await sys.find('sys_user_permission_set', {\n where: { user_id: userId },\n limit: 50,\n });\n const platformLinks = (Array.isArray(links) ? links : []).filter(\n (l: any) => !l.organization_id,\n );\n if (platformLinks.length) {\n const sets = await sys.find('sys_permission_set', { limit: 50 });\n const adminSet = (Array.isArray(sets) ? sets : []).find(\n (r: any) => r.name === 'admin_full_access',\n );\n if (adminSet && platformLinks.some((l: any) => l.permission_set_id === adminSet.id)) {\n return true;\n }\n }\n // 2) org owner/admin — membership role in the active org (or any org).\n const where: any = { user_id: userId };\n if (activeOrgId) where.organization_id = activeOrgId;\n const members = await sys.find('sys_member', { where, limit: 10 });\n for (const m of (Array.isArray(members) ? members : [])) {\n const raw = typeof m?.role === 'string' ? m.role : '';\n if (\n raw\n .split(',')\n .map((s: string) => s.trim())\n .some((r: string) => r === 'owner' || r === 'admin')\n ) {\n return true;\n }\n }\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compose the framework's identity-source stamp (`account.create.after`)\n * with any host-supplied `databaseHooks`, preserving BOTH. The cloud passes\n * `user.create.after` (personal-org provisioning) + `session.create.before`\n * (active-org) — different model/op, so no collision — but if a host ever\n * adds its own `account.create.after` we chain it after the stamp rather\n * than silently dropping one.\n */\n private composeDatabaseHooks(\n host?: BetterAuthOptions['databaseHooks'],\n ): BetterAuthOptions['databaseHooks'] {\n const stamp = (account: any, ctx: any) => this.stampIdentitySource(account, ctx);\n const hostAccountAfter = (host as any)?.account?.create?.after;\n const after = hostAccountAfter\n ? async (account: any, ctx: any) => {\n await stamp(account, ctx);\n return hostAccountAfter(account, ctx);\n }\n : stamp;\n return {\n ...(host ?? {}),\n account: {\n ...((host as any)?.account ?? {}),\n create: {\n ...((host as any)?.account?.create ?? {}),\n after,\n },\n },\n } as BetterAuthOptions['databaseHooks'];\n }\n\n /**\n * Maintain `sys_user.source` (ADR-0024 D4 provenance) as accounts are linked.\n * Drives the managed-vs-native user-mgmt gating: a managed (`idp-provisioned`)\n * user holds no local credential, so the password / identity-edit actions\n * hide for them — preventing a managed user from self-minting a local\n * password that would bypass enforced SSO.\n *\n * Two cases, both break-glass safe and idempotent (only writes on a real\n * change, so trackHistory stays quiet):\n *\n * • A **federated** account (any non-`credential` provider — the cloud-as-IdP\n * `objectstack-cloud` provider OR a customer's own OIDC/SAML IdP) is\n * linked AND the user holds NO local credential → mark `idp-provisioned`.\n * A user who already has a `credential` account (an env-native user who\n * linked SSO) is left `env-native` — they keep a usable password.\n *\n * • A **credential** account is created (local signup, or the break-glass\n * owner's password set via set-initial-password — which can land AFTER the\n * first SSO link) → ensure `env-native`. This flips a previously-stamped\n * owner back, so the break-glass admin never loses self-service password\n * management.\n *\n * Best-effort: any failure leaves the prior value (the gate fails open — a\n * managed user might transiently show a password action that simply errors —\n * never a hard login failure).\n */\n private async stampIdentitySource(account: any, _ctx?: unknown): Promise<void> {\n try {\n const providerId = account?.providerId ?? account?.provider_id;\n const userId = account?.userId ?? account?.user_id;\n if (!userId || !providerId) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n\n if (providerId === 'credential') {\n // Gained a local password → env-native. Only write if currently\n // managed (avoids a no-op history row on every local signup).\n const u = await engine.findOne('sys_user', {\n filter: { id: userId }, fields: ['id', 'source'], context: SYSTEM_CTX,\n } as any);\n if (u && u.source === 'idp_provisioned') {\n await engine.update('sys_user', { id: userId, source: 'env_native' }, { context: SYSTEM_CTX } as any);\n }\n return;\n }\n\n // Federated link → managed, unless a local credential already exists.\n const credentialCount = await engine.count('sys_account', {\n filter: { user_id: userId, provider_id: 'credential' },\n context: SYSTEM_CTX,\n } as any);\n if (typeof credentialCount === 'number' && credentialCount > 0) return;\n await engine.update('sys_user', { id: userId, source: 'idp_provisioned' }, { context: SYSTEM_CTX } as any);\n } catch {\n // Provenance stamp must never break federated login. Leave the prior value.\n }\n }\n\n /**\n * ADR-0069 D1 — reject a password that doesn't meet the configured character-\n * class complexity. No-op when `passwordRequireComplexity` is off. Counts the\n * four classes (upper / lower / digit / symbol) present and throws\n * `PASSWORD_POLICY_VIOLATION` when fewer than `passwordMinClasses` are used.\n */\n private async assertPasswordComplexity(password: string): Promise<void> {\n if (!this.config.passwordRequireComplexity) return;\n const min = Math.min(4, Math.max(1, Math.floor(Number(this.config.passwordMinClasses) || 3)));\n const classes =\n (/[a-z]/.test(password) ? 1 : 0) +\n (/[A-Z]/.test(password) ? 1 : 0) +\n (/[0-9]/.test(password) ? 1 : 0) +\n (/[^A-Za-z0-9]/.test(password) ? 1 : 0);\n if (classes < min) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('BAD_REQUEST', {\n message:\n `Password must include at least ${min} of: uppercase, lowercase, ` +\n 'digit, symbol.',\n code: 'PASSWORD_POLICY_VIOLATION',\n });\n }\n }\n\n /**\n * ADR-0069 — is any authentication-policy gate enabled? Cheap, synchronous;\n * lets the transport seams skip session lookups entirely when off (the\n * default), keeping the gate zero-overhead until an admin opts in.\n */\n public isAuthGateActive(): boolean {\n // Per-org MFA (no global flag) can still activate the gate — keep the cheap\n // sync check honest by consulting a lazily-refreshed cache.\n this.refreshOrgMfaCacheIfStale();\n return (\n Math.floor(Number(this.config.passwordExpiryDays) || 0) > 0 ||\n this.config.mfaRequired === true ||\n this._orgMfaCache.value\n );\n }\n\n /**\n * ADR-0069 — refresh the \"any org requires MFA\" cache in the background when\n * stale (60s TTL). Fire-and-forget: a brand-new per-org requirement activates\n * the gate on the next request, never blocking this one. No-op when global MFA\n * is already on (the gate is active regardless).\n */\n private refreshOrgMfaCacheIfStale(): void {\n if (this.config.mfaRequired === true) return;\n if (this._orgMfaRefreshing) return;\n if (Date.now() - this._orgMfaCache.at < 60_000) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n this._orgMfaRefreshing = true;\n void (async () => {\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const n = await engine.count('sys_organization', {\n where: { require_mfa: true }, context: SYSTEM_CTX,\n } as any);\n this._orgMfaCache = { value: typeof n === 'number' && n > 0, at: Date.now() };\n } catch {\n // leave the prior value; try again after the TTL\n } finally {\n this._orgMfaRefreshing = false;\n }\n })();\n }\n\n /**\n * ADR-0069 — compute the auth-policy gate posture for a session. Returns an\n * `{ code, message }` when the user is currently blocked (e.g. password\n * expired), else undefined. No-op (and no DB read) when no gate feature is\n * enabled. Fails OPEN on any lookup error — a transient hiccup must never lock\n * a compliant user out.\n */\n private async computeAuthGate(\n userId: string,\n _activeOrgId: string | undefined,\n _twoFactorEnabledHint: boolean,\n ): Promise<{ code: string; message: string } | undefined> {\n const expiryDays = Math.floor(Number(this.config.passwordExpiryDays) || 0);\n const mfaGlobal = this.config.mfaRequired === true;\n // Per-org tightening: an org may require MFA above the global floor.\n const orgMaybeRequires = !mfaGlobal && !!_activeOrgId && this._orgMfaCache.value;\n if (expiryDays <= 0 && !mfaGlobal && !orgMaybeRequires) return undefined; // no gate feature active\n const engine = this.getDataEngine();\n if (!engine || !userId) return undefined;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const u = await engine.findOne('sys_user', {\n where: { id: userId },\n fields: ['password_changed_at', 'two_factor_enabled', 'mfa_required_at'],\n context: SYSTEM_CTX,\n } as any);\n\n // Effective MFA requirement: global floor OR the active org's require_mfa.\n let mfaRequired = mfaGlobal;\n if (!mfaRequired && orgMaybeRequires) {\n const org = await engine.findOne('sys_organization', {\n where: { id: _activeOrgId }, fields: ['require_mfa'], context: SYSTEM_CTX,\n } as any);\n mfaRequired = org?.require_mfa === true || org?.require_mfa === 1;\n }\n\n // ── Password expiry ───────────────────────────────────────────────\n if (expiryDays > 0) {\n const changed = u?.password_changed_at;\n // Null = never expires (existing accounts on upgrade) until next change.\n if (changed && Date.now() - new Date(changed).getTime() > expiryDays * 86_400_000) {\n return {\n code: 'PASSWORD_EXPIRED',\n message: 'Your password has expired. Please change it to continue.',\n };\n }\n }\n\n // ── Enforced MFA ──────────────────────────────────────────────────\n // A user without TOTP enrolled is blocked once their grace window\n // elapses. The clock (`mfa_required_at`) starts the first time we see\n // them required-but-unenrolled (stamped lazily, best-effort).\n if (mfaRequired && !(u?.two_factor_enabled === true || u?.two_factor_enabled === 1)) {\n const graceDays = Math.max(0, Math.floor(Number(this.config.mfaGracePeriodDays ?? 7)));\n let requiredAt = u?.mfa_required_at;\n if (!requiredAt) {\n requiredAt = new Date();\n // Best-effort: start the grace clock; never block on the write.\n engine\n .update('sys_user', { id: userId, mfa_required_at: requiredAt }, { context: SYSTEM_CTX } as any)\n .catch(() => undefined);\n }\n const elapsedMs = Date.now() - new Date(requiredAt).getTime();\n if (elapsedMs > graceDays * 86_400_000) {\n return {\n code: 'MFA_REQUIRED',\n message:\n 'Multi-factor authentication is required. Please set up an authenticator app to continue.',\n };\n }\n }\n } catch {\n return undefined; // fail-open\n }\n return undefined;\n }\n\n /**\n * ADR-0069 D1 — stamp `sys_user.password_changed_at = now` after a password is\n * set (sign-up / change / reset). Best-effort; never throws. Written as a Date\n * (never epoch-ms) per ADR-0074.\n */\n private async stampPasswordChangedAt(userId: string): Promise<void> {\n const engine = this.getDataEngine();\n if (!engine || !userId) return;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n await engine.update(\n 'sys_user',\n { id: userId, password_changed_at: new Date() },\n { context: SYSTEM_CTX } as any,\n );\n } catch {\n // audit stamp is best-effort — never break a valid password change\n }\n }\n\n /**\n * ADR-0069 D1 — parse the bounded `previous_password_hashes` JSON column into\n * a string[] of hashes, tolerating null / malformed values.\n */\n private parseHashes(raw: unknown): string[] {\n if (typeof raw !== 'string' || !raw.trim()) return [];\n try {\n const arr = JSON.parse(raw);\n return Array.isArray(arr) ? arr.filter((h): h is string => typeof h === 'string' && !!h) : [];\n } catch {\n return [];\n }\n }\n\n /**\n * ADR-0069 D1 — resolve the user whose password is being changed. For\n * `/change-password` the caller is authenticated (session); for\n * `/reset-password` the user is carried by the reset token's verification\n * value (the same lookup better-auth's own handler uses).\n */\n private async resolvePasswordChangeUserId(ctx: any): Promise<string | undefined> {\n if (ctx?.path === '/change-password') {\n const { getSessionFromCtx } = await import('better-auth/api');\n const sess: any = await getSessionFromCtx(ctx).catch(() => null);\n return sess?.user?.id ?? sess?.session?.userId ?? undefined;\n }\n if (ctx?.path === '/reset-password') {\n const token = typeof ctx?.body?.token === 'string' ? ctx.body.token : '';\n if (!token) return undefined;\n try {\n const v: any = await ctx.context.internalAdapter.findVerificationValue(`reset-password:${token}`);\n const raw = v?.value;\n if (!raw) return undefined;\n if (typeof raw === 'string') {\n const t = raw.trim();\n if (t.startsWith('{') || t.startsWith('\"')) {\n try {\n const o = JSON.parse(t);\n return (typeof o === 'string' ? o : o?.userId) ?? undefined;\n } catch {\n return t;\n }\n }\n return t;\n }\n return raw?.userId ?? undefined;\n } catch {\n return undefined;\n }\n }\n return undefined;\n }\n\n /**\n * ADR-0069 D1 — throw `PASSWORD_REUSE` when `candidate` matches the user's\n * current password or any hash in the bounded history. Reuses better-auth's\n * native `password.verify` (passed in) rather than re-hashing. Returns the\n * current hash (for the after-hook to append) when the candidate is fresh, or\n * undefined when the feature is off / nothing to compare.\n */\n private async assertPasswordNotReused(\n userId: string,\n candidate: string,\n verify?: (data: { password: string; hash: string }) => Promise<boolean>,\n ): Promise<string | undefined> {\n const count = Math.floor(Number(this.config.passwordHistoryCount) || 0);\n if (count <= 0 || typeof verify !== 'function') return undefined;\n const engine = this.getDataEngine();\n if (!engine) return undefined;\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n let account: any;\n try {\n account = await engine.findOne('sys_account', {\n where: { user_id: userId, provider_id: 'credential' },\n fields: ['id', 'password', 'previous_password_hashes'],\n context: SYSTEM_CTX,\n } as any);\n } catch {\n return undefined; // fail-open on lookup error\n }\n if (!account?.id) return undefined;\n const currentHash = typeof account.password === 'string' ? account.password : '';\n const compareList = [currentHash, ...this.parseHashes(account.previous_password_hashes)].filter(Boolean);\n for (const h of compareList) {\n let match = false;\n try { match = await verify({ password: candidate, hash: h }); } catch { match = false; }\n if (match) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('BAD_REQUEST', {\n message: `For security you can't reuse one of your last ${count} passwords. Please choose a different one.`,\n code: 'PASSWORD_REUSE',\n });\n }\n }\n return currentHash;\n }\n\n /**\n * ADR-0069 D1 — append `oldHash` to the bounded password-history ring after a\n * successful change/reset. Best-effort; never throws.\n */\n private async recordPasswordHistory(userId: string, oldHash: string): Promise<void> {\n const count = Math.floor(Number(this.config.passwordHistoryCount) || 0);\n if (count <= 0 || !oldHash) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const account = await engine.findOne('sys_account', {\n where: { user_id: userId, provider_id: 'credential' },\n fields: ['id', 'previous_password_hashes'],\n context: SYSTEM_CTX,\n } as any);\n if (!account?.id) return;\n const prev = this.parseHashes(account.previous_password_hashes);\n const next = [oldHash, ...prev.filter((h) => h !== oldHash)].slice(0, count);\n await engine.update(\n 'sys_account',\n { id: account.id, previous_password_hashes: JSON.stringify(next) },\n { context: SYSTEM_CTX } as any,\n );\n } catch {\n // history maintenance is best-effort — never break a valid password change\n }\n }\n\n /**\n * ADR-0069 D2 — throw `ACCOUNT_LOCKED` when the identity is currently locked\n * out (brute-force protection). No-op when lockout is disabled\n * (`lockoutThreshold <= 0`) or no data engine is wired. Fails OPEN on a\n * lookup error: an infra hiccup must never block every login.\n */\n private async assertAccountNotLocked(email: string): Promise<void> {\n const threshold = Number(this.config.lockoutThreshold) || 0;\n if (threshold <= 0) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n let locked = false;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const u = await engine.findOne('sys_user', {\n where: { email }, fields: ['id', 'locked_until'], context: SYSTEM_CTX,\n } as any);\n const lu = u?.locked_until;\n locked = !!(lu && new Date(lu).getTime() > Date.now());\n } catch {\n return; // fail-open\n }\n if (locked) {\n const { APIError } = await import('better-auth/api');\n throw new APIError('FORBIDDEN', {\n message:\n 'This account is temporarily locked after too many failed sign-in ' +\n 'attempts. Try again later or ask an administrator to unlock it.',\n code: 'ACCOUNT_LOCKED',\n });\n }\n }\n\n /**\n * ADR-0069 D2 — record a sign-in outcome for lockout accounting. On failure\n * increments `failed_login_count` and, once it reaches `lockoutThreshold`,\n * stamps `locked_until = now + lockoutDurationMinutes`. On success resets\n * both (only writing when there is something to clear, to avoid a no-op\n * history row on every login). No-op when lockout is disabled. Never throws —\n * a counter write must not turn a valid login into an error.\n */\n private async recordSignInOutcome(email: string, success: boolean): Promise<void> {\n const threshold = Number(this.config.lockoutThreshold) || 0;\n if (threshold <= 0) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const u = await engine.findOne('sys_user', {\n where: { email },\n fields: ['id', 'failed_login_count', 'locked_until'],\n context: SYSTEM_CTX,\n } as any);\n if (!u?.id) return;\n if (success) {\n if ((Number(u.failed_login_count) || 0) !== 0 || u.locked_until) {\n await engine.update(\n 'sys_user',\n { id: u.id, failed_login_count: 0, locked_until: null },\n { context: SYSTEM_CTX } as any,\n );\n }\n return;\n }\n const next = (Number(u.failed_login_count) || 0) + 1;\n const patch: Record<string, unknown> = { id: u.id, failed_login_count: next };\n if (next >= threshold) {\n const mins = Number(this.config.lockoutDurationMinutes) || 15;\n patch.locked_until = new Date(Date.now() + mins * 60_000);\n }\n await engine.update('sys_user', patch, { context: SYSTEM_CTX } as any);\n } catch {\n // Lockout accounting is best-effort — never break the auth response.\n }\n }\n\n /**\n * ADR-0069 D2 — clear a user's lockout state (admin \"Unlock\" action).\n * Resets `failed_login_count` and `locked_until`. Returns false when no data\n * engine is wired or the user does not exist.\n */\n public async unlockUser(userId: string): Promise<boolean> {\n const engine = this.getDataEngine();\n if (!engine || !userId) return false;\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const u = await engine.findOne('sys_user', {\n where: { id: userId }, fields: ['id'], context: SYSTEM_CTX,\n } as any);\n if (!u?.id) return false;\n await engine.update(\n 'sys_user',\n { id: userId, failed_login_count: 0, locked_until: null },\n { context: SYSTEM_CTX } as any,\n );\n return true;\n }\n\n /**\n * ADR-0069 D4 — idle / absolute session enforcement, run per request from\n * `customSession`. No-op when both are off. Revokes (expires in place +\n * stamps revoked_at/revoke_reason) when a limit is exceeded so better-auth\n * returns no session on the NEXT request; otherwise touches `last_activity_at`\n * (throttled to once a minute). Best-effort — never throws.\n */\n private async enforceSessionControls(sessionId: string | undefined, createdAtHint: unknown): Promise<void> {\n const idleMin = Math.floor(Number(this.config.sessionIdleTimeoutMinutes) || 0);\n const absHrs = Math.floor(Number(this.config.sessionAbsoluteMaxHours) || 0);\n if (idleMin <= 0 && absHrs <= 0) return;\n const engine = this.getDataEngine();\n if (!engine || !sessionId) return;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const srow = await engine.findOne('sys_session', {\n where: { id: sessionId },\n fields: ['id', 'created_at', 'last_activity_at', 'revoked_at'],\n context: SYSTEM_CTX,\n } as any);\n if (!srow?.id || srow.revoked_at) return;\n const now = Date.now();\n let reason: string | undefined;\n if (absHrs > 0) {\n const created = srow.created_at ?? createdAtHint;\n if (created && now - new Date(created as any).getTime() > absHrs * 3_600_000) reason = 'absolute_max';\n }\n if (!reason && idleMin > 0) {\n const last = srow.last_activity_at ?? srow.created_at ?? createdAtHint;\n if (last && now - new Date(last as any).getTime() > idleMin * 60_000) reason = 'idle_timeout';\n }\n if (reason) {\n await engine.update(\n 'sys_session',\n { id: sessionId, expires_at: new Date(now - 1000), revoked_at: new Date(now), revoke_reason: reason },\n { context: SYSTEM_CTX } as any,\n ).catch(() => undefined);\n return;\n }\n if (idleMin > 0) {\n const la = srow.last_activity_at ? new Date(srow.last_activity_at as any).getTime() : 0;\n if (now - la > 60_000) {\n await engine.update('sys_session', { id: sessionId, last_activity_at: new Date(now) }, { context: SYSTEM_CTX } as any).catch(() => undefined);\n }\n }\n } catch {\n // session controls are best-effort — never break a request\n }\n }\n\n /**\n * ADR-0069 D4 — concurrent-session cap, run from the sign-in after-hook.\n * Keeps the newest `maxConcurrentSessions` live sessions for the user and\n * revokes the rest (oldest first). No-op when off. Best-effort.\n */\n private async enforceConcurrentCap(userId: string): Promise<void> {\n const cap = Math.floor(Number(this.config.maxConcurrentSessions) || 0);\n if (cap <= 0 || !userId) return;\n const engine = this.getDataEngine();\n if (!engine) return;\n try {\n const SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] };\n const rows = await engine.find('sys_session', {\n where: { user_id: userId },\n fields: ['id', 'created_at', 'expires_at', 'revoked_at'],\n limit: 200,\n context: SYSTEM_CTX,\n } as any);\n const now = Date.now();\n const live = (Array.isArray(rows) ? rows : [])\n .filter((sn: any) => !sn.revoked_at && (!sn.expires_at || new Date(sn.expires_at).getTime() > now))\n .sort((a: any, b: any) => new Date(b.created_at ?? 0).getTime() - new Date(a.created_at ?? 0).getTime());\n for (const sn of live.slice(cap)) {\n await engine.update(\n 'sys_session',\n { id: sn.id, expires_at: new Date(now - 1000), revoked_at: new Date(now), revoke_reason: 'concurrent_cap' },\n { context: SYSTEM_CTX } as any,\n ).catch(() => undefined);\n }\n } catch {\n // best-effort — never break a successful sign-in\n }\n }\n\n /**\n * ADR-0069 D5 — is `ip` within the configured allow-list? True (allow) when no\n * ranges are configured, OR when the IP can't be determined (fail-open so a\n * misconfigured proxy never locks everyone out — an admin enabling this must\n * ensure forwarded headers are trusted). Supports IPv4 CIDR + exact IPv4/IPv6.\n */\n public isClientIpAllowed(ip: string | undefined): boolean {\n const ranges = this.config.allowedIpRanges;\n if (!ranges || ranges.length === 0) return true;\n if (!ip) return true; // undetermined → fail-open\n return ranges.some((r) => ipMatchesRange(ip, r));\n }\n\n /**\n * Returns the data engine wired into this auth manager. Used by route\n * handlers (e.g. bootstrap-status) that need to query identity tables\n * directly without going through better-auth.\n */\n public getDataEngine(): IDataEngine | undefined {\n return this.config.dataEngine;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IDataEngine } from '@objectstack/core';\nimport { createAdapterFactory } from 'better-auth/adapters';\nimport type { CleanedWhere } from 'better-auth/adapters';\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * Mapping from better-auth model names to ObjectStack protocol object names.\n *\n * better-auth uses hardcoded model names ('user', 'session', 'account', 'verification')\n * while ObjectStack's protocol layer uses `sys_` prefixed names. This map bridges the two.\n */\nexport const AUTH_MODEL_TO_PROTOCOL: Record<string, string> = {\n user: SystemObjectName.USER,\n session: SystemObjectName.SESSION,\n account: SystemObjectName.ACCOUNT,\n verification: SystemObjectName.VERIFICATION,\n // Plugin models. `@better-auth/sso` and `@better-auth/scim` both hardcode\n // their model name and accept NO `schema` option (verified vs 1.6.2x — no\n // mergeSchema, runtime never reads options.schema), so the table name is\n // bridged here and `createObjectQLAdapterFactory` (below) auto-maps their\n // camelCase fields to snake_case (oidcConfig→oidc_config, scimToken→\n // scim_token, …) on every CRUD op via resolveProtocolName. Off by default\n // (OS_SSO_ENABLED / OS_SCIM_ENABLED). See ADR-0024 / ADR-0071.\n ssoProvider: 'sys_sso_provider',\n scimProvider: 'sys_scim_provider',\n};\n\n/**\n * Resolve a better-auth model name to the ObjectStack protocol object name.\n * Falls back to the original model name for custom / non-core models.\n */\nexport function resolveProtocolName(model: string): string {\n return AUTH_MODEL_TO_PROTOCOL[model] ?? model;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth datetime columns (snake_case) per model.\n *\n * When the underlying driver stored these as JavaScript `Date` objects\n * (legacy behaviour), the libsql HTTP transport coerces the value to a REAL\n * column and round-trips it as a string like `\"1779497911249.0\"`. That\n * string is not a valid Date string (it has a trailing `.0`), so\n * `new Date(...)` produces `Invalid Date` and better-auth's client treats\n * the session as expired — causing a login/redirect loop.\n *\n * We normalise these legacy values back to ISO strings on **read** so the\n * factory's `supportsDates: false` parser can turn them into real Date\n * objects. New writes always go through better-auth's own\n * `Date → ISO string` conversion (because we declare `supportsDates: false`\n * below), so no further `.0`-suffixed values will ever be created.\n */\nconst LEGACY_DATETIME_FIELDS_BY_MODEL: Record<string, string[]> = {\n user: ['created_at', 'updated_at'],\n session: ['expires_at', 'created_at', 'updated_at'],\n account: [\n 'access_token_expires_at',\n 'refresh_token_expires_at',\n 'created_at',\n 'updated_at',\n ],\n verification: ['expires_at', 'created_at', 'updated_at'],\n};\n\nconst NUMERIC_STRING_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * If `value` looks like a stringified epoch-ms (optionally with `.0`),\n * convert it to an ISO 8601 string. Otherwise return it unchanged.\n */\nfunction normaliseLegacyDate(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (!NUMERIC_STRING_RE.test(value)) return value;\n const n = parseFloat(value);\n if (!Number.isFinite(n)) return value;\n // Heuristic: epoch milliseconds are at least 10 digits (year 2001+).\n if (Math.abs(n) < 1e10) return value;\n const d = new Date(n);\n if (Number.isNaN(d.getTime())) return value;\n return d.toISOString();\n}\n\n/**\n * Walk a record and rewrite any legacy `.0`-suffixed datetime values\n * into ISO strings. Mutates and returns the record.\n */\nfunction normaliseLegacyDates<T extends Record<string, any> | null | undefined>(\n model: string,\n record: T,\n): T {\n if (!record) return record;\n const cols = LEGACY_DATETIME_FIELDS_BY_MODEL[model];\n if (!cols) return record;\n for (const col of cols) {\n if (col in record) {\n (record as Record<string, unknown>)[col] = normaliseLegacyDate(\n (record as Record<string, unknown>)[col],\n );\n }\n }\n return record;\n}\n\n/**\n * Convert better-auth where clause to ObjectQL query format.\n *\n * Field names in the incoming {@link CleanedWhere} are expected to already be\n * in snake_case (transformed by `createAdapterFactory`).\n */\nfunction convertWhere(where: CleanedWhere[]): Record<string, any> {\n const filter: Record<string, any> = {};\n\n for (const condition of where) {\n const fieldName = condition.field;\n\n if (condition.operator === 'eq') {\n filter[fieldName] = condition.value;\n } else if (condition.operator === 'ne') {\n filter[fieldName] = { $ne: condition.value };\n } else if (condition.operator === 'in') {\n filter[fieldName] = { $in: condition.value };\n } else if (condition.operator === 'gt') {\n filter[fieldName] = { $gt: condition.value };\n } else if (condition.operator === 'gte') {\n filter[fieldName] = { $gte: condition.value };\n } else if (condition.operator === 'lt') {\n filter[fieldName] = { $lt: condition.value };\n } else if (condition.operator === 'lte') {\n filter[fieldName] = { $lte: condition.value };\n } else if (condition.operator === 'contains') {\n filter[fieldName] = { $regex: condition.value };\n }\n }\n\n return filter;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a data engine so its READ operations (find / findOne / count) run as\n * SYSTEM reads — injecting `context.isSystem: true` (merged; any caller-supplied\n * context still wins on other keys). better-auth has already authenticated the\n * session and scopes every query by its OWN where-clauses (e.g. member.userId =\n * session.user). A deployment's control-plane org-scope read hook, however, keys\n * off the CALLER's user id, and these adapter reads carry no caller context — so\n * without isSystem that hook filters sys_member / sys_organization reads down to\n * zero and `organization.list()` returns no orgs for a real member. Writes pass\n * through untouched (org-scope is a read-only hook).\n */\nexport function withSystemReadContext(engine: IDataEngine): IDataEngine {\n const e = engine as any;\n const asSystem = (q: any) => ({ ...(q ?? {}), context: { isSystem: true, ...(q?.context ?? {}) } });\n return {\n insert: (m: string, d: any) => e.insert(m, d),\n update: (m: string, d: any) => e.update(m, d),\n delete: (m: string, q?: any) => e.delete(m, q),\n find: (m: string, q?: any) => e.find(m, asSystem(q)),\n findOne: (m: string, q?: any) => e.findOne(m, asSystem(q)),\n count: (m: string, q?: any) => e.count(m, asSystem(q)),\n } as unknown as IDataEngine;\n}\n\n/**\n * Create an ObjectQL adapter **factory** for better-auth.\n *\n * Uses better-auth's official `createAdapterFactory` so that model-name and\n * field-name transformations (declared via `modelName` / `fields` in the\n * betterAuth config) are applied **automatically** before any data reaches\n * ObjectQL. This eliminates the need for manual camelCase ↔ snake_case\n * conversion inside the adapter.\n *\n * The returned value is an `AdapterFactory` – a function of type\n * `(options: BetterAuthOptions) => DBAdapter` – which is the shape expected\n * by `betterAuth({ database: … })`.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth AdapterFactory\n */\nexport function createObjectQLAdapterFactory(rawDataEngine: IDataEngine) {\n const dataEngine = withSystemReadContext(rawDataEngine);\n // Field-name bridging for better-auth plugins that expose NO `schema` option\n // (e.g. @better-auth/sso): when a model is remapped via AUTH_MODEL_TO_PROTOCOL,\n // its camelCase model fields are also converted to snake_case columns on the\n // way in and back to camelCase on the way out. SCOPED by `objectName !== model`\n // so core / schema-declared models are byte-for-byte untouched.\n const camelToSnake = (s: string): string => s.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase());\n const snakeToCamel = (s: string): string => s.replace(/_([a-z])/g, (_m, c) => c.toUpperCase());\n const remapKeys = (obj: Record<string, any>, fn: (k: string) => string): Record<string, any> => {\n const out: Record<string, any> = {};\n for (const k of Object.keys(obj)) out[fn(k)] = obj[k];\n return out;\n };\n const remapWhere = (where: CleanedWhere[]): CleanedWhere[] =>\n where.map((c) => ({ ...c, field: camelToSnake(c.field) }));\n\n return createAdapterFactory({\n config: {\n adapterId: 'objectql',\n // We let better-auth handle Date↔string and boolean↔0/1 conversion so\n // that values land in the underlying SQL driver as primitive strings\n // and integers. Some drivers (e.g. libsql over the HTTP transport)\n // otherwise mangle `Date` objects into `\"<epoch>.0\"` strings that\n // break the client-side session parser.\n supportsBooleans: false,\n supportsDates: false,\n supportsJSON: true,\n },\n adapter: () => ({\n create: async <T extends Record<string, any>>(\n { model, data, select: _select }: { model: string; data: T; select?: string[] },\n ): Promise<T> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const result = await dataEngine.insert(objectName, bridged ? remapKeys(data, camelToSnake) : data);\n const norm = normaliseLegacyDates(model, result);\n return (bridged ? remapKeys(norm, snakeToCamel) : norm) as T;\n },\n\n findOne: async <T>(\n { model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any },\n ): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = convertWhere(bridged ? remapWhere(where) : where);\n const fields = bridged && select ? select.map(camelToSnake) : select;\n\n const result = await dataEngine.findOne(objectName, { where: filter, fields });\n if (!result) return null;\n const norm = normaliseLegacyDates(model, result);\n return (bridged ? remapKeys(norm, snakeToCamel) : norm) as T;\n },\n\n findMany: async <T>(\n { model, where, limit, offset, sortBy, join: _join }: {\n model: string; where?: CleanedWhere[]; limit: number;\n offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any;\n },\n ): Promise<T[]> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = where ? convertWhere(bridged ? remapWhere(where) : where) : {};\n\n const orderBy = sortBy\n ? [{ field: bridged ? camelToSnake(sortBy.field) : sortBy.field, order: sortBy.direction as 'asc' | 'desc' }]\n : undefined;\n\n const results = await dataEngine.find(objectName, {\n where: filter,\n limit: limit || 100,\n offset,\n orderBy,\n });\n\n return results.map((r) => {\n const norm = normaliseLegacyDates(model, r as Record<string, any>);\n return bridged ? remapKeys(norm, snakeToCamel) : norm;\n }) as T[];\n },\n\n count: async (\n { model, where }: { model: string; where?: CleanedWhere[] },\n ): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = where ? convertWhere(bridged ? remapWhere(where) : where) : {};\n return await dataEngine.count(objectName, { where: filter });\n },\n\n update: async <T>(\n { model, where, update }: { model: string; where: CleanedWhere[]; update: T },\n ): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = convertWhere(bridged ? remapWhere(where) : where);\n\n // ObjectQL requires an ID for updates – find the record first\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return null;\n\n const patch = bridged ? remapKeys(update as any, camelToSnake) : (update as any);\n const result = await dataEngine.update(objectName, { ...patch, id: record.id });\n if (!result) return null;\n const norm = normaliseLegacyDates(model, result);\n return (bridged ? remapKeys(norm, snakeToCamel) : norm) as T;\n },\n\n updateMany: async (\n { model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> },\n ): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = convertWhere(bridged ? remapWhere(where) : where);\n\n // Sequential updates: ObjectQL requires an ID per update\n const records = await dataEngine.find(objectName, { where: filter });\n const patch = bridged ? remapKeys(update, camelToSnake) : update;\n for (const record of records) {\n await dataEngine.update(objectName, { ...patch, id: record.id });\n }\n return records.length;\n },\n\n delete: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<void> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = convertWhere(bridged ? remapWhere(where) : where);\n\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return;\n\n await dataEngine.delete(objectName, { where: { id: record.id } });\n },\n\n deleteMany: async (\n { model, where }: { model: string; where: CleanedWhere[] },\n ): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const bridged = objectName !== model;\n const filter = convertWhere(bridged ? remapWhere(where) : where);\n\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.delete(objectName, { where: { id: record.id } });\n }\n return records.length;\n },\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Legacy adapter (kept for backward compatibility)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a raw ObjectQL adapter for better-auth (without factory wrapping).\n *\n * > **Prefer {@link createObjectQLAdapterFactory}** for production use.\n * > The factory version leverages `createAdapterFactory` and automatically\n * > handles model-name + field-name transformations declared in the\n * > better-auth config.\n *\n * This function is retained for direct / low-level usage where callers\n * manage field-name conversion themselves.\n *\n * @param dataEngine - ObjectQL data engine instance\n * @returns better-auth CustomAdapter (raw, without factory wrapping)\n */\nexport function createObjectQLAdapter(rawDataEngine: IDataEngine) {\n const dataEngine = withSystemReadContext(rawDataEngine);\n return {\n create: async <T extends Record<string, any>>({ model, data, select: _select }: { model: string; data: T; select?: string[] }): Promise<T> => {\n const objectName = resolveProtocolName(model);\n const result = await dataEngine.insert(objectName, data);\n return result as T;\n },\n\n findOne: async <T>({ model, where, select, join: _join }: { model: string; where: CleanedWhere[]; select?: string[]; join?: any }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const result = await dataEngine.findOne(objectName, { where: filter, fields: select });\n return result ? result as T : null;\n },\n\n findMany: async <T>({ model, where, limit, offset, sortBy, join: _join }: { model: string; where?: CleanedWhere[]; limit: number; offset?: number; sortBy?: { field: string; direction: 'asc' | 'desc' }; join?: any }): Promise<T[]> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n const orderBy = sortBy ? [{ field: sortBy.field, order: sortBy.direction as 'asc' | 'desc' }] : undefined;\n const results = await dataEngine.find(objectName, { where: filter, limit: limit || 100, offset, orderBy });\n return results as T[];\n },\n\n count: async ({ model, where }: { model: string; where?: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = where ? convertWhere(where) : {};\n return await dataEngine.count(objectName, { where: filter });\n },\n\n update: async <T>({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<T | null> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return null;\n const result = await dataEngine.update(objectName, { ...update, id: record.id });\n return result ? result as T : null;\n },\n\n updateMany: async ({ model, where, update }: { model: string; where: CleanedWhere[]; update: Record<string, any> }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.update(objectName, { ...update, id: record.id });\n }\n return records.length;\n },\n\n delete: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<void> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const record = await dataEngine.findOne(objectName, { where: filter });\n if (!record) return;\n await dataEngine.delete(objectName, { where: { id: record.id } });\n },\n\n deleteMany: async ({ model, where }: { model: string; where: CleanedWhere[] }): Promise<number> => {\n const objectName = resolveProtocolName(model);\n const filter = convertWhere(where);\n const records = await dataEngine.find(objectName, { where: filter });\n for (const record of records) {\n await dataEngine.delete(objectName, { where: { id: record.id } });\n }\n return records.length;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { SystemObjectName } from '@objectstack/spec/system';\n\n/**\n * better-auth ↔ ObjectStack Schema Mapping\n *\n * better-auth uses camelCase field names internally (e.g. `emailVerified`, `userId`)\n * while ObjectStack's protocol layer uses snake_case (e.g. `email_verified`, `user_id`).\n *\n * These constants declare the `modelName` and `fields` mappings for each core auth\n * model, following better-auth's official schema customisation API\n * ({@link https://www.better-auth.com/docs/concepts/database}).\n *\n * The mappings serve two purposes:\n * 1. `modelName` — maps the default model name to the ObjectStack protocol name\n * (e.g. `user` → `sys_user`).\n * 2. `fields` — maps camelCase field names to their snake_case database column\n * equivalents. Only fields whose names differ need to be listed; fields that\n * are already identical (e.g. `email`, `name`, `token`) are omitted.\n *\n * These mappings are consumed by:\n * - The `betterAuth()` configuration in {@link AuthManager} so that\n * `getAuthTables()` builds the correct schema.\n * - The ObjectQL adapter factory (via `createAdapterFactory`) which uses the\n * schema to transform data and where-clauses automatically.\n */\n\n// ---------------------------------------------------------------------------\n// User model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `user` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | emailVerified | email_verified |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_USER_CONFIG = {\n modelName: SystemObjectName.USER, // 'sys_user'\n fields: {\n emailVerified: 'email_verified',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Session model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `session` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | ipAddress | ip_address |\n * | userAgent | user_agent |\n */\nexport const AUTH_SESSION_CONFIG = {\n modelName: SystemObjectName.SESSION, // 'sys_session'\n fields: {\n userId: 'user_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n ipAddress: 'ip_address',\n userAgent: 'user_agent',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Account model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `account` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:--------------------------|:-------------------------------|\n * | userId | user_id |\n * | providerId | provider_id |\n * | accountId | account_id |\n * | accessToken | access_token |\n * | refreshToken | refresh_token |\n * | idToken | id_token |\n * | accessTokenExpiresAt | access_token_expires_at |\n * | refreshTokenExpiresAt | refresh_token_expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ACCOUNT_CONFIG = {\n modelName: SystemObjectName.ACCOUNT, // 'sys_account'\n fields: {\n userId: 'user_id',\n providerId: 'provider_id',\n accountId: 'account_id',\n accessToken: 'access_token',\n refreshToken: 'refresh_token',\n idToken: 'id_token',\n accessTokenExpiresAt: 'access_token_expires_at',\n refreshTokenExpiresAt: 'refresh_token_expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Verification model\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `verification` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_VERIFICATION_CONFIG = {\n modelName: SystemObjectName.VERIFICATION, // 'sys_verification'\n fields: {\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ===========================================================================\n// Plugin Table Mappings\n// ===========================================================================\n//\n// better-auth plugins (organization, two-factor, etc.) introduce additional\n// tables with their own camelCase field names. The mappings below are passed\n// to the plugin's `schema` option so that `createAdapterFactory` transforms\n// them to snake_case automatically, just like the core models above.\n// ===========================================================================\n\n// ---------------------------------------------------------------------------\n// Organization plugin – organization table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `organization` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_ORGANIZATION_SCHEMA = {\n modelName: SystemObjectName.ORGANIZATION, // 'sys_organization'\n fields: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – member table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `member` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_MEMBER_SCHEMA = {\n modelName: SystemObjectName.MEMBER, // 'sys_member'\n fields: {\n organizationId: 'organization_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – invitation table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `invitation` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | inviterId | inviter_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | teamId | team_id |\n */\nexport const AUTH_INVITATION_SCHEMA = {\n modelName: SystemObjectName.INVITATION, // 'sys_invitation'\n fields: {\n organizationId: 'organization_id',\n inviterId: 'inviter_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n teamId: 'team_id',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – session additional fields\n// ---------------------------------------------------------------------------\n\n/**\n * Organization plugin adds `activeOrganizationId` (and optionally\n * `activeTeamId`) to the session model. These field mappings are\n * injected via the organization plugin's `schema.session.fields`.\n */\nexport const AUTH_ORG_SESSION_FIELDS = {\n activeOrganizationId: 'active_organization_id',\n activeTeamId: 'active_team_id',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – team table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `team` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | organizationId | organization_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_TEAM_SCHEMA = {\n modelName: SystemObjectName.TEAM, // 'sys_team'\n fields: {\n organizationId: 'organization_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Organization plugin – teamMember table (optional, when teams enabled)\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Organization plugin `teamMember` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | teamId | team_id |\n * | userId | user_id |\n * | createdAt | created_at |\n */\nexport const AUTH_TEAM_MEMBER_SCHEMA = {\n modelName: SystemObjectName.TEAM_MEMBER, // 'sys_team_member'\n fields: {\n teamId: 'team_id',\n userId: 'user_id',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Two-Factor plugin – twoFactor table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth Two-Factor plugin `twoFactor` model mapping.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | backupCodes | backup_codes |\n * | userId | user_id |\n */\nexport const AUTH_TWO_FACTOR_SCHEMA = {\n modelName: SystemObjectName.TWO_FACTOR, // 'sys_two_factor'\n fields: {\n backupCodes: 'backup_codes',\n userId: 'user_id',\n },\n} as const;\n\n/**\n * Two-Factor plugin adds a `twoFactorEnabled` field to the user model.\n */\nexport const AUTH_TWO_FACTOR_USER_FIELDS = {\n twoFactorEnabled: 'two_factor_enabled',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Admin plugin – user/session field additions\n// ---------------------------------------------------------------------------\n\n/**\n * Admin plugin adds platform-level admin fields to the `user` model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | banReason | ban_reason |\n * | banExpires | ban_expires |\n *\n * `role` and `banned` already have matching snake_case names and are\n * therefore omitted from this mapping (better-auth's database hooks\n * read them by the auto-derived column names).\n */\nexport const AUTH_ADMIN_USER_FIELDS = {\n banReason: 'ban_reason',\n banExpires: 'ban_expires',\n} as const;\n\n/**\n * Admin plugin adds an `impersonatedBy` field to the session model\n * recording the operator user id when an admin impersonates someone.\n */\nexport const AUTH_ADMIN_SESSION_FIELDS = {\n impersonatedBy: 'impersonated_by',\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthClient table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthClient` model mapping.\n *\n * The model name (`oauthClient`) is mapped to the existing\n * `sys_oauth_application` table to preserve data continuity from the\n * deprecated `oidc-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:---------------------------|:--------------------------------|\n * | clientId | client_id |\n * | clientSecret | client_secret |\n * | skipConsent | skip_consent |\n * | enableEndSession | enable_end_session |\n * | subjectType | subject_type |\n * | userId | user_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n * | redirectUris | redirect_uris |\n * | postLogoutRedirectUris | post_logout_redirect_uris |\n * | tokenEndpointAuthMethod | token_endpoint_auth_method |\n * | grantTypes | grant_types |\n * | responseTypes | response_types |\n * | requirePKCE | require_pkce |\n * | softwareId | software_id |\n * | softwareVersion | software_version |\n * | softwareStatement | software_statement |\n * | referenceId | reference_id |\n */\nexport const AUTH_OAUTH_CLIENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_APPLICATION, // 'sys_oauth_application'\n fields: {\n clientId: 'client_id',\n clientSecret: 'client_secret',\n skipConsent: 'skip_consent',\n enableEndSession: 'enable_end_session',\n subjectType: 'subject_type',\n userId: 'user_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n redirectUris: 'redirect_uris',\n postLogoutRedirectUris: 'post_logout_redirect_uris',\n tokenEndpointAuthMethod: 'token_endpoint_auth_method',\n grantTypes: 'grant_types',\n responseTypes: 'response_types',\n requirePKCE: 'require_pkce',\n softwareId: 'software_id',\n softwareVersion: 'software_version',\n softwareStatement: 'software_statement',\n referenceId: 'reference_id',\n },\n} as const;\n\n/**\n * @deprecated Use {@link AUTH_OAUTH_CLIENT_SCHEMA}. Retained as an alias for\n * historical imports; the new package renamed `oauthApplication` → `oauthClient`.\n */\nexport const AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthAccessToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthAccessToken` model mapping.\n *\n * In the new package, access tokens and refresh tokens are stored in\n * **separate** models. `oauthAccessToken` no longer carries a refresh token;\n * see {@link AUTH_OAUTH_REFRESH_TOKEN_SCHEMA} for the companion model.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | refreshId | refresh_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n */\nexport const AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_ACCESS_TOKEN, // 'sys_oauth_access_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n refreshId: 'refresh_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthRefreshToken table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthRefreshToken` model mapping.\n *\n * Refresh tokens are linked to a session (via `session_id`) and to the\n * issuing client. Each access token rotation produces a new refresh-token\n * row.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | sessionId | session_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | expiresAt | expires_at |\n * | createdAt | created_at |\n * | authTime | auth_time |\n */\nexport const AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {\n modelName: SystemObjectName.OAUTH_REFRESH_TOKEN, // 'sys_oauth_refresh_token'\n fields: {\n clientId: 'client_id',\n sessionId: 'session_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n expiresAt: 'expires_at',\n createdAt: 'created_at',\n authTime: 'auth_time',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// OAuth Provider plugin – oauthConsent table\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/oauth-provider` plugin `oauthConsent` model mapping.\n *\n * The new package dropped the boolean `consentGiven` flag — the presence of\n * a row implies consent was given for the listed scopes. A new\n * `referenceId` column was added for client-supplied correlation.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | clientId | client_id |\n * | userId | user_id |\n * | referenceId | reference_id |\n * | createdAt | created_at |\n * | updatedAt | updated_at |\n */\nexport const AUTH_OAUTH_CONSENT_SCHEMA = {\n modelName: SystemObjectName.OAUTH_CONSENT, // 'sys_oauth_consent'\n fields: {\n clientId: 'client_id',\n userId: 'user_id',\n referenceId: 'reference_id',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Device Authorization plugin – deviceCode table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `device-authorization` plugin `deviceCode` model mapping.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant). Stores\n * pending device-flow requests issued via `POST /device/code`, polled at\n * `POST /device/token`, and approved/denied via `POST /device/{approve,deny}`.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | deviceCode | device_code |\n * | userCode | user_code |\n * | userId | user_id |\n * | expiresAt | expires_at |\n * | lastPolledAt | last_polled_at |\n * | pollingInterval | polling_interval |\n * | clientId | client_id |\n */\nexport const AUTH_DEVICE_CODE_SCHEMA = {\n modelName: SystemObjectName.DEVICE_CODE, // 'sys_device_code'\n fields: {\n deviceCode: 'device_code',\n userCode: 'user_code',\n userId: 'user_id',\n expiresAt: 'expires_at',\n lastPolledAt: 'last_polled_at',\n pollingInterval: 'polling_interval',\n clientId: 'client_id',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `twoFactor()` plugin.\n *\n * @returns An object suitable for `twoFactor({ schema: … })`\n */\nexport function buildTwoFactorPluginSchema() {\n return {\n twoFactor: AUTH_TWO_FACTOR_SCHEMA,\n user: {\n fields: AUTH_TWO_FACTOR_USER_FIELDS,\n },\n };\n}\n\n/**\n * Builds the `schema` option for better-auth's `admin()` plugin.\n *\n * The admin plugin extends the user model with `role`/`banned`/`banReason`/\n * `banExpires` and the session model with `impersonatedBy`. Only the\n * snake_case-differing fields are mapped explicitly.\n */\nexport function buildAdminPluginSchema() {\n return {\n user: {\n fields: AUTH_ADMIN_USER_FIELDS,\n },\n session: {\n fields: AUTH_ADMIN_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build organization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `organization()` plugin.\n *\n * The organization plugin accepts a `schema` sub-option that allows\n * customising model names and field names for each table it manages.\n * This helper assembles the correct snake_case mappings from the\n * individual `AUTH_*_SCHEMA` constants above.\n *\n * @returns An object suitable for `organization({ schema: … })`\n */\nexport function buildOrganizationPluginSchema() {\n return {\n organization: AUTH_ORGANIZATION_SCHEMA,\n member: AUTH_MEMBER_SCHEMA,\n invitation: AUTH_INVITATION_SCHEMA,\n team: AUTH_TEAM_SCHEMA,\n teamMember: AUTH_TEAM_MEMBER_SCHEMA,\n session: {\n fields: AUTH_ORG_SESSION_FIELDS,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// JWT plugin – jwks table\n// ---------------------------------------------------------------------------\n\n/**\n * better-auth `jwt` plugin `jwks` model mapping.\n *\n * The JWT plugin maintains a small set of rotating asymmetric key pairs\n * used to sign and verify issued JWTs (id_tokens for OIDC, JWT access\n * tokens). It is required by the `@better-auth/oauth-provider` plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | publicKey | public_key |\n * | privateKey | private_key |\n * | createdAt | created_at |\n * | expiresAt | expires_at |\n */\nexport const AUTH_JWKS_SCHEMA = {\n modelName: SystemObjectName.JWKS, // 'sys_jwks'\n fields: {\n publicKey: 'public_key',\n privateKey: 'private_key',\n createdAt: 'created_at',\n expiresAt: 'expires_at',\n },\n} as const;\n\n/**\n * Builds the `schema` option for better-auth's `jwt()` plugin.\n *\n * @returns An object suitable for `jwt({ schema: … })`\n */\nexport function buildJwtPluginSchema() {\n return {\n jwks: AUTH_JWKS_SCHEMA,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build OAuth provider plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for `@better-auth/oauth-provider`'s\n * `oauthProvider()` plugin.\n *\n * The plugin manages four tables: `oauthClient` (registered client apps —\n * mapped to ObjectStack's `sys_oauth_application` table for backwards\n * compatibility), `oauthAccessToken` (issued access tokens),\n * `oauthRefreshToken` (issued refresh tokens, linked to a session), and\n * `oauthConsent` (recorded user consents).\n *\n * @returns An object suitable for `oauthProvider({ schema: … })`\n */\nexport function buildOauthProviderPluginSchema() {\n return {\n oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,\n oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,\n oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,\n oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA,\n };\n}\n\n/**\n * @deprecated Use {@link buildOauthProviderPluginSchema}. Retained as an\n * alias for callers that imported the previous name during the migration\n * from the deprecated `better-auth/plugins/oidc-provider` plugin.\n */\nexport const buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;\n\n// ---------------------------------------------------------------------------\n// SSO plugin – ssoProvider table (@better-auth/sso)\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/sso` plugin `ssoProvider` model mapping.\n *\n * Each row is an external OIDC/SAML IdP this environment federates login to\n * (the relying-party side — ADR-0024's OPEN per-env SSO mechanism). The\n * protocol detail lives in JSON blobs (`oidcConfig` / `samlConfig`); the model\n * itself is thin. Mirrors @better-auth/sso@1.6.20's `BaseSSOProvider`.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | providerId | provider_id |\n * | oidcConfig | oidc_config |\n * | samlConfig | saml_config |\n * | userId | user_id |\n * | organizationId | organization_id |\n * | issuer / domain | (same name — no remap) |\n */\nexport const AUTH_SSO_PROVIDER_SCHEMA = {\n modelName: 'sys_sso_provider',\n fields: {\n providerId: 'provider_id',\n oidcConfig: 'oidc_config',\n samlConfig: 'saml_config',\n userId: 'user_id',\n organizationId: 'organization_id',\n // DNS domain-ownership proof (ADR-0024 ②). @better-auth/sso writes\n // `domainVerified` on its `ssoProvider` model when domain verification is\n // enabled; map it so the env can surface a verified/unverified badge. The\n // one-time `domainVerificationToken` is NOT a provider column — it lives in\n // the verification table and is returned only from request-domain-verification.\n domainVerified: 'domain_verified',\n },\n} as const;\n\n// NOTE: there is intentionally no `buildSsoPluginSchema()`. Unlike\n// `oauthProvider`, the @better-auth/sso plugin exposes NO `schema` option\n// (verified vs 1.6.20), so the mapping above cannot be handed to the plugin —\n// it must be consumed at the ADAPTER layer (AUTH_MODEL_TO_PROTOCOL + field\n// resolution in objectql-adapter.ts). See ADR-0024.\n\n// ---------------------------------------------------------------------------\n// SCIM plugin – scimProvider table (@better-auth/scim)\n// ---------------------------------------------------------------------------\n\n/**\n * `@better-auth/scim` plugin `scimProvider` model mapping.\n *\n * Each row is a SCIM connection: a bearer token an external IdP (Okta / Entra)\n * uses to auto-provision / deprovision THIS environment's users — the env is\n * the SCIM Service Provider (ADR-0071). Like `@better-auth/sso`, the plugin\n * hardcodes its model and exposes NO `schema` option, so the mapping is\n * consumed at the ADAPTER layer (AUTH_MODEL_TO_PROTOCOL + field resolution in\n * objectql-adapter.ts), NOT handed to the plugin.\n *\n * | camelCase (better-auth) | snake_case (ObjectStack) |\n * |:------------------------|:-------------------------|\n * | providerId | provider_id |\n * | scimToken | scim_token |\n * | organizationId | organization_id |\n * | userId | user_id |\n */\nexport const AUTH_SCIM_PROVIDER_SCHEMA = {\n modelName: 'sys_scim_provider',\n fields: {\n providerId: 'provider_id',\n scimToken: 'scim_token',\n organizationId: 'organization_id',\n userId: 'user_id',\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Helper: build device-authorization plugin schema option\n// ---------------------------------------------------------------------------\n\n/**\n * Builds the `schema` option for better-auth's `deviceAuthorization()` plugin.\n *\n * The plugin manages a single `deviceCode` table tracking pending RFC 8628\n * device-flow requests. This helper returns the snake_case mappings that\n * point the plugin at ObjectStack's `sys_device_code` object.\n *\n * @returns An object suitable for `deviceAuthorization({ schema: … })`\n */\nexport function buildDeviceAuthorizationPluginSchema() {\n return {\n deviceCode: AUTH_DEVICE_CODE_SCHEMA,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared `set-initial-password` handler.\n *\n * better-auth ships a `setPassword` operation that does EXACTLY what we want\n * (require a session, enforce min/max length, link a `credential` account if\n * none exists, refuse if one already does). But it is registered with\n * `createAuthEndpoint({ ... })` — note: NO leading path string — which means\n * better-auth deliberately exposes it as a **server-only** `auth.api.setPassword`\n * call and gives it no HTTP route. Setting a password without proving the old\n * one is privilege-sensitive, so it must not be reachable over the wire by\n * default.\n *\n * To let an SSO-onboarded user set an *initial* local password from the\n * browser, we wrap that server API in our own authenticated HTTP route. This\n * helper is the single source of truth for that route body so the two mount\n * points — the full `AuthPlugin` (host kernel) and the cloud `AuthProxyPlugin`\n * (per-environment runtime) — stay in lockstep instead of hand-copying ~50\n * lines of hash/createAccount logic (the original drift that let #1544 ship a\n * route on one path but not the other).\n */\n\n/** Minimal shape of the better-auth server API we depend on. */\nexport interface SetPasswordCapableApi {\n setPassword(opts: { body: { newPassword: string }; headers: Headers }): Promise<unknown>;\n}\n\nexport interface SetInitialPasswordResult {\n /** HTTP status to return to the caller. */\n status: number;\n /** JSON body; mirrors the `{ success, error: { code, message } }` envelope the client parses. */\n body: { success: boolean; error?: { code: string; message: string } };\n}\n\n/**\n * Run set-initial-password against the environment's better-auth API.\n *\n * @param authApi the better-auth server api (`auth.api`, via `AuthManager.getApi()`)\n * @param request the raw Web `Request` — its `headers` carry the session\n * cookie that better-auth's session middleware reads, and its\n * body carries `{ newPassword }`.\n */\nexport async function runSetInitialPassword(\n authApi: SetPasswordCapableApi,\n request: Request,\n): Promise<SetInitialPasswordResult> {\n let parsed: unknown;\n try {\n parsed = await request.json();\n } catch {\n parsed = {};\n }\n const newPassword: unknown = (parsed as { newPassword?: unknown } | null)?.newPassword;\n if (typeof newPassword !== 'string' || newPassword.length === 0) {\n return {\n status: 400,\n body: { success: false, error: { code: 'invalid_request', message: 'newPassword is required' } },\n };\n }\n\n try {\n // better-auth's session middleware reads the session from `headers`;\n // length checks + the \"already set\" guard happen inside setPassword.\n await authApi.setPassword({ body: { newPassword }, headers: request.headers });\n return { status: 200, body: { success: true } };\n } catch (error) {\n return mapSetPasswordError(error);\n }\n}\n\n/**\n * Map a better-auth `APIError` (better-call: `{ statusCode, status, body: { code, message } }`)\n * onto our response envelope. The client only surfaces `error.message`, but we\n * preserve the status code and code string for parity with the change/reset\n * flows. `PASSWORD_ALREADY_SET` is normalised to 409 so callers can tell\n * \"already has a password → use change-password\" apart from validation errors.\n */\nfunction mapSetPasswordError(error: unknown): SetInitialPasswordResult {\n const e = error as {\n statusCode?: number;\n status?: number | string;\n body?: { code?: string; message?: string };\n message?: string;\n } | null;\n\n const code = e?.body?.code ?? 'internal';\n const message = e?.body?.message ?? e?.message ?? 'set-initial-password failed';\n const rawStatus =\n typeof e?.statusCode === 'number' ? e.statusCode : typeof e?.status === 'number' ? e.status : 500;\n const status = code === 'PASSWORD_ALREADY_SET' ? 409 : rawStatus;\n\n return { status, body: { success: false, error: { code, message } } };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared `register-sso-provider` (form) handler.\n *\n * `@better-auth/sso`'s `POST /sso/register` expects the OIDC protocol fields\n * NESTED under `oidcConfig` ({ clientId, clientSecret, discoveryEndpoint,\n * scopes, mapping }). The `sys_sso_provider` `register_sso_provider` UI action\n * collects FLAT form fields (the action param schema has no nested-path\n * support), so posting them straight to `/sso/register` drops\n * clientId/clientSecret at the top level (Zod-stripped) and persists an\n * unusable `oidc_config = null` provider that can never complete a login\n * (ADR-0024).\n *\n * This helper reshapes the flat form body into the nested shape and\n * RE-DISPATCHES it through the real `/sso/register` endpoint (via the\n * better-auth universal handler passed in) so the admin gate, the\n * public-routable `trustedOrigins` allowance, discovery hydration, and secret\n * handling all still run — no logic is duplicated. It is the single source of\n * truth for the two mount points that must stay in lockstep: the full\n * `AuthPlugin` (self-host / OSS host kernel) and the cloud `AuthProxyPlugin`\n * (per-environment runtime) — mirroring `runSetInitialPassword`.\n */\n\nexport interface RegisterSsoFormResult {\n /** HTTP status to return to the caller. */\n status: number;\n /** JSON body; mirrors the `{ success, data?, error? }` envelope the client parses. */\n body: {\n success: boolean;\n data?: { providerId: string };\n error?: { code: string; message: string };\n };\n}\n\n/** A better-auth universal handler: `(request) => Response`. */\nexport type AuthRequestHandler = (request: Request) => Promise<Response>;\n\n/**\n * Resolve the caller's active organization id by re-dispatching a\n * `/get-session` through the same better-auth handler. Returns `undefined` on\n * any failure / when no active org is set — callers fall back to an org-less\n * (registrar-only) provider, so this is strictly best-effort. `registerUrl` is\n * the resolved `…/sso/register` URL; we swap the trailing path for\n * `…/get-session` on the same origin/basePath.\n */\nasync function resolveActiveOrganizationId(\n handle: AuthRequestHandler,\n registerUrl: string,\n headers: Headers,\n): Promise<string | undefined> {\n try {\n const sessionUrl = registerUrl.replace(/\\/sso\\/register$/, '/get-session');\n if (sessionUrl === registerUrl) return undefined;\n const h = new Headers({ accept: 'application/json' });\n const cookie = headers.get('cookie');\n if (cookie) h.set('cookie', cookie);\n const authz = headers.get('authorization');\n if (authz) h.set('authorization', authz);\n const resp = await handle(new Request(sessionUrl, { method: 'GET', headers: h }));\n if (!resp.ok) return undefined;\n const data: any = await resp.json().catch(() => null);\n const org = data?.session?.activeOrganizationId ?? data?.activeOrganizationId;\n return typeof org === 'string' && org.length > 0 ? org : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Reshape a flat SSO-provider registration form body and register it.\n *\n * @param handle the better-auth universal handler (`AuthManager.handleRequest`\n * on the host kernel, or the resolved per-env handler in the\n * cloud proxy). Used to re-dispatch the nested body to the real\n * `/sso/register` route so all of its gates run.\n * @param request the raw Web `Request` — its headers carry the caller's session\n * cookie / bearer + Origin; its body carries the flat form\n * fields ({ providerId, issuer, domain, clientId, clientSecret,\n * discoveryEndpoint?, scopes?, mapId?, mapEmail?, mapName? }).\n */\nexport async function runRegisterSsoProviderFromForm(\n handle: AuthRequestHandler,\n request: Request,\n): Promise<RegisterSsoFormResult> {\n let body: any;\n try {\n body = await request.json();\n } catch {\n body = {};\n }\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const providerId = str(body?.providerId);\n const issuer = str(body?.issuer);\n const domain = str(body?.domain);\n const clientId = str(body?.clientId);\n const clientSecret = str(body?.clientSecret);\n const discoveryEndpoint = str(body?.discoveryEndpoint);\n const scopesRaw = str(body?.scopes);\n\n const missing = (\n [\n ['providerId', providerId],\n ['issuer', issuer],\n ['domain', domain],\n ['clientId', clientId],\n ['clientSecret', clientSecret],\n ] as const\n )\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) {\n return {\n status: 400,\n body: { success: false, error: { code: 'invalid_request', message: `Missing required field(s): ${missing.join(', ')}` } },\n };\n }\n\n const oidcConfig: Record<string, unknown> = { clientId, clientSecret };\n if (discoveryEndpoint) oidcConfig.discoveryEndpoint = discoveryEndpoint;\n oidcConfig.scopes = scopesRaw ? scopesRaw.split(/[\\s,]+/).filter(Boolean) : ['openid', 'email', 'profile'];\n oidcConfig.mapping = {\n id: str(body?.mapId) || 'sub',\n email: str(body?.mapEmail) || 'email',\n name: str(body?.mapName) || 'name',\n };\n\n // Re-dispatch to the real /sso/register (same origin, sibling path) so the\n // admin gate + public-IdP trustedOrigins allowance + discovery hydration run.\n let innerUrl: string;\n let origin: string;\n try {\n const url = new URL(request.url);\n origin = url.origin;\n innerUrl = `${origin}${url.pathname.replace(/\\/admin\\/sso\\/register$/, '/sso/register')}`;\n } catch {\n return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Bad request URL' } } };\n }\n const headers = new Headers({ 'content-type': 'application/json' });\n const cookie = request.headers.get('cookie');\n if (cookie) headers.set('cookie', cookie);\n const authz = request.headers.get('authorization');\n if (authz) headers.set('authorization', authz);\n headers.set('origin', request.headers.get('origin') || origin);\n\n // Org-scope the provider to the caller's active organization (best-effort).\n // `@better-auth/sso`'s management endpoints (delete / update / domain\n // verification) gate org-scoped providers on `isOrgAdmin` but gate ORG-LESS\n // ones on `provider.userId === caller` — i.e. only the original registrar can\n // manage them. Scoping to the org means ANY org owner/admin can manage the\n // env's IdPs (the env is single-org in V1). Resolved by re-dispatching a\n // `/get-session` through the same handler; falls back to org-less (no\n // regression) when no active org is set.\n const organizationId = await resolveActiveOrganizationId(handle, innerUrl, headers);\n\n const innerReq = new Request(innerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ providerId, issuer, domain, oidcConfig, ...(organizationId ? { organizationId } : {}) }),\n });\n\n const resp = await handle(innerReq);\n let parsed: any = {};\n try {\n const t = await resp.text();\n parsed = t ? JSON.parse(t) : {};\n } catch {\n parsed = {};\n }\n if (!resp.ok) {\n return {\n status: resp.status,\n body: { success: false, error: { code: 'sso_register_failed', message: parsed?.message || 'SSO provider registration failed' } },\n };\n }\n return { status: 200, body: { success: true, data: { providerId: parsed?.providerId ?? providerId } } };\n}\n\n\n/**\n * ADR-0069 P3 — SAML 2.0 sibling of {@link runRegisterSsoProviderFromForm}.\n *\n * `@better-auth/sso` (samlify-backed) registers a SAML IdP via the SAME\n * `/sso/register` endpoint, with the protocol fields nested under `samlConfig`\n * ({ entryPoint, cert, callbackUrl, identifierFormat? }) instead of `oidcConfig`.\n * The UI action collects FLAT fields; this helper reshapes them, derives the\n * per-provider ACS callback URL (`/sso/saml2/sp/acs/<providerId>`), and\n * re-dispatches through `/sso/register` so the admin gate + provisioning run.\n * Returns the SP ACS + metadata URLs the admin must configure on the IdP.\n */\nexport async function runRegisterSamlProviderFromForm(\n handle: AuthRequestHandler,\n request: Request,\n): Promise<RegisterSsoFormResult & { body: RegisterSsoFormResult['body'] & { acsUrl?: string; spMetadataUrl?: string } }> {\n let body: any;\n try { body = await request.json(); } catch { body = {}; }\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const providerId = str(body?.providerId);\n const issuer = str(body?.issuer);\n const domain = str(body?.domain);\n const entryPoint = str(body?.entryPoint);\n const cert = str(body?.cert);\n const identifierFormat = str(body?.identifierFormat);\n\n const missing = (\n [\n ['providerId', providerId],\n ['issuer', issuer],\n ['domain', domain],\n ['entryPoint', entryPoint],\n ['cert', cert],\n ] as const\n ).filter(([, v]) => !v).map(([k]) => k);\n if (missing.length) {\n return { status: 400, body: { success: false, error: { code: 'invalid_request', message: `Missing required field(s): ${missing.join(', ')}` } } };\n }\n\n let origin: string;\n let prefix: string;\n let innerUrl: string;\n try {\n const url = new URL(request.url);\n origin = url.origin;\n prefix = url.pathname.replace(/\\/admin\\/sso\\/register-saml$/, '');\n innerUrl = `${origin}${prefix}/sso/register`;\n } catch {\n return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Bad request URL' } } };\n }\n const acsUrl = `${origin}${prefix}/sso/saml2/sp/acs/${encodeURIComponent(providerId)}`;\n const spMetadataUrl = `${origin}${prefix}/sso/saml2/sp/metadata?providerId=${encodeURIComponent(providerId)}`;\n\n const samlConfig: Record<string, unknown> = {\n entryPoint,\n cert,\n callbackUrl: acsUrl,\n // better-auth requires an SP descriptor (its inner fields are optional). Use\n // the SP metadata URL as our EntityID — the value the IdP keys this SP on.\n spMetadata: { entityID: spMetadataUrl },\n };\n if (identifierFormat) samlConfig.identifierFormat = identifierFormat;\n\n const headers = new Headers({ 'content-type': 'application/json' });\n const cookie = request.headers.get('cookie');\n if (cookie) headers.set('cookie', cookie);\n const authz = request.headers.get('authorization');\n if (authz) headers.set('authorization', authz);\n headers.set('origin', request.headers.get('origin') || origin);\n\n // Org-scope to the caller's active org (best-effort) so any org owner/admin\n // can manage the provider — see the OIDC helper above.\n const organizationId = await resolveActiveOrganizationId(handle, innerUrl, headers);\n\n const innerReq = new Request(innerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ providerId, issuer, domain, samlConfig, ...(organizationId ? { organizationId } : {}) }),\n });\n const resp = await handle(innerReq);\n let parsed: any = {};\n try { const t = await resp.text(); parsed = t ? JSON.parse(t) : {}; } catch { parsed = {}; }\n if (!resp.ok) {\n return { status: resp.status, body: { success: false, error: { code: 'saml_register_failed', message: parsed?.message || 'SAML provider registration failed' } } };\n }\n return { status: 200, body: { success: true, data: { providerId: parsed?.providerId ?? providerId }, acsUrl, spMetadataUrl } };\n}\n\n\n// ── Domain verification (ADR-0024 ②, opt-in OS_SSO_DOMAIN_VERIFICATION) ──────\n//\n// `@better-auth/sso` proves an external IdP's email DOMAIN is controlled by the\n// registrant via a DNS-TXT challenge, mounted ONLY when `domainVerification` is\n// enabled on `sso()`. The two endpoints are:\n// • POST /sso/request-domain-verification {providerId} → 201 {domainVerificationToken}\n// • POST /sso/verify-domain {providerId} → 204 (or 502 if the TXT\n// record is absent / not yet propagated)\n// The token alone is not actionable — the admin needs the full DNS record\n// (name `_better-auth-token-<providerId>.<domain>`, value\n// `_better-auth-token-<providerId>=<token>`; the prefix is @better-auth/sso's\n// default `tokenPrefix`, which we do not override). These bridges re-dispatch\n// through the real endpoints (so the per-provider admin gate runs) and reshape\n// the response into the `{ success, data }` envelope the action `resultDialog`\n// reads — request returns the ready-to-paste DNS record; verify returns a\n// friendly success/error message. A `404` from the inner endpoint means the\n// feature is OFF for this env (endpoints unmounted) → surfaced as such, not a\n// bare \"not found\".\n\n/** @better-auth/sso default verification token prefix (we don't override `tokenPrefix`). */\nconst SSO_DOMAIN_TOKEN_PREFIX = 'better-auth-token';\n\n/**\n * Strip protocol / path / port so `https://acme.com/` → `acme.com` for the DNS\n * record. Regex-free on purpose — `domain` is request-controlled input, so a\n * backtracking pattern here would be a ReDoS vector (CodeQL js/polynomial-redos).\n */\nfunction bareHostname(domain: string): string {\n let d = domain.trim();\n if (!d) return d;\n const schemeIdx = d.indexOf('://');\n if (schemeIdx !== -1) {\n try {\n return new URL(d).hostname;\n } catch {\n d = d.slice(schemeIdx + 3); // malformed URL — drop the scheme and strip manually\n }\n }\n // Truncate at the first path / port / query / fragment separator.\n for (const sep of ['/', ':', '?', '#']) {\n const i = d.indexOf(sep);\n if (i !== -1) d = d.slice(0, i);\n }\n return d;\n}\n\nfunction rewriteSsoAdminUrl(request: Request, fromSuffix: RegExp, toPath: string): { innerUrl: string; origin: string } | null {\n try {\n const url = new URL(request.url);\n return { origin: url.origin, innerUrl: `${url.origin}${url.pathname.replace(fromSuffix, toPath)}` };\n } catch {\n return null;\n }\n}\n\nfunction forwardAuthHeaders(request: Request, origin: string): Headers {\n const headers = new Headers({ 'content-type': 'application/json' });\n const cookie = request.headers.get('cookie');\n if (cookie) headers.set('cookie', cookie);\n const authz = request.headers.get('authorization');\n if (authz) headers.set('authorization', authz);\n headers.set('origin', request.headers.get('origin') || origin);\n return headers;\n}\n\n/**\n * Request a DNS-TXT domain-verification challenge for a registered provider and\n * return the ready-to-paste DNS record (for a one-shot `resultDialog`).\n *\n * Body: `{ providerId, domain? }` (domain only shapes the displayed record name).\n */\nexport async function runRequestDomainVerification(\n handle: AuthRequestHandler,\n request: Request,\n): Promise<RegisterSsoFormResult & { body: RegisterSsoFormResult['body'] & { data?: any } }> {\n let body: any;\n try { body = await request.json(); } catch { body = {}; }\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const providerId = str(body?.providerId);\n const domain = bareHostname(str(body?.domain));\n if (!providerId) {\n return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Missing required field: providerId' } } };\n }\n\n const rw = rewriteSsoAdminUrl(request, /\\/admin\\/sso\\/request-domain-verification$/, '/sso/request-domain-verification');\n if (!rw) return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Bad request URL' } } };\n const headers = forwardAuthHeaders(request, rw.origin);\n\n const resp = await handle(new Request(rw.innerUrl, { method: 'POST', headers, body: JSON.stringify({ providerId }) }));\n let parsed: any = {};\n try { const t = await resp.text(); parsed = t ? JSON.parse(t) : {}; } catch { parsed = {}; }\n if (!resp.ok) {\n if (resp.status === 404 && !parsed?.code) {\n return { status: 400, body: { success: false, error: { code: 'domain_verification_disabled', message: 'Domain verification is not enabled for this environment (set OS_SSO_DOMAIN_VERIFICATION).' } } };\n }\n return { status: resp.status, body: { success: false, error: { code: parsed?.code || 'request_domain_verification_failed', message: parsed?.message || 'Failed to request domain verification' } } };\n }\n\n const token = str(parsed?.domainVerificationToken);\n const label = `_${SSO_DOMAIN_TOKEN_PREFIX}-${providerId}`;\n const dnsRecordName = domain ? `${label}.${domain}` : label;\n const dnsRecordValue = `${label}=${token}`;\n return {\n status: 200,\n body: {\n success: true,\n data: { providerId, domain, token, dnsRecordType: 'TXT', dnsRecordName, dnsRecordValue },\n },\n };\n}\n\n/**\n * Verify a provider's domain ownership (re-checks the DNS-TXT record). Reshapes\n * @better-auth/sso's empty `204` / `502` into a `{ success, data:{ message } }`\n * envelope so the action surfaces a clear toast.\n *\n * Body: `{ providerId }`.\n */\nexport async function runVerifyDomain(\n handle: AuthRequestHandler,\n request: Request,\n): Promise<RegisterSsoFormResult & { body: RegisterSsoFormResult['body'] & { data?: any } }> {\n let body: any;\n try { body = await request.json(); } catch { body = {}; }\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const providerId = str(body?.providerId);\n if (!providerId) {\n return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Missing required field: providerId' } } };\n }\n\n const rw = rewriteSsoAdminUrl(request, /\\/admin\\/sso\\/verify-domain$/, '/sso/verify-domain');\n if (!rw) return { status: 400, body: { success: false, error: { code: 'invalid_request', message: 'Bad request URL' } } };\n const headers = forwardAuthHeaders(request, rw.origin);\n\n const resp = await handle(new Request(rw.innerUrl, { method: 'POST', headers, body: JSON.stringify({ providerId }) }));\n let parsed: any = {};\n try { const t = await resp.text(); parsed = t ? JSON.parse(t) : {}; } catch { parsed = {}; }\n if (resp.ok) {\n return { status: 200, body: { success: true, data: { providerId, verified: true, message: 'Domain ownership verified — this provider can now sign users in.' } } };\n }\n // Friendlier copy for the expected failure modes.\n let message = parsed?.message || 'Domain verification failed';\n if (resp.status === 404 && !parsed?.code) {\n message = 'Domain verification is not enabled for this environment (set OS_SSO_DOMAIN_VERIFICATION).';\n } else if (parsed?.code === 'NO_PENDING_VERIFICATION') {\n message = 'No pending verification — click “Request Domain Verification” first to get the DNS record.';\n } else if (parsed?.code === 'DOMAIN_VERIFICATION_FAILED') {\n message = 'DNS TXT record not found yet. Add the record shown when you requested verification, allow time for DNS to propagate, then retry.';\n }\n return { status: resp.status, body: { success: false, error: { code: parsed?.code || 'verify_domain_failed', message } } };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-auth manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `auth-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysAccount,\n SysApiKey,\n SysDeviceCode,\n SysInvitation,\n SysMember,\n SysJwks,\n SysOauthAccessToken,\n SysOauthApplication,\n SysOauthConsent,\n SysOauthRefreshToken,\n SysOrganization,\n SysSession,\n SysSsoProvider,\n SysScimProvider,\n SysTeam,\n SysTeamMember,\n SysTwoFactor,\n SysUser,\n SysUserPreference,\n SysVerification,\n} from '@objectstack/platform-objects/identity';\n\nexport const AUTH_PLUGIN_ID = 'com.objectstack.plugin-auth';\nexport const AUTH_PLUGIN_VERSION = '3.0.1';\n\n/** Identity objects owned by plugin-auth. */\nexport const authIdentityObjects: any[] = [\n SysUser,\n SysSession,\n SysAccount,\n SysVerification,\n SysOrganization,\n SysMember,\n SysInvitation,\n SysTeam,\n SysTeamMember,\n SysApiKey,\n SysTwoFactor,\n SysUserPreference,\n SysOauthApplication,\n SysOauthAccessToken,\n SysOauthRefreshToken,\n SysOauthConsent,\n SysJwks,\n SysDeviceCode,\n SysSsoProvider,\n SysScimProvider,\n];\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const authPluginManifestHeader = {\n id: AUTH_PLUGIN_ID,\n namespace: 'sys',\n version: AUTH_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Authentication & Identity Plugin',\n description: 'Core authentication objects for ObjectStack (User, Session, Account, Verification)',\n};\n"],"mappings":"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","names":["import_system","import_system","organization","dataEngine"]}