Better Auth
@veritio/better-auth maps Better Auth lifecycle hooks into portable Veritio event inputs or records. The host owns Better Auth configuration, tenant resolution, storage, and the decision about which authentication context is necessary to retain.
Install and create the adapter
Section titled “Install and create the adapter”npm install @veritio/better-auth@0.0.4 @veritio/core@0.4.7import { createAuditRecorder, MemoryAuditStore } from '@veritio/core'import { createBetterAuthVeritioAdapter } from '@veritio/better-auth'
const recorder = createAuditRecorder({ store: new MemoryAuditStore() })export const veritioAuth = createBetterAuthVeritioAdapter({ recorder, environment: 'production',})Replace MemoryAuditStore with an authoritative durable store before production.
Record user creation from a server hook
Section titled “Record user creation from a server hook”databaseHooks: { user: { create: { after: async (user) => { await veritioAuth.recordUserCreated({ user: { id: user.id }, tenantId: resolveTenantId(user), }) }, }, },}The adapter also records session creation/revocation, organization creation, invitations, membership changes, and role changes through explicit methods. Every call requires stable IDs needed for tenant scope, target identity, and deterministic idempotency material.
Authentication security context
Section titled “Authentication security context”When the application intentionally retains sign-in security context, pass only allowlisted derived values:
await veritioAuth.recordSessionCreated({ user: { id: session.userId }, session: { id: session.id }, tenantId: await resolveTenantId(session.userId), requestId, securityContext: { ipAddressHash: await hashIpAddress(session.ipAddress), userAgentHash: await hashUserAgent(session.userAgent), location: { country: 'DE', region: 'BE' }, },})Do not pass passwords, bearer or reset tokens, authorization headers, cookies, raw email addresses, IP addresses, precise locations, or full user-agent strings.
Deterministic defaults
Section titled “Deterministic defaults”The verified adapter records authentication lifecycle events with purpose access_management, lawful basis contract, and retention label security_1y. Those are product defaults in this adapter, not universal legal conclusions. A host that cannot support them should choose an explicit mapper or policy boundary instead of silently accepting them.
Idempotency keys are derived from the event type, tenant, and stable resource ID. Replaying the same hook returns the original record; a conflicting payload with the same key fails closed in a conforming store.
Pure mappers for queues and outboxes
Section titled “Pure mappers for queues and outboxes”When delivery is owned by a queue, outbox, or hosted client, use a pure builder such as buildBetterAuthSessionCreatedAuditEventInput. It returns the same portable AuditEventInput without performing storage I/O. The delivery boundary then owns idempotency, retries, and persistence.
Run an end-to-end application
Section titled “Run an end-to-end application”The repository includes runnable Next.js, React, Vue, SvelteKit, and TanStack Start examples. The Next.js reference exercises Better Auth hooks, governed CRUD, a local evidence feed, and optional server-to-server dispatch:
git clone https://github.com/getveritio/veritio.gitgit -C veritio checkout c4100ee7b678d0c6b227c67ae6ea1d8a1f373967cd veritiobun install --frozen-lockfilebun run verify:examplesInspect the produced record’s tenant scope, actor, target, action, governance labels, and idempotency replay. Then change a payload while reusing its idempotency key and confirm the store rejects the conflict.
Use the Next.js integration for the full server boundary and Deterministic redaction for the metadata rules.