Authentication
Add user authentication with @flowlib/user-auth.
The auth plugin wraps Better Auth to provide user login, session management, and admin-only user creation. It's a backend + frontend package.
Installation
pnpm add @flowlib/user-authnpm install @flowlib/user-authyarn add @flowlib/user-authbun add @flowlib/user-authBackend setup
The simplest setup lets the plugin manage Better Auth internally — no separate auth.ts file needed:
import { defineConfig } from '@flowlib/core';
import { auth } from '@flowlib/user-auth';
export const flowlibConfig = defineConfig({
database: { type: 'sqlite', connectionString: 'file:./dev.db' },
plugins: [
auth({
betterAuthOptions: {
secret: process.env.BETTER_AUTH_SECRET,
},
globalAdmins: [{ email: 'admin@example.com', pw: 'changeme', name: 'Admin' }],
}),
],
});For full control, create your own Better Auth instance and pass it in:
import { betterAuth } from 'better-auth';
import { auth } from '@flowlib/user-auth';
const myAuth = betterAuth({
/* full Better Auth config */
});
export const flowlibConfig = defineConfig({
// ...
plugins: [auth({ auth: myAuth })],
});The globalAdmins array seeds admin users on startup. These are the only users that can log in initially — sign-up is disabled in the UI. Subsequent users are created by admins through the user management interface.
After adding the plugin, regenerate and apply the schema:
npx flowlib-cli generate
npx flowlib-cli migrate --pushOptions
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
auth | BetterAuthInstance | No | — | Your own Better Auth instance. If omitted, one is created internally. |
betterAuthOptions | BetterAuthPassthroughOptions | No | — | Options passed to the internal Better Auth instance (ignored when auth is provided). |
globalAdmins | BetterAuthGlobalAdmin[] | No | [] | Admin accounts to seed on startup. |
database | unknown | No | — | Database for the internal Better Auth instance. Uses Flowlib's DB when omitted. |
baseURL | string | No | auto-detected | Base URL for auth server (cookies, CSRF). Falls back to BETTER_AUTH_URL env var. |
trustedOrigins | string[] | (req) => string[] | No | localhost + baseURL | Origins trusted for CORS / cookie sharing. |
publicPaths | string[] | No | [] | Paths accessible without a valid session (auth proxy routes are always public). |
mapUser | (user, session) => FlowlibIdentity | No | default mapping | Custom function to map Better Auth users to Flowlib identities. |
mapRole | (role) => FlowlibRole | No | default mapping | Custom function to map Better Auth role strings to Flowlib roles. |
apiKey | boolean | ApiKeyPluginOptions | No | false | Enable the Better Auth API Key plugin for programmatic access. |
frontend | unknown | No | — | Deprecated. The auth plugin registers its UI automatically. |
What it does
Backend:
onRequesthook resolves the session from cookies on every API requestonAuthorizehook enforces that the user is logged in- Endpoints for sign-in, sign-out, session management, and user CRUD (proxied to Better Auth)
- Admin user seeding on startup from
globalAdmins
Frontend:
AuthProvider— React context providing session stateAuthGate— renders children only when authenticated, shows sign-in page otherwiseSignInPage— pre-built sign-in formUserButton— avatar with sign-out menuUserManagement— admin panel for creating/managing usersProfilePage— user profile pageApiKeysDialog— API key management (requiresapiKeyoption enabled)SidebarUserMenu— user menu component for the sidebarauthFrontend— deprecated. Auth UI is now registered automatically via the plugin'sappShell.
Frontend integration
No extra wrapper component is needed. Pass the auth() plugin in flowlib.config.ts — <Flowlib> reads the config and automatically wraps itself with an auth gate:
'use client';
import dynamic from 'next/dynamic';
import '@flowlib/ui/styles';
import config from '../../flowlib.config'; // contains plugins: [auth(...), rbac()]
const Flowlib = dynamic(() => import('@flowlib/ui').then((m) => ({ default: m.Flowlib })), {
ssr: false,
});
export default function FlowlibPage() {
return <Flowlib config={config} />;
}The OAuth callback route is handled internally at <frontendPath>/oauth/callback. You do not need a separate OAuth2CallbackHandler route.
For non-Next.js frontends (e.g. Vite + React), import the shared config and pass it the same way:
import { Flowlib } from '@flowlib/ui';
import '@flowlib/ui/styles';
import { flowlibConfig } from '../flowlib.config';
export default function App() {
return <Flowlib config={flowlibConfig} />;
}Environment variables
| Variable | Required | Description |
|---|---|---|
BETTER_AUTH_SECRET | Yes | Session encryption secret |
FLOWLIB_ADMIN_EMAIL | No | Alternative to globalAdmins config |
FLOWLIB_ADMIN_PASSWORD | No | Alternative to globalAdmins config |