# plugin/plugin _(no submodules)_ ## Purpose `createAuthPlugin(options)` is the factory that returns a typed `PluginDescriptor` ready to register with the host's `PluginRegistry`. It is the canonical exemplar of the 3-way plug-and-play contract every `@suaveplan/api-plugin-*` package follows: the plugin owns its `.surql` DDL, its Zod schemas, and its OpenAPI routes, and applies all three during `onInit`. ## Features - Zod-validated options via `AuthPluginOptionsSchema` — invalid input raises `AuthError(AUTH_INVALID_INPUT)` carrying the `ZodError`. - 6-step `onInit` lifecycle: resolve services → apply schema → construct Better-Auth → register `authService` → register routes → install session middleware. - `routePrefix`-aware route mounting — paths are rebuilt at the prefix the operator chose, both in the served Hono app and in the `OpenAPIRegistry` snapshot. - Graceful degradation — when `openAPIRegistry` is absent, routes mount on the Hono app directly and a warning is logged. When neither `migrationRunner` nor `db` is available with `applySchemaOnInit: true`, init fails fast with `AUTH_SERVICE_NOT_AVAILABLE`. - Collision detection — pre-existing routes under the configured prefix raise `AUTH_ROUTE_PREFIX_COLLISION`. ## Usage: Basic ```ts import { defineApp } from "@suaveplan/api"; import { createAuthPlugin } from "@suaveplan/api-plugin-auth"; const app = await defineApp() .name("my-api") .use( createAuthPlugin({ betterAuth: { secret: process.env.BETTER_AUTH_SECRET!, baseURL: "http://localhost:3000", emailAndPassword: { enabled: true }, database: surrealAdapter, }, }) ) .start(); ``` ## Usage: Advanced Mount the plugin under a versioned prefix, disable the in-plugin schema apply step (an external migration tool handles DDL), and inject a custom principal mapping that decorates the Better-Auth user with roles derived from the active organisation: ```ts import { defineApp } from "@suaveplan/api"; import { createAuthPlugin, mapBetterAuthUserToPrincipal, } from "@suaveplan/api-plugin-auth"; const app = await defineApp() .name("my-api") .use( createAuthPlugin({ betterAuth: { /* … */ }, routePrefix: "/api/v1/auth", applySchemaOnInit: false, principalMapping: (view) => { const base = mapBetterAuthUserToPrincipal(view); const session = view.session as { roles?: string[] }; return { ...base, roles: [...base.roles, ...(session.roles ?? [])], }; }, }) ) .start(); ``` ## API Reference ### `function createAuthPlugin(options: AuthPluginOptions): PluginDescriptor` - Validates `options` via `AuthPluginOptionsSchema`. - Returns a `PluginDescriptor` with `name: "auth"`, `version: "0.1.0"`, `tags: ["auth", "security"]`, and the 4 lifecycle hooks (only `onInit` + `onClose` are wired; `onRegister` and `onReady` are default no-ops). - `onInit` runs the canonical 6-step lifecycle described above. It raises `AUTH_*` codes on the explicit failure modes — see `../error-codes/error-codes.md`. - `onClose` releases the cached Better-Auth reference so the registry can re-init the plugin on subsequent boots. ## Implementation Notes - The descriptor's `configSchema` is the `AuthPluginOptionsSchema` so the host registry re-validates the merged config at registration time. The `defaults` field carries the resolved options so plugins registered without an explicit config still see the parsed shape. - The session middleware is installed AFTER routes are registered so the OpenAPI doc snapshot is taken before any per-request work begins. Order does not affect runtime behaviour but keeps the registry's events ordered consistently across reboots. - The runtime check against `openAPIRegistry === undefined` exists even though the registry's TS-level slot is non-optional — `ctx.getService` adds `| undefined` to every slot's return type, so the absent-runtime case still type-checks. - `applySchema` prefers the migration runner over the raw `db` adapter so operators get migration-record tracking when the runner is wired. Falling back to `applyBetterAuthSchema(db)` ensures the plugin still boots in lightweight deployments that ship without `@suaveplan/api-migrations`.