Jazz database adapter for Better Auth

The package jazz-tools/better-auth/database-adapter is a database adapter for Better Auth based on Jazz. Better Auth's data will be stored in CoValues encrypted by Server Worker, synced on our distributed cloud infrastructure.

Getting started

  1. Install and configure Better Auth
  2. Install Jazz package pnpm jazz-tools
  3. Generate a worker's credentials
npx jazz-run account create --name "Better Auth Server Worker"
Security

Although all workers have the same capabilities, we recommend to use different workers for different purposes. As it will store user's credentials, the best practice is to keep it isolated from other workers.

  1. Setup the database adapter on Better Auth server instance.
import { betterAuth } from "better-auth";
import { JazzBetterAuthDatabaseAdapter } from "jazz-tools/better-auth/database-adapter";
const apiKey = process.env.JAZZ_API_KEY;

const auth = betterAuth({
  database: JazzBetterAuthDatabaseAdapter({
    syncServer: `wss://cloud.jazz.tools/?key=${apiKey}`,
    accountID: "auth-worker-account-id",
    accountSecret: "your-worker-account-secret",
  }),

  // other Better Auth settings
});
  1. You're ready to use Better Auth features without managing any database by yourself!

How it works

The adapter automatically creates Jazz schemas from Better Auth's database schema, even if not all the SQL-like features are supported yet. The database is defined as a CoMap with two properties: group and tables. The first one contains the master Group that will own all the tables; the second one is a CoMap with table names as keys and data as values.

Internally it uses specialized repository for known models like User, Session and Verification, to add indexes and boost performances on common operations.

How to access the database

The easiest way to access the database is using the same Server Worker's credentials and access the table we're looking for.

import { startWorker } from 'jazz-tools/worker';
import { co, z } from 'jazz-tools';
const apiKey = process.env.JAZZ_API_KEY;


const worker1 = await startWorker({
  syncServer: `wss://cloud.jazz.tools/?key=${apiKey}`,
  accountID: process.env.WORKER_ACCOUNT_ID,
  accountSecret: process.env.WORKER_ACCOUNT_SECRET,
});

const DatabaseRoot = co.map({
  tables: co.map({
    user: co.list(co.map({
      name: z.string(),
      email: z.string(),
    }))
  })
});

const db = await DatabaseRoot.loadUnique("better-auth-root", process.env.WORKER_ACCOUNT_ID, {
  resolve: {
    tables: {
      user: {
        $each: true,
      }
    }
  }
});

console.log(db.tables.user);

Rotating the worker's credentials

If you need to change the worker, you can create a new one and add it to the master Group.

import { Account } from 'jazz-tools';
import { startWorker } from 'jazz-tools/worker';
const apiKey = process.env.JAZZ_API_KEY;

// Start the main worker and fetch database reference
const { worker } = await startWorker({
  syncServer: `wss://cloud.jazz.tools/?key=${apiKey}`,
  accountID: process.env.WORKER_ACCOUNT_ID,
  accountSecret: process.env.WORKER_ACCOUNT_SECRET,
});

const DatabaseRoot = co.map({
  group: Group,
  tables: co.map({}),
});

const db = await DatabaseRoot.loadUnique("better-auth-root", process.env.WORKER_ACCOUNT_ID, {
  loadAs: worker,
  resolve: {
    group: true,
    tables: true,
  },
});

// Load the new worker account
const newWorkerRef = await Account.load(process.env.NEW_WORKER_ACCOUNT_ID);

// Add the new worker to the group as admin
db.group.$jazz.addMember(newWorkerRef, "admin");

await db.group.$jazz.waitForSync();

// Now the new worker can access the tables
const { worker: newWorker } = await startWorker({
  syncServer: `wss://cloud.jazz.tools/?key=${apiKey}`,
  accountID: process.env.NEW_WORKER_ACCOUNT_ID,
  accountSecret: process.env.NEW_WORKER_ACCOUNT_SECRET,
});

// Create the database root on the new worker with the same group's and tables' references
await DatabaseRoot.upsertUnique({
  unique: "better-auth-root",
  value: {
    group: db.group,
    tables: db.tables,
  },
  owner: newWorker,
});

// Now the new worker can be used for the Database Adapter.

// Don't forget to remove the old worker from the group
db.group.$jazz.removeMember(worker);
Security

Rotating keys means that data stored from that point forward will be encrypted with the new key, but the old worker's secret can still read data written up until the rotation. Read more about encryption in Server Worker.

Compatibility

The adapter generates Jazz schemas reading from Better Auth's database schema, so it should be compatible with any plugin / user's code that introduces new tables or extends the existing ones.

So far, the adapter has been tested with Better Auth v1.3.7 with the following plugins:

Plugin/FeatureCompatibility
Email & Password auth
Social Provider auth
Email OTP

More features and plugins will be tested in the future.