Is there a better way to handle the situation below? For example: - Can we access Neon Auth's BETTER_AUTH_SECRET so our app uses the same one? - Is there a recommended pattern for running a custom Better Auth server alongside Neon Auth that avoids this table collision? - Any concerns with our approach of using a separate JWKS table?
How we extend Neon Auth
We use Neon Auth for user management (signup, login, sessions). But our app also needs:
1. JWT signing for PowerSync — Our sync engine (PowerSync) requires signed JWTs with a custom payload (id, email, audience: 'authenticated'). We use Better Auth's JWT plugin with EdDSA/Ed25519 keys. Neon Auth doesn't expose a token endpoint we can use for this. 2. Custom branded emails — Verification, password reset, and invite emails with Yeti Guides branding, sent via Resend in 3 languages (en/de/af). Neon Auth sends generic Neon-branded emails. 3. Offline tokens — We generate 30-day JWTs for client-side offline verification.
So we run our own Better Auth server on Vercel with search_path=public,neon_auth to share the user/session/account tables that Neon Auth manages.
The problem
Neon Auth's managed Better Auth writes JWKS key pairs to neon_auth.jwks, encrypted with Neon's internal BETTER_AUTH_SECRET.
Our app's Better Auth also needs a jwks table for its JWT plugin. But it seems we forgot/changed the Better Auth Secret our Neon production DB uses, because we get this
BetterAuthError: Failed to decrypt private key
BetterAuthError: Failed to decrypt private key
error, but only on our production branch, our dev branch works fine. (We migrated users from Supabase to Neon Auth, which might've caused this problem.)
Our fix
Our app now has its own JWKS keys encrypted with its own secret. We configured Better Auth's JWT plugin to use a custom table name so it creates public.app_jwks instead of colliding with neon_auth.jwks. PowerSync points at our app's jwks endpoint.