4.4 KiB
options
(no submodules)
Purpose
Zod-validated boundary for the Better-Auth adapter's configuration bag. Both
createBetterAuthSession and createBetterAuthProvider accept the same
{@link BetterAuthOptions} shape so a single options object can be threaded
through host configuration even when the operator wires only one side of the
auth split. Validation fires at factory-construction time so misconfiguration
fails fast at boot instead of surfacing as a confusing runtime error on the
first inbound request.
Features
- Required fields —
database(opaque pass-through to Better-Auth),baseUrl(URL-validated),secret(>= 16 chars). - Optional fields —
routePrefix(/authby default, must start with/),oauth.providers(per-provider URL-validated client config),session(cookie tuning),principalMapping(pluggable user → Principal). - Test hook —
betterAuthFactorylets unit tests stub the underlyingbetterAuth(...)call without standing up a real database.
Usage: Basic
import { parseBetterAuthOptions } from "@suaveplan/api-auth-better-auth/options";
const opts = parseBetterAuthOptions({
database: pool, // Better-Auth-shaped value (pg.Pool, Kysely dialect, …)
baseUrl: "https://api.example.com",
secret: process.env.BETTER_AUTH_SECRET!,
});
Usage: Advanced
import { parseBetterAuthOptions } from "@suaveplan/api-auth-better-auth/options";
import type { Principal } from "@suaveplan/api-types/principal";
const opts = parseBetterAuthOptions({
database: pool,
baseUrl: "https://api.example.com",
routePrefix: "/api/auth",
secret: process.env.BETTER_AUTH_SECRET!,
oauth: {
providers: {
github: {
providerId: "github",
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scopes: ["read:user", "user:email"],
authorizationUrl: "https://github.com/login/oauth/authorize",
tokenUrl: "https://github.com/login/oauth/access_token",
userInfoUrl: "https://api.github.com/user",
},
},
},
session: { cookieName: "ba.session", cookieSameSite: "lax", cookieSecure: true },
principalMapping: ({ user }): Principal => ({
id: user.id as never,
scopes: (user.roles ?? []) as never[],
claims: { email: user.email },
}),
});
API Reference
| Export | Shape | Purpose |
|---|---|---|
BetterAuthOptionsSchema |
Zod schema | Validates the full options object |
parseBetterAuthOptions(raw) |
(unknown) => BetterAuthOptions |
Parse + throw on failure |
DEFAULT_ROUTE_PREFIX |
"/auth" |
Default mount prefix |
DEFAULT_COOKIE_NAME |
"better-auth.session" |
Default cookie name |
BetterAuthOptions |
interface | Outer options shape |
BetterAuthOauthProvider |
interface | Per-provider OAuth config |
BetterAuthSessionOptions |
interface | Cookie tuning bag |
BetterAuthUserView / BetterAuthSessionRecord / BetterAuthSessionView |
interfaces | Mapper input shape |
BetterAuthLike |
interface | Structural slice of better-auth's Auth |
PrincipalMapping |
function type | (view) => Principal |
Implementation Notes
database is intentionally typed as unknown and the schema only checks
non-null/non-undefined — Better-Auth itself supports pg.Pool, Kysely
Dialect, D1Database, DBAdapterInstance and several other shapes; the
wrapper never inspects this value beyond passing it through to
betterAuth({ database }). This keeps the wrapper one-driver-agnostic and
lets operators bring their own connection topology (transaction-pool, single
client, serverless adapter, …) without the wrapper opinionating on it.
secret enforces a 16-character floor because Better-Auth uses it for cookie
signing and shorter secrets are trivially brute-forced. The exact constant is
informational — Better-Auth itself would still accept shorter strings — so
the strict check here is a defence-in-depth guard at the wrapper boundary.
betterAuthFactory is the only sanctioned way to inject a stubbed
Better-Auth instance into the adapter. Production callers MUST omit it so the
real betterAuth(...) import drives the runtime; tests use it to swap in a
synchronous in-memory stub conforming to {@link BetterAuthLike}.