BetterIAM

Installation

Install better-iam, set the secret and base URL, create the betterIam() instance, connect PostgreSQL, SQLite, or libSQL, migrate, mount the HTTP handler in your framework, and create the typed client.

This page adds Better IAM to an existing application: one package, two environment variables, a configuration module, a database, the HTTP handler mounted in your framework, and the typed browser client. When you finish, the API answers under /api/iam and your server code can call iam.api and iam.require. The Quickstart then signs in the first administrator and makes the first authorization check.

Requirements

RequirementNotes
Node.js 22.12 or newerThe engines field of every package. Web-standard Request/Response, Web Crypto, and AsyncLocalStorage are used throughout.
ESMPackages are ES modules with TypeScript declarations. Use "type": "module" or a bundler.
Native modulesargon2 (password hashing) and, for SQLite, better-sqlite3. Allow their install scripts (pnpm: allowBuilds / onlyBuiltDependencies).
A databasePostgreSQL, SQLite, or libSQL/Turso through the bundled adapters, or your own adapter.
TypeScript (optional)moduleResolution of bundler, node16, or nodenext so subpath exports resolve.

Setup

Install the package

npm i better-iam

better-iam depends on every @better-iam/* package except the Nuxt module and re-exports each one as a subpath (better-iam/adapter-postgres, better-iam/client, better-iam/next, and so on). Framework and protocol code loads only when you import its subpath, so unused integrations cost nothing at runtime. It also installs the better-iam command-line tool.

Framework peers (next, react, vue, svelte, @sveltejs/kit, react-router, @nestjs/*) are optional peer dependencies: install the ones your application already uses. Nuxt projects also install @better-iam/nuxt.

Set environment variables

Better IAM derives its signing and encryption keys from one deployment secret of at least 32 characters. Generate one with the CLI and append it to your .env file:

npx better-iam secret --env >> .env

Then add the URL your application is served from:

.env
BETTER_IAM_SECRET=generated-value
BETTER_IAM_BASE_URL=http://localhost:3000
VariableUsed for
BETTER_IAM_SECRETThe secret option. Keep it stable across restarts and processes, and store it like a database password. Rotate it later with previousSecrets; see Secrets.
BETTER_IAM_BASE_URLThe baseURL option: the public origin of your application. It becomes the trusted origin for cookie requests.

Better IAM does not load .env files itself. Make the variables available to every process that loads the configuration (your server, scripts, and the CLI), for example with your framework's .env support or node --env-file=.env.

Create the instance

Put the options in a configuration module whose default export is the options object. Your application and the better-iam CLI both load it, so migrations and scheduled jobs always see the same settings.

better-iam.config.mjs
export default {
  secret: process.env.BETTER_IAM_SECRET,
  baseURL: process.env.BETTER_IAM_BASE_URL,
  // database: added in the next step
};

Then create the one instance the rest of your server code imports:

iam.ts
import { betterIam } from 'better-iam';
import config from './better-iam.config.mjs';

export const iam = betterIam(config);

Frameworks expect this file in a server-only place: lib/iam.ts in Next.js, server/iam.ts in Nuxt, src/lib/server/iam.ts in SvelteKit, and app/iam.server.ts in React Router.

Generate the configuration instead

npx better-iam init --database postgres writes a starter better-iam.config.mjs that reads the secret and URLs from the environment (--typescript writes a typed better-iam.config.ts, which the CLI loads on Node.js 22.18 or later). Its default export is a defineConfig factory rather than a plain object, so create the instance with betterIam(await configOptions(config)), both imported from better-iam/server. Importing an .mjs module from TypeScript needs allowJs in tsconfig.json (the tsconfig.json that create-next-app generates sets it).

Configure the database

Better IAM keeps all of its records in your database through a storage adapter, passed as the database option. The three bundled adapters share one schema, so you can start on SQLite and move to PostgreSQL later.

better-iam.config.mjs
import { postgresAdapter } from 'better-iam/adapter-postgres';

export default {
  database: postgresAdapter({ connectionString: process.env.DATABASE_URL }),
  secret: process.env.BETTER_IAM_SECRET,
  baseURL: process.env.BETTER_IAM_BASE_URL,
};

The recommended production store, through pg and kysely (both installed with the adapter). Optional poolSize and lockTimeoutMs tune the connection pool and lock waits. See PostgreSQL.

See Storage operations for durability settings, snapshots, and moving between databases, and Adapters and plugins to write your own adapter.

Create the tables

Run the migrations once per deployment, before the application serves traffic:

npx better-iam migrate

The CLI finds the nearest better-iam.config.mjs (or .js, .ts); pass --config PATH or set BETTER_IAM_CONFIG to use another file. migrate creates or upgrades the schema and applies plugin migrations, and it is safe to run again.

To migrate from code instead, for example when a single-process app starts, call iam.initialize() before the first request:

server.ts
import { iam } from './iam';

await iam.initialize(); // creates or upgrades the schema, then runs plugin migrations

See Migrations and the migrate command.

Mount the handler

The instance exposes a Web-standard iam.handler (Request to Response) and a Node iam.nodeHandler. The browser client, passkey ceremonies, and the OAuth, SAML, and SCIM protocol mounts all reach the server through it, under the basePath option (default /api/iam). Every API method is a POST; GET serves the operational /health, /metrics (when a metrics token is configured), and /.well-known/jwks.json (when session JWTs are enabled) endpoints.

lib/iam-next.ts
import { createIamNext } from 'better-iam/next';
import { iam } from './iam';

export const iamNext = createIamNext(iam, { loginPath: '/login' });
app/api/iam/[...path]/route.ts
import { iamNext } from '@/lib/iam-next';

export const runtime = 'nodejs';
export const { GET, POST, OPTIONS } = iamNext.handlers();

List the server packages and native modules in serverExternalPackages in next.config.mjs so they load as plain Node modules. See Next.js.

Create the client

The typed client calls the mounted handler from the browser. Import the server instance with import type, so no server code reaches the bundle; every method's input and result is then inferred from your configuration.

lib/iam-client.ts
import { createIamClient } from 'better-iam/client';
import type { iam } from './iam';

export const client = createIamClient<typeof iam>();

In a browser the client defaults to the page's origin and the /api/iam base path. Pass baseURL when the API is on another origin or when the client runs outside a browser, and basePath when you changed the server's. React, Vue, and Svelte bindings build on the same client; see Typed client.

Done

The API is live. Check that the configuration loads and the database is ready:

npx better-iam doctor

doctor reports schema, bootstrap, secret strength, durability, email transport, and scheduled-job problems, and --strict exits non-zero on any warning so you can run it in CI. See the doctor command.

A fresh database has no tenants yet. Continue with the Quickstart to bootstrap the root administrator, create an organization, and make your first authorization check.

Subpath imports

Every subpath of the umbrella package maps to one workspace package, so importing from a subpath loads only that package:

ImportPackageWhat it provides
better-iam@better-iam/server and @better-iam/coreThe common entry points: betterIam(), IamError, definePolicy, evaluatePolicy, verifyAssertion, verifyWebhookSignature, and the main types
better-iam/server@better-iam/serverEverything the server package exports, including defineConfig, the route tables, and server types
better-iam/session-tokens@better-iam/server/session-tokensOffline verification of signed session JWTs in other services
better-iam/core@better-iam/coreModels, evaluatePolicy, definePolicy, the storage contract, IamError
better-iam/auth, better-iam/auth/templates@better-iam/authAuthentication service internals and renderDeliveryMessage
better-iam/client, /client/session, /client/passkeys, /client/device@better-iam/clientTyped browser client, session store, WebAuthn helpers, registered-device proofs
better-iam/adapter-postgres, /adapter-sqlite, /adapter-libsql@better-iam/adapter-*Storage adapters
better-iam/oauth, better-iam/saml, better-iam/scim@better-iam/oauth, saml, scimFederation protocols
better-iam/ldap@better-iam/ldapLDAP directory gateway
better-iam/mcp, better-iam/a2a@better-iam/mcp, a2aTool-level authorization for MCP servers; signed A2A agent cards and A2A server authorization
better-iam/react, better-iam/vue@better-iam/react, vueUI bindings
better-iam/next, /next/edge, /next/client@better-iam/nextNext.js App Router helpers
better-iam/svelte, /svelte/kit@better-iam/svelteSvelte stores and SvelteKit hooks
better-iam/react-router@better-iam/react-routerReact Router middleware and guards
better-iam/nestjs, /nestjs/testing@better-iam/nestjsNestJS module, guard, decorators
better-iam/middleware, /express, /hono, /fastify@better-iam/middlewareNode framework middleware
better-iam/cli, better-iam/projects@better-iam/cli, projectsCLI entry point, reference plugin

The Nuxt module is published separately as @better-iam/nuxt because it depends on @nuxt/kit.

All packages

better-iam0.3.1

Umbrella package: installs every package and exposes them as subpath imports.

/core/auth/auth/templates/server/session-tokens/oauth/saml/scim/adapter-postgres/adapter-sqlite/adapter-libsql/client/cli/client/passkeys/projects/react/next/next/edge/next/client/nestjs/nestjs/testing/vue/svelte/svelte/kit/middleware/express/hono/fastify/react-router/client/session/client/device/mcp/a2a/ldap

Peers: @nestjs/common, @nestjs/core, next, react, vue, svelte, @sveltejs/kit, react-router

@better-iam/a2a0.3.1

Agent2Agent (A2A) support: IAM-attested agent cards, card verification and discovery, and authorization for A2A servers.

@better-iam/adapter-libsql0.3.1

libSQL / Turso storage adapter.

@better-iam/adapter-postgres0.3.1

PostgreSQL storage adapter.

@better-iam/adapter-sqlite0.3.1

SQLite storage adapter (better-sqlite3).

@better-iam/auth0.3.1

Passwords, sessions, MFA, passkeys, magic links, recovery, and delivery templates.

/templates
@better-iam/cli0.3.1

The `better-iam` command line: migrations, bootstrap, audits, config as code, jobs.

@better-iam/client0.3.1

Typed browser client with session store and passkey helpers.

/passkeys/session/device
@better-iam/core0.3.1

Models, the policy engine, the storage contract, the audit chain, and shared errors.

/conformance
@better-iam/ldap0.3.1

A read-only LDAPv3 directory gateway over Better IAM: people, groups and password binds for applications that only speak LDAP.

@better-iam/mcp0.3.1

Tool-level authorization for Model Context Protocol servers: Better IAM credentials, agents and OAuth tokens.

@better-iam/middleware0.3.1

Framework-neutral middleware core with Express, Hono, and Fastify adapters.

/express/hono/fastify
@better-iam/nestjs0.3.1

NestJS module, guard, decorators, and testing utilities.

/testing

Peers: @nestjs/common, @nestjs/core, reflect-metadata, rxjs

@better-iam/next0.3.1

Next.js App Router helpers: guarded pages, routes, actions, and edge checks.

/edge/client

Peers: next, react

@better-iam/nuxt0.3.1

Nuxt module and h3 helpers.

/h3

Peers: nuxt, vue

@better-iam/oauth0.3.1

OAuth/OIDC authorization server, resource-server helpers, and Shared Signals.

@better-iam/projects0.3.1

Reference tenant-scoped Projects plugin.

@better-iam/react0.3.1

React provider, hooks, and permission-gated components.

Peers: react

@better-iam/react-router0.3.1

React Router (framework mode) middleware, guarded loaders, and actions.

Peers: react-router

@better-iam/saml0.3.1

SAML 2.0 service provider with tenant-managed connections.

@better-iam/scim0.3.1

SCIM 2.0 inbound provisioning and outbound provisioning to applications.

@better-iam/server0.3.1

The `betterIam()` factory: tenants, identities, authorization, governance, HTTP API.

/assertions/session-tokens
@better-iam/svelte0.3.1

Svelte stores and SvelteKit hooks, guards, and actions.

/kit

Peers: @sveltejs/kit, svelte

@better-iam/vue0.3.1

Vue plugin, composables, and the `IamCan` component.

Peers: vue

Next steps

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page