@veritio/better-auth
v0.0.2
Published
Better Auth adapter for recording Veritio audit trail evidence from server-side auth lifecycle events.
Maintainers
Readme
@veritio/better-auth
Better Auth adapter for emitting Veritio events for auth lifecycle activity.
The adapter is a thin mapper. Host applications own Better Auth configuration, tenant resolution, and Veritio storage setup.
Initial event targets:
- user creation
- session creation and revocation
- organization creation
- organization invitation creation and acceptance
- organization/member changes when used by the host app
This adapter must receive a configured Veritio recorder from the host application. It must not read secrets or storage credentials directly.
Install
npm install @veritio/better-auth @veritio/coreUsage
import { createAuditRecorder, MemoryAuditStore } from "@veritio/core";
import { createBetterAuthVeritioAdapter } from "@veritio/better-auth";
const store = new MemoryAuditStore();
const recorder = createAuditRecorder({ store });
export const veritioAuth = createBetterAuthVeritioAdapter({
recorder,
environment: "production",
});Wire the returned methods from server-side Better Auth hooks. For example,
Better Auth databaseHooks.user.create.after can call recordUserCreated:
databaseHooks: {
user: {
create: {
after: async (user) => {
await veritioAuth.recordUserCreated({
user: { id: user.id },
tenantId: resolveTenantId(user),
});
},
},
},
}Session hooks can pass an allowlisted auth-security context object when the host intentionally records sign-in/logout context:
databaseHooks: {
session: {
create: {
after: async (session, context) => {
await veritioAuth.recordSessionCreated({
user: { id: session.userId },
session: { id: session.id },
tenantId: await resolveTenantId(session.userId),
requestId: context?.headers.get("x-request-id") ?? undefined,
securityContext: {
ipAddressHash: await hashIpAddress(session.ipAddress),
userAgentHash: await hashUserAgent(session.userAgent),
location: await resolveCountryRegion(context),
},
});
},
},
delete: {
after: async (session, context) => {
await veritioAuth.recordSessionRevoked({
user: { id: session.userId },
session: { id: session.id },
tenantId: await resolveTenantId(session.userId),
requestId: context?.headers.get("x-request-id") ?? undefined,
});
},
},
},
}For Better Auth organization plugin hooks, map only stable IDs and allowlisted fields:
organizationHooks: {
afterCreateOrganization: async ({ organization, user }) => {
await veritioAuth.recordOrganizationCreated({
actor: { id: user.id },
organization: { id: organization.id },
});
},
afterCreateInvitation: async ({ invitation, inviter, organization }) => {
await veritioAuth.recordInvitationCreated({
invitation: { id: invitation.id, role: invitation.role },
inviter: { id: inviter.id },
organization: { id: organization.id },
});
},
}Do not pass raw emails, passwords, bearer tokens, reset tokens, authorization
headers, cookies, raw IP addresses, precise locations, or raw user agents into
adapter metadata. Prefer securityContext.ipAddressHash, networkHash,
userAgentHash, and country/region location when the host has intentionally
chosen to capture authentication security context.
Defaults and determinism
Every recorded event carries purpose: "access_management",
lawfulBasis: "contract", and retention: "security_1y", and derives a
deterministic idempotency key (for example
better-auth:session-created:<tenantId>:<sessionId>) so a replayed hook cannot
duplicate evidence. Stable IDs are required and validated before they become
tenant scope, actor, target, or idempotency-key material.
Pure event mappers
When the host records through something other than an AuditStore (an outbox,
a queue, a hosted ingest call), use the pure mappers instead of the recorder
methods. They return a portable AuditEventInput with the same
metadata-minimization rules and no side effects:
import { buildBetterAuthSessionCreatedAuditEventInput } from "@veritio/better-auth";
const eventInput = buildBetterAuthSessionCreatedAuditEventInput(
{ user: { id: "usr_123" }, session: { id: "sess_123" }, tenantId: "org_123" },
"production",
);buildBetterAuthUserCreatedAuditEventInput,
buildBetterAuthOrganizationCreatedAuditEventInput, and
buildBetterAuthSessionRevokedAuditEventInput follow the same shape.
Full examples
Five runnable reference apps wire this adapter end to end (Better Auth
lifecycle hooks + governed CRUD + evidence graph): examples/nextjs-better-auth,
examples/react-better-auth, examples/vue-better-auth,
examples/sveltekit-better-auth, and examples/tanstack-start-better-auth.
Veritio supports audit trail evidence workflows; it is not legal advice and does not guarantee compliance with any regulation or framework.
