Better Auth Sync Plugin for Webhooks
better-auth-sync is a plugin for the Better Auth system, designed to synchronize authentication data to external databases via webhooks. It offers first-class integration and helper functions specifically for Convex, enabling real-time mirroring of user and session data. The current stable version is 0.2.0, indicating it is an early-stage but actively developed package. While a specific release cadence isn't defined, its low version number suggests ongoing evolution and potential for rapid iteration. Key differentiators include its tight integration with Better Auth's plugin architecture and specialized utilities for Convex, such as schema definitions, JWT handling, and webhook verification, streamlining complex authentication setups involving external data stores.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'APP_ORIGIN')
cause Environment variable `APP_ORIGIN` is not defined or accessible where `convexJwt` is configured.fixEnsure `APP_ORIGIN` is correctly set in your environment variables for the Better Auth server. For Node.js, this typically means `process.env.APP_ORIGIN`. -
Response with status 401 from webhook endpoint
cause The `verifyWebhook` function in your Convex HTTP route failed, likely due to a mismatch in `WEBHOOK_SECRET` or an invalid signature/timestamp in the incoming webhook request.fixVerify that `process.env.WEBHOOK_SECRET` in your `convex/http.ts` matches the `secret` configured in the `syncPlugin` on your Better Auth server. Also, check for clock skew between the Better Auth server and Convex. -
Convex `ConvexProviderWithAuth` or `useAuth` hook not working as expected, authentication state is missing.
cause The `createConvexBetterAuth` setup in your `src/providers/convex-provider.tsx` is incorrect, or `authClient` is not properly initialized, preventing the auth context from being provided.fixDouble-check that `authClient` from `@/lib/auth-client` is correctly configured and that `ConvexClientProvider` is wrapping your application's components at a high level in the React tree.
Warnings
- gotcha Mismatch between `applicationID` in `convex/auth.config.ts` and `issuer`/`audience` in `convexJwt` will cause JWT validation failures, preventing users from being authenticated correctly via Convex. This is a common misconfiguration point.
- gotcha The `WEBHOOK_SECRET` environment variable must be a long, random, and securely stored string. If exposed or easily guessable, it could allow unauthorized parties to send forged authentication events to your Convex backend, leading to data corruption or security vulnerabilities.
- gotcha Incorrect configuration of HTTP routes in Convex for `/auth-webhook` or `/.well-known/jwks.json` will prevent the sync plugin from functioning or JWTs from being validated. Missing routes or incorrect methods/handlers will lead to 404/405 errors.
- gotcha The package is in version `0.2.0`, indicating it's still under active development and not yet at a stable 1.0.0 release. Minor versions might introduce breaking changes without a major version bump, though semver would dictate otherwise. Expect API adjustments in future updates.
Install
-
npm install better-auth-sync -
yarn add better-auth-sync -
pnpm add better-auth-sync
Imports
- syncPlugin
const syncPlugin = require('better-auth-sync').syncPlugin;import { syncPlugin } from 'better-auth-sync'; - convexJwt
import { convexJwt } from 'better-auth-sync';import { convexJwt } from 'better-auth-sync/jwt'; - authTables
import { authTables } from 'better-auth-sync/schema';import { authTables } from 'better-auth-sync/convex'; - createConvexBetterAuth
import { createConvexBetterAuth } from 'better-auth-sync';import { createConvexBetterAuth } from 'better-auth-sync/react';
Quickstart
import { betterAuth } from "better-auth";
import { syncPlugin } from "better-auth-sync";
import { convexJwt } from "better-auth-sync/jwt";
// Ensure these environment variables are set in your Better Auth server environment
// BETTER_AUTH_URL=https://auth.your-app.com
// WEBHOOK_URL=https://your-project.convex.site/auth-webhook
// WEBHOOK_SECRET=replace-with-a-long-random-secret
// APP_ORIGIN=https://your-app.com
export const auth = betterAuth({
// Replace with your actual adapter, trusted origins, and providers
// adapter: ...,
// trustedOrigins: [process.env.APP_ORIGIN!],
// providers: { email: { ... } },
plugins: [
convexJwt({
issuer: process.env.APP_ORIGIN ?? 'https://your-app.com',
audience: process.env.APP_ORIGIN ?? 'https://your-app.com'
}),
syncPlugin({
secret: process.env.WEBHOOK_SECRET ?? 'your-webhook-secret-placeholder',
url: process.env.WEBHOOK_URL ?? 'https://your-project.convex.site/auth-webhook',
retryAttempts: 3
})
]
});
console.log('Better Auth instance configured with sync and Convex JWT plugins.');
console.log('Ensure environment variables are correctly loaded and JWT issuer/audience match APP_ORIGIN.');