Flowlib
Plugins

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-auth
npm install @flowlib/user-auth
yarn add @flowlib/user-auth
bun add @flowlib/user-auth

Backend setup

The simplest setup lets the plugin manage Better Auth internally — no separate auth.ts file needed:

flowlib.config.ts
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:

flowlib.config.ts
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 --push

Options

OptionTypeRequiredDefaultDescription
authBetterAuthInstanceNoYour own Better Auth instance. If omitted, one is created internally.
betterAuthOptionsBetterAuthPassthroughOptionsNoOptions passed to the internal Better Auth instance (ignored when auth is provided).
globalAdminsBetterAuthGlobalAdmin[]No[]Admin accounts to seed on startup.
databaseunknownNoDatabase for the internal Better Auth instance. Uses Flowlib's DB when omitted.
baseURLstringNoauto-detectedBase URL for auth server (cookies, CSRF). Falls back to BETTER_AUTH_URL env var.
trustedOriginsstring[] | (req) => string[]Nolocalhost + baseURLOrigins trusted for CORS / cookie sharing.
publicPathsstring[]No[]Paths accessible without a valid session (auth proxy routes are always public).
mapUser(user, session) => FlowlibIdentityNodefault mappingCustom function to map Better Auth users to Flowlib identities.
mapRole(role) => FlowlibRoleNodefault mappingCustom function to map Better Auth role strings to Flowlib roles.
apiKeyboolean | ApiKeyPluginOptionsNofalseEnable the Better Auth API Key plugin for programmatic access.
frontendunknownNoDeprecated. The auth plugin registers its UI automatically.

What it does

Backend:

  • onRequest hook resolves the session from cookies on every API request
  • onAuthorize hook 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 state
  • AuthGate — renders children only when authenticated, shows sign-in page otherwise
  • SignInPage — pre-built sign-in form
  • UserButton — avatar with sign-out menu
  • UserManagement — admin panel for creating/managing users
  • ProfilePage — user profile page
  • ApiKeysDialog — API key management (requires apiKey option enabled)
  • SidebarUserMenu — user menu component for the sidebar
  • authFrontend — deprecated. Auth UI is now registered automatically via the plugin's appShell.

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:

app/flowlib/[[...slug]]/page.tsx
'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:

src/App.tsx
import { Flowlib } from '@flowlib/ui';
import '@flowlib/ui/styles';
import { flowlibConfig } from '../flowlib.config';

export default function App() {
  return <Flowlib config={flowlibConfig} />;
}

Environment variables

VariableRequiredDescription
BETTER_AUTH_SECRETYesSession encryption secret
FLOWLIB_ADMIN_EMAILNoAlternative to globalAdmins config
FLOWLIB_ADMIN_PASSWORDNoAlternative to globalAdmins config

On this page